= 1:
return
try:
- import BmpImagePlugin
+ from PIL import BmpImagePlugin
except ImportError:
pass
try:
- import GifImagePlugin
+ from PIL import GifImagePlugin
except ImportError:
pass
try:
- import JpegImagePlugin
+ from PIL import JpegImagePlugin
except ImportError:
pass
try:
- import PpmImagePlugin
+ from PIL import PpmImagePlugin
except ImportError:
pass
try:
- import PngImagePlugin
+ from PIL import PngImagePlugin
except ImportError:
pass
# try:
@@ -321,66 +347,47 @@ def preinit():
_initialized = 1
-##
-# Explicitly initializes the Python Imaging Library. This function
-# loads all available file format drivers.
def init():
- "Load all file format drivers."
+ """
+ Explicitly initializes the Python Imaging Library. This function
+ loads all available file format drivers.
+ """
global _initialized
if _initialized >= 2:
return 0
- visited = {}
-
- directories = sys.path
-
- try:
- directories = directories + [os.path.dirname(__file__)]
- except NameError:
- pass
-
- # only check directories (including current, if present in the path)
- for directory in filter(isDirectory, directories):
- fullpath = os.path.abspath(directory)
- if visited.has_key(fullpath):
- continue
- for file in os.listdir(directory):
- if file[-14:] == "ImagePlugin.py":
- f, e = os.path.splitext(file)
- try:
- sys.path.insert(0, directory)
- try:
- __import__(f, globals(), locals(), [])
- finally:
- del sys.path[0]
- except ImportError:
- if DEBUG:
- print "Image: failed to import",
- print f, ":", sys.exc_value
- visited[fullpath] = None
+ for plugin in _plugins:
+ try:
+ if DEBUG:
+ print ("Importing %s"%plugin)
+ __import__("PIL.%s"%plugin, globals(), locals(), [])
+ except ImportError:
+ if DEBUG:
+ print("Image: failed to import", end=' ')
+ print(plugin, ":", sys.exc_info()[1])
if OPEN or SAVE:
_initialized = 2
return 1
# --------------------------------------------------------------------
-# Codec factories (used by tostring/fromstring and ImageFile.load)
+# Codec factories (used by tobytes/frombytes and ImageFile.load)
def _getdecoder(mode, decoder_name, args, extra=()):
# tweak arguments
if args is None:
args = ()
- elif not isTupleType(args):
+ elif not isinstance(args, tuple):
args = (args,)
try:
# get decoder
decoder = getattr(core, decoder_name + "_decoder")
- # print decoder, (mode,) + args + extra
- return apply(decoder, (mode,) + args + extra)
+ # print(decoder, mode, args + extra)
+ return decoder(mode, *args + extra)
except AttributeError:
raise IOError("decoder %s not available" % decoder_name)
@@ -389,14 +396,14 @@ def _getencoder(mode, encoder_name, args, extra=()):
# tweak arguments
if args is None:
args = ()
- elif not isTupleType(args):
+ elif not isinstance(args, tuple):
args = (args,)
try:
# get encoder
encoder = getattr(core, encoder_name + "_encoder")
- # print encoder, (mode,) + args + extra
- return apply(encoder, (mode,) + args + extra)
+ # print(encoder, mode, args + extra)
+ return encoder(mode, *args + extra)
except AttributeError:
raise IOError("encoder %s not available" % encoder_name)
@@ -404,26 +411,31 @@ def _getencoder(mode, encoder_name, args, extra=()):
# --------------------------------------------------------------------
# Simple expression analyzer
+def coerce_e(value):
+ return value if isinstance(value, _E) else _E(value)
+
class _E:
- def __init__(self, data): self.data = data
- def __coerce__(self, other): return self, _E(other)
- def __add__(self, other): return _E((self.data, "__add__", other.data))
- def __mul__(self, other): return _E((self.data, "__mul__", other.data))
+ def __init__(self, data):
+ self.data = data
+ def __add__(self, other):
+ return _E((self.data, "__add__", coerce_e(other).data))
+ def __mul__(self, other):
+ return _E((self.data, "__mul__", coerce_e(other).data))
def _getscaleoffset(expr):
stub = ["stub"]
data = expr(_E(stub)).data
try:
(a, b, c) = data # simplified syntax
- if (a is stub and b == "__mul__" and isNumberType(c)):
+ if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)):
return c, 0.0
- if (a is stub and b == "__add__" and isNumberType(c)):
+ if (a is stub and b == "__add__" and isinstance(c, numbers.Number)):
return 1.0, c
except TypeError: pass
try:
((a, b, c), d, e) = data # full syntax
- if (a is stub and b == "__mul__" and isNumberType(c) and
- d == "__add__" and isNumberType(e)):
+ if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and
+ d == "__add__" and isinstance(e, numbers.Number)):
return c, e
except TypeError: pass
raise ValueError("illegal expression")
@@ -432,17 +444,17 @@ def _getscaleoffset(expr):
# --------------------------------------------------------------------
# Implementation wrapper
-##
-# This class represents an image object. To create Image objects, use
-# the appropriate factory functions. There's hardly ever any reason
-# to call the Image constructor directly.
-#
-# @see #open
-# @see #new
-# @see #fromstring
-
class Image:
+ """
+ This class represents an image object. To create
+ :py:class:`~PIL.Image.Image` objects, use the appropriate factory
+ functions. There's hardly ever any reason to call the Image constructor
+ directly.
+ * :py:func:`~PIL.Image.open`
+ * :py:func:`~PIL.Image.new`
+ * :py:func:`~PIL.Image.frombytes`
+ """
format = None
format_description = None
@@ -463,7 +475,8 @@ class Image:
new.mode = im.mode
new.size = im.size
new.palette = self.palette
- if im.mode == "P":
+ if im.mode == "P" and not new.palette:
+ from PIL import ImagePalette
new.palette = ImagePalette.ImagePalette()
try:
new.info = self.info.copy()
@@ -507,23 +520,22 @@ class Image:
shape, typestr = _conv_type_shape(self)
new['shape'] = shape
new['typestr'] = typestr
- new['data'] = self.tostring()
+ new['data'] = self.tobytes()
return new
raise AttributeError(name)
- ##
- # Returns a string containing pixel data.
- #
- # @param encoder_name What encoder to use. The default is to
- # use the standard "raw" encoder.
- # @param *args Extra arguments to the encoder.
- # @return An 8-bit string.
+ def tobytes(self, encoder_name="raw", *args):
+ """
+ Return image as a bytes object
- def tostring(self, encoder_name="raw", *args):
- "Return image as a binary string"
+ :param encoder_name: What encoder to use. The default is to
+ use the standard "raw" encoder.
+ :param args: Extra arguments to the encoder.
+ :rtype: A bytes object.
+ """
# may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
+ if len(args) == 1 and isinstance(args[0], tuple):
args = args[0]
if encoder_name == "raw" and args == ():
@@ -538,47 +550,54 @@ class Image:
bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
data = []
- while 1:
+ while True:
l, s, d = e.encode(bufsize)
data.append(d)
if s:
break
if s < 0:
- raise RuntimeError("encoder error %d in tostring" % s)
+ raise RuntimeError("encoder error %d in tobytes" % s)
- return string.join(data, "")
+ return b"".join(data)
- ##
- # Returns the image converted to an X11 bitmap. This method
- # only works for mode "1" images.
- #
- # @param name The name prefix to use for the bitmap variables.
- # @return A string containing an X11 bitmap.
- # @exception ValueError If the mode is not "1"
+ # Declare tostring as alias to tobytes
+ def tostring(self, *args, **kw):
+ warnings.warn(
+ 'tostring() is deprecated. Please call tobytes() instead.',
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return self.tobytes(*args, **kw)
def tobitmap(self, name="image"):
- "Return image as an XBM bitmap"
+ """
+ Returns the image converted to an X11 bitmap.
+
+ .. note:: This method only works for mode "1" images.
+
+ :param name: The name prefix to use for the bitmap variables.
+ :returns: A string containing an X11 bitmap.
+ :raises ValueError: If the mode is not "1"
+ """
self.load()
if self.mode != "1":
raise ValueError("not a bitmap")
- data = self.tostring("xbm")
- return string.join(["#define %s_width %d\n" % (name, self.size[0]),
- "#define %s_height %d\n"% (name, self.size[1]),
- "static char %s_bits[] = {\n" % name, data, "};"], "")
+ data = self.tobytes("xbm")
+ return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'),
+ ("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'),
+ ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"])
- ##
- # Loads this image with pixel data from a string.
- #
- # This method is similar to the {@link #fromstring} function, but
- # loads data into this image instead of creating a new image
- # object.
+ def frombytes(self, data, decoder_name="raw", *args):
+ """
+ Loads this image with pixel data from a bytes object.
- def fromstring(self, data, decoder_name="raw", *args):
- "Load data to image from binary string"
+ This method is similar to the :py:func:`~PIL.Image.frombytes` function,
+ but loads data into this image instead of creating a new image object.
+ """
# may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
+ if len(args) == 1 and isinstance(args[0], tuple):
args = args[0]
# default format
@@ -595,75 +614,87 @@ class Image:
if s[1] != 0:
raise ValueError("cannot decode image data")
- ##
- # Allocates storage for the image and loads the pixel data. In
- # normal cases, you don't need to call this method, since the
- # Image class automatically loads an opened image when it is
- # accessed for the first time.
- #
- # @return An image access object.
+ def fromstring(self, *args, **kw):
+ """Deprecated alias to frombytes.
+
+ .. deprecated:: 2.0
+ """
+ warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning)
+ return self.frombytes(*args, **kw)
def load(self):
- "Explicitly load pixel data."
+ """
+ Allocates storage for the image and loads the pixel data. In
+ normal cases, you don't need to call this method, since the
+ Image class automatically loads an opened image when it is
+ accessed for the first time.
+
+ :returns: An image access object.
+ """
if self.im and self.palette and self.palette.dirty:
# realize palette
- apply(self.im.putpalette, self.palette.getdata())
+ self.im.putpalette(*self.palette.getdata())
self.palette.dirty = 0
self.palette.mode = "RGB"
self.palette.rawmode = None
- if self.info.has_key("transparency"):
- self.im.putpalettealpha(self.info["transparency"], 0)
+ if "transparency" in self.info:
+ if isinstance(self.info["transparency"], int):
+ self.im.putpalettealpha(self.info["transparency"], 0)
+ else:
+ self.im.putpalettealphas(self.info["transparency"])
self.palette.mode = "RGBA"
+
if self.im:
return self.im.pixel_access(self.readonly)
- ##
- # Verifies the contents of a file. For data read from a file, this
- # method attempts to determine if the file is broken, without
- # actually decoding the image data. If this method finds any
- # problems, it raises suitable exceptions. If you need to load
- # the image after using this method, you must reopen the image
- # file.
-
def verify(self):
- "Verify file contents."
+ """
+ Verifies the contents of a file. For data read from a file, this
+ method attempts to determine if the file is broken, without
+ actually decoding the image data. If this method finds any
+ problems, it raises suitable exceptions. If you need to load
+ the image after using this method, you must reopen the image
+ file.
+ """
pass
- ##
- # Returns a converted copy of this image. For the "P" mode, this
- # method translates pixels through the palette. If mode is
- # omitted, a mode is chosen so that all information in the image
- # and the palette can be represented without a palette.
- #
- # The current version supports all possible conversions between
- # "L", "RGB" and "CMYK."
- #
- # When translating a colour image to black and white (mode "L"),
- # the library uses the ITU-R 601-2 luma transform:
- #
- # L = R * 299/1000 + G * 587/1000 + B * 114/1000
- #
- # When translating a greyscale image into a bilevel image (mode
- # "1"), all non-zero values are set to 255 (white). To use other
- # thresholds, use the {@link #Image.point} method.
- #
- # @def convert(mode, matrix=None, **options)
- # @param mode The requested mode.
- # @param matrix An optional conversion matrix. If given, this
- # should be 4- or 16-tuple containing floating point values.
- # @param options Additional options, given as keyword arguments.
- # @keyparam dither Dithering method, used when converting from
- # mode "RGB" to "P".
- # Available methods are NONE or FLOYDSTEINBERG (default).
- # @keyparam palette Palette to use when converting from mode "RGB"
- # to "P". Available palettes are WEB or ADAPTIVE.
- # @keyparam colors Number of colors to use for the ADAPTIVE palette.
- # Defaults to 256.
- # @return An Image object.
-
- def convert(self, mode=None, data=None, dither=None,
+ def convert(self, mode=None, matrix=None, dither=None,
palette=WEB, colors=256):
- "Convert to other pixel format"
+ """
+ Returns a converted copy of this image. For the "P" mode, this
+ method translates pixels through the palette. If mode is
+ omitted, a mode is chosen so that all information in the image
+ and the palette can be represented without a palette.
+
+ The current version supports all possible conversions between
+ "L", "RGB" and "CMYK." The **matrix** argument only supports "L"
+ and "RGB".
+
+ When translating a color image to black and white (mode "L"),
+ the library uses the ITU-R 601-2 luma transform::
+
+ L = R * 299/1000 + G * 587/1000 + B * 114/1000
+
+ The default method of converting a greyscale ("L") or "RGB"
+ image into a bilevel (mode "1") image uses Floyd-Steinberg
+ dither to approximate the original image luminosity levels. If
+ dither is NONE, all non-zero values are set to 255 (white). To
+ use other thresholds, use the :py:meth:`~PIL.Image.Image.point`
+ method.
+
+ :param mode: The requested mode.
+ :param matrix: An optional conversion matrix. If given, this
+ should be 4- or 16-tuple containing floating point values.
+ :param dither: Dithering method, used when converting from
+ mode "RGB" to "P" or from "RGB" or "L" to "1".
+ Available methods are NONE or FLOYDSTEINBERG (default).
+ :param palette: Palette to use when converting from mode "RGB"
+ to "P". Available palettes are WEB or ADAPTIVE.
+ :param colors: Number of colors to use for the ADAPTIVE palette.
+ Defaults to 256.
+ :rtype: :py:class:`~PIL.Image.Image`
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if not mode:
# determine default mode
@@ -678,21 +709,25 @@ class Image:
self.load()
- if data:
+ if matrix:
# matrix conversion
if mode not in ("L", "RGB"):
raise ValueError("illegal conversion")
- im = self.im.convert_matrix(mode, data)
+ im = self.im.convert_matrix(mode, matrix)
return self._new(im)
if mode == "P" and palette == ADAPTIVE:
im = self.im.quantize(colors)
return self._new(im)
- # colourspace conversion
+ # colorspace conversion
if dither is None:
dither = FLOYDSTEINBERG
+ # Use transparent conversion to promote from transparent color to an alpha channel.
+ if self.mode in ("L", "RGB") and mode == "RGBA" and "transparency" in self.info:
+ return self._new(self.im.convert_transparent(mode, self.info['transparency']))
+
try:
im = self.im.convert(mode, dither)
except ValueError:
@@ -710,6 +745,7 @@ class Image:
# methods:
# 0 = median cut
# 1 = maximum coverage
+ # 2 = fast octree
# NOTE: this functionality will be moved to the extended
# quantizer interface in a later version of PIL.
@@ -731,34 +767,33 @@ class Image:
im = self.im.quantize(colors, method, kmeans)
return self._new(im)
- ##
- # Copies this image. Use this method if you wish to paste things
- # into an image, but still retain the original.
- #
- # @return An Image object.
-
def copy(self):
- "Copy raster data"
+ """
+ Copies this image. Use this method if you wish to paste things
+ into an image, but still retain the original.
+ :rtype: :py:class:`~PIL.Image.Image`
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
self.load()
im = self.im.copy()
return self._new(im)
- ##
- # Returns a rectangular region from this image. The box is a
- # 4-tuple defining the left, upper, right, and lower pixel
- # coordinate.
- #
- # This is a lazy operation. Changes to the source image may or
- # may not be reflected in the cropped image. To break the
- # connection, call the {@link #Image.load} method on the cropped
- # copy.
- #
- # @param The crop rectangle, as a (left, upper, right, lower)-tuple.
- # @return An Image object.
-
def crop(self, box=None):
- "Crop region from image"
+ """
+ Returns a rectangular region from this image. The box is a
+ 4-tuple defining the left, upper, right, and lower pixel
+ coordinate.
+
+ This is a lazy operation. Changes to the source image may or
+ may not be reflected in the cropped image. To break the
+ connection, call the :py:meth:`~PIL.Image.Image.load` method on
+ the cropped copy.
+
+ :param box: The crop rectangle, as a (left, upper, right, lower)-tuple.
+ :rtype: :py:class:`~PIL.Image.Image`
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
self.load()
if box is None:
@@ -767,22 +802,23 @@ class Image:
# lazy operation
return _ImageCrop(self, box)
- ##
- # Configures the image file loader so it returns a version of the
- # image that as closely as possible matches the given mode and
- # size. For example, you can use this method to convert a colour
- # JPEG to greyscale while loading it, or to extract a 128x192
- # version from a PCD file.
- #
- # Note that this method modifies the Image object in place. If
- # the image has already been loaded, this method has no effect.
- #
- # @param mode The requested mode.
- # @param size The requested size.
-
def draft(self, mode, size):
- "Configure image decoder"
+ """
+ NYI
+
+ Configures the image file loader so it returns a version of the
+ image that as closely as possible matches the given mode and
+ size. For example, you can use this method to convert a color
+ JPEG to greyscale while loading it, or to extract a 128x192
+ version from a PCD file.
+ Note that this method modifies the :py:class:`~PIL.Image.Image` object
+ in place. If the image has already been loaded, this method has no
+ effect.
+
+ :param mode: The requested mode.
+ :param size: The requested size.
+ """
pass
def _expand(self, xmargin, ymargin=None):
@@ -791,20 +827,17 @@ class Image:
self.load()
return self._new(self.im.expand(xmargin, ymargin, 0))
- ##
- # Filters this image using the given filter. For a list of
- # available filters, see the ImageFilter module.
- #
- # @param filter Filter kernel.
- # @return An Image object.
- # @see ImageFilter
-
def filter(self, filter):
- "Apply environment filter to image"
+ """
+ Filters this image using the given filter. For a list of
+ available filters, see the :py:mod:`~PIL.ImageFilter` module.
+
+ :param filter: Filter kernel.
+ :returns: An :py:class:`~PIL.Image.Image` object. """
self.load()
- if callable(filter):
+ if isinstance(filter, collections.Callable):
filter = filter()
if not hasattr(filter, "filter"):
raise TypeError("filter argument should be ImageFilter.Filter instance or class")
@@ -817,41 +850,39 @@ class Image:
ims.append(self._new(filter.filter(self.im.getband(c))))
return merge(self.mode, ims)
- ##
- # Returns a tuple containing the name of each band in this image.
- # For example, getbands on an RGB image returns ("R", "G", "B").
- #
- # @return A tuple containing band names.
-
def getbands(self):
- "Get band names"
+ """
+ Returns a tuple containing the name of each band in this image.
+ For example, **getbands** on an RGB image returns ("R", "G", "B").
+ :returns: A tuple containing band names.
+ :rtype: tuple
+ """
return ImageMode.getmode(self.mode).bands
- ##
- # Calculates the bounding box of the non-zero regions in the
- # image.
- #
- # @return The bounding box is returned as a 4-tuple defining the
- # left, upper, right, and lower pixel coordinate. If the image
- # is completely empty, this method returns None.
-
def getbbox(self):
- "Get bounding box of actual data (non-zero pixels) in image"
+ """
+ Calculates the bounding box of the non-zero regions in the
+ image.
+
+ :returns: The bounding box is returned as a 4-tuple defining the
+ left, upper, right, and lower pixel coordinate. If the image
+ is completely empty, this method returns None.
+
+ """
self.load()
return self.im.getbbox()
- ##
- # Returns a list of colors used in this image.
- #
- # @param maxcolors Maximum number of colors. If this number is
- # exceeded, this method returns None. The default limit is
- # 256 colors.
- # @return An unsorted list of (count, pixel) values.
-
def getcolors(self, maxcolors=256):
- "Get colors from image, up to given limit"
+ """
+ Returns a list of colors used in this image.
+
+ :param maxcolors: Maximum number of colors. If this number is
+ exceeded, this method returns None. The default limit is
+ 256 colors.
+ :returns: An unsorted list of (count, pixel) values.
+ """
self.load()
if self.mode in ("1", "L", "P"):
@@ -865,40 +896,38 @@ class Image:
return out
return self.im.getcolors(maxcolors)
- ##
- # Returns the contents of this image as a sequence object
- # containing pixel values. The sequence object is flattened, so
- # that values for line one follow directly after the values of
- # line zero, and so on.
- #
- # Note that the sequence object returned by this method is an
- # internal PIL data type, which only supports certain sequence
- # operations. To convert it to an ordinary sequence (e.g. for
- # printing), use list(im.getdata()).
- #
- # @param band What band to return. The default is to return
- # all bands. To return a single band, pass in the index
- # value (e.g. 0 to get the "R" band from an "RGB" image).
- # @return A sequence-like object.
-
def getdata(self, band = None):
- "Get image data as sequence object."
+ """
+ Returns the contents of this image as a sequence object
+ containing pixel values. The sequence object is flattened, so
+ that values for line one follow directly after the values of
+ line zero, and so on.
+
+ Note that the sequence object returned by this method is an
+ internal PIL data type, which only supports certain sequence
+ operations. To convert it to an ordinary sequence (e.g. for
+ printing), use **list(im.getdata())**.
+
+ :param band: What band to return. The default is to return
+ all bands. To return a single band, pass in the index
+ value (e.g. 0 to get the "R" band from an "RGB" image).
+ :returns: A sequence-like object.
+ """
self.load()
if band is not None:
return self.im.getband(band)
return self.im # could be abused
- ##
- # Gets the the minimum and maximum pixel values for each band in
- # the image.
- #
- # @return For a single-band image, a 2-tuple containing the
- # minimum and maximum pixel value. For a multi-band image,
- # a tuple containing one 2-tuple for each band.
-
def getextrema(self):
- "Get min/max value"
+ """
+ Gets the the minimum and maximum pixel values for each band in
+ the image.
+
+ :returns: For a single-band image, a 2-tuple containing the
+ minimum and maximum pixel value. For a multi-band image,
+ a tuple containing one 2-tuple for each band.
+ """
self.load()
if self.im.bands > 1:
@@ -908,82 +937,78 @@ class Image:
return tuple(extrema)
return self.im.getextrema()
- ##
- # Returns a PyCObject that points to the internal image memory.
- #
- # @return A PyCObject object.
-
def getim(self):
- "Get PyCObject pointer to internal image memory"
+ """
+ Returns a capsule that points to the internal image memory.
+
+ :returns: A capsule object.
+ """
self.load()
return self.im.ptr
- ##
- # Returns the image palette as a list.
- #
- # @return A list of color values [r, g, b, ...], or None if the
- # image has no palette.
-
def getpalette(self):
- "Get palette contents."
+ """
+ Returns the image palette as a list.
+
+ :returns: A list of color values [r, g, b, ...], or None if the
+ image has no palette.
+ """
self.load()
try:
- return map(ord, self.im.getpalette())
+ if bytes is str:
+ return [i8(c) for c in self.im.getpalette()]
+ else:
+ return list(self.im.getpalette())
except ValueError:
return None # no palette
- ##
- # Returns the pixel value at a given position.
- #
- # @param xy The coordinate, given as (x, y).
- # @return The pixel value. If the image is a multi-layer image,
- # this method returns a tuple.
-
def getpixel(self, xy):
- "Get pixel value"
+ """
+ Returns the pixel value at a given position.
+
+ :param xy: The coordinate, given as (x, y).
+ :returns: The pixel value. If the image is a multi-layer image,
+ this method returns a tuple.
+ """
self.load()
return self.im.getpixel(xy)
- ##
- # Returns the horizontal and vertical projection.
- #
- # @return Two sequences, indicating where there are non-zero
- # pixels along the X-axis and the Y-axis, respectively.
-
def getprojection(self):
- "Get projection to x and y axes"
+ """
+ Get projection to x and y axes
+
+ :returns: Two sequences, indicating where there are non-zero
+ pixels along the X-axis and the Y-axis, respectively.
+ """
self.load()
x, y = self.im.getprojection()
- return map(ord, x), map(ord, y)
-
- ##
- # Returns a histogram for the image. The histogram is returned as
- # a list of pixel counts, one for each pixel value in the source
- # image. If the image has more than one band, the histograms for
- # all bands are concatenated (for example, the histogram for an
- # "RGB" image contains 768 values).
- #
- # A bilevel image (mode "1") is treated as a greyscale ("L") image
- # by this method.
- #
- # If a mask is provided, the method returns a histogram for those
- # parts of the image where the mask image is non-zero. The mask
- # image must have the same size as the image, and be either a
- # bi-level image (mode "1") or a greyscale image ("L").
- #
- # @def histogram(mask=None)
- # @param mask An optional mask.
- # @return A list containing pixel counts.
+ return [i8(c) for c in x], [i8(c) for c in y]
def histogram(self, mask=None, extrema=None):
- "Take histogram of image"
+ """
+ Returns a histogram for the image. The histogram is returned as
+ a list of pixel counts, one for each pixel value in the source
+ image. If the image has more than one band, the histograms for
+ all bands are concatenated (for example, the histogram for an
+ "RGB" image contains 768 values).
+ A bilevel image (mode "1") is treated as a greyscale ("L") image
+ by this method.
+
+ If a mask is provided, the method returns a histogram for those
+ parts of the image where the mask image is non-zero. The mask
+ image must have the same size as the image, and be either a
+ bi-level image (mode "1") or a greyscale image ("L").
+
+ :param mask: An optional mask.
+ :returns: A list containing pixel counts.
+ """
self.load()
if mask:
mask.load()
@@ -994,70 +1019,69 @@ class Image:
return self.im.histogram(extrema)
return self.im.histogram()
- ##
- # (Deprecated) Returns a copy of the image where the 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.
- #
- # This method is deprecated. New code should use the offset
- # function in the ImageChops module.
- #
- # @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(self, xoffset, yoffset=None):
- "(deprecated) Offset image in horizontal and/or vertical direction"
+ """
+ .. deprecated:: 2.0
+
+ .. note:: New code should use :py:func:`PIL.ImageChops.offset`.
+
+ Returns a copy of the image where the 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.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if warnings:
warnings.warn(
"'offset' is deprecated; use 'ImageChops.offset' instead",
DeprecationWarning, stacklevel=2
)
- import ImageChops
+ from PIL import ImageChops
return ImageChops.offset(self, xoffset, yoffset)
- ##
- # Pastes another image into this image. The box argument is either
- # a 2-tuple giving the upper left corner, a 4-tuple defining the
- # left, upper, right, and lower pixel coordinate, or None (same as
- # (0, 0)). If a 4-tuple is given, the size of the pasted image
- # must match the size of the region.
- #
- # If the modes don't match, the pasted image is converted to the
- # mode of this image (see the {@link #Image.convert} method for
- # details).
- #
- # Instead of an image, the source can be a integer or tuple
- # containing pixel values. The method then fills the region
- # with the given colour. When creating RGB images, you can
- # also use colour strings as supported by the ImageColor module.
- #
- # If a mask is given, this method updates only the regions
- # indicated by the mask. You can use either "1", "L" or "RGBA"
- # images (in the latter case, the alpha band is used as mask).
- # Where the mask is 255, the given image is copied as is. Where
- # the mask is 0, the current value is preserved. Intermediate
- # values can be used for transparency effects.
- #
- # Note that if you paste an "RGBA" image, the alpha band is
- # ignored. You can work around this by using the same image as
- # both source image and mask.
- #
- # @param im Source image or pixel value (integer or tuple).
- # @param box An optional 4-tuple giving the region to paste into.
- # If a 2-tuple is used instead, it's treated as the upper left
- # corner. If omitted or None, the source is pasted into the
- # upper left corner.
- #
- # If an image is given as the second argument and there is no
- # third, the box defaults to (0, 0), and the second argument
- # is interpreted as a mask image.
- # @param mask An optional mask image.
- # @return An Image object.
-
def paste(self, im, box=None, mask=None):
- "Paste other image into region"
+ """
+ Pastes another image into this image. The box argument is either
+ a 2-tuple giving the upper left corner, a 4-tuple defining the
+ left, upper, right, and lower pixel coordinate, or None (same as
+ (0, 0)). If a 4-tuple is given, the size of the pasted image
+ must match the size of the region.
+
+ If the modes don't match, the pasted image is converted to the mode of
+ this image (see the :py:meth:`~PIL.Image.Image.convert` method for
+ details).
+
+ Instead of an image, the source can be a integer or tuple
+ containing pixel values. The method then fills the region
+ with the given color. When creating RGB images, you can
+ also use color strings as supported by the ImageColor module.
+
+ If a mask is given, this method updates only the regions
+ indicated by the mask. You can use either "1", "L" or "RGBA"
+ images (in the latter case, the alpha band is used as mask).
+ Where the mask is 255, the given image is copied as is. Where
+ the mask is 0, the current value is preserved. Intermediate
+ values can be used for transparency effects.
+
+ Note that if you paste an "RGBA" image, the alpha band is
+ ignored. You can work around this by using the same image as
+ both source image and mask.
+
+ :param im: Source image or pixel value (integer or tuple).
+ :param box: An optional 4-tuple giving the region to paste into.
+ If a 2-tuple is used instead, it's treated as the upper left
+ corner. If omitted or None, the source is pasted into the
+ upper left corner.
+
+ If an image is given as the second argument and there is no
+ third, the box defaults to (0, 0), and the second argument
+ is interpreted as a mask image.
+ :param mask: An optional mask image.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if isImageType(box) and mask is None:
# abbreviated paste(im, mask) syntax
@@ -1081,7 +1105,7 @@ class Image:
box = box + (box[0]+size[0], box[1]+size[1])
if isStringType(im):
- import ImageColor
+ from PIL import ImageColor
im = ImageColor.getcolor(im, self.mode)
elif isImageType(im):
@@ -1102,35 +1126,38 @@ class Image:
else:
self.im.paste(im, box)
- ##
- # Maps this image through a lookup table or function.
- #
- # @param lut A lookup table, containing 256 values per band in the
- # image. A function can be used instead, it should take a single
- # argument. The function is called once for each possible pixel
- # value, and the resulting table is applied to all bands of the
- # image.
- # @param mode Output mode (default is same as input). In the
- # current version, this can only be used if the source image
- # has mode "L" or "P", and the output has mode "1".
- # @return An Image object.
-
def point(self, lut, mode=None):
- "Map image through lookup table"
+ """
+ Maps this image through a lookup table or function.
+
+ :param lut: A lookup table, containing 256 (or 65336 if
+ self.mode=="I" and mode == "L") values per band in the
+ image. A function can be used instead, it should take a
+ single argument. The function is called once for each
+ possible pixel value, and the resulting table is applied to
+ all bands of the image.
+ :param mode: Output mode (default is same as input). In the
+ current version, this can only be used if the source image
+ has mode "L" or "P", and the output has mode "1" or the
+ source image mode is "I" and the output mode is "L".
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
self.load()
if isinstance(lut, ImagePointHandler):
return lut.point(self)
- if not isSequenceType(lut):
+ if callable(lut):
# if it isn't a list, it should be a function
if self.mode in ("I", "I;16", "F"):
# check if the function can be used with point_transform
+ # UNDONE wiredfool -- I think this prevents us from ever doing
+ # a gamma function point transform on > 8bit images.
scale, offset = _getscaleoffset(lut)
return self._new(self.im.point_transform(scale, offset))
# for other modes, convert the function to a table
- lut = map(lut, range(256)) * self.im.bands
+ lut = [lut(i) for i in range(256)] * self.im.bands
if self.mode == "F":
# FIXME: _imaging returns a confusing error message for this case
@@ -1138,17 +1165,16 @@ class Image:
return self._new(self.im.point(lut, mode))
- ##
- # Adds or replaces the alpha layer in this image. If the image
- # does not have an alpha layer, it's converted to "LA" or "RGBA".
- # The new layer must be either "L" or "1".
- #
- # @param im The new alpha layer. This can either be an "L" or "1"
- # image having the same size as this image, or an integer or
- # other color value.
-
def putalpha(self, alpha):
- "Set alpha layer"
+ """
+ Adds or replaces the alpha layer in this image. If the image
+ does not have an alpha layer, it's converted to "LA" or "RGBA".
+ The new layer must be either "L" or "1".
+
+ :param alpha: The new alpha layer. This can either be an "L" or "1"
+ image having the same size as this image, or an integer or
+ other color value.
+ """
self.load()
if self.readonly:
@@ -1194,19 +1220,18 @@ class Image:
self.im.putband(alpha.im, band)
- ##
- # Copies pixel data to this image. This method copies data from a
- # sequence object into the image, starting at the upper left
- # corner (0, 0), and continuing until either the image or the
- # sequence ends. The scale and offset values are used to adjust
- # the sequence values: pixel = value*scale + offset.
- #
- # @param data A sequence object.
- # @param scale An optional scale value. The default is 1.0.
- # @param offset An optional offset value. The default is 0.0.
-
def putdata(self, data, scale=1.0, offset=0.0):
- "Put data from a sequence object into an image."
+ """
+ Copies pixel data to this image. This method copies data from a
+ sequence object into the image, starting at the upper left
+ corner (0, 0), and continuing until either the image or the
+ sequence ends. The scale and offset values are used to adjust
+ the sequence values: **pixel = value*scale + offset**.
+
+ :param data: A sequence object.
+ :param scale: An optional scale value. The default is 1.0.
+ :param offset: An optional offset value. The default is 0.0.
+ """
self.load()
if self.readonly:
@@ -1214,19 +1239,18 @@ class Image:
self.im.putdata(data, scale, offset)
- ##
- # Attaches a palette to this image. The image must be a "P" or
- # "L" image, and the palette sequence must contain 768 integer
- # values, where each group of three values represent the red,
- # green, and blue values for the corresponding pixel
- # index. Instead of an integer sequence, you can use an 8-bit
- # string.
- #
- # @def putpalette(data)
- # @param data A palette sequence (either a list or a string).
-
def putpalette(self, data, rawmode="RGB"):
- "Put palette data into an image."
+ """
+ Attaches a palette to this image. The image must be a "P" or
+ "L" image, and the palette sequence must contain 768 integer
+ values, where each group of three values represent the red,
+ green, and blue values for the corresponding pixel
+ index. Instead of an integer sequence, you can use an 8-bit
+ string.
+
+ :param data: A palette sequence (either a list or a string).
+ """
+ from PIL import ImagePalette
if self.mode not in ("L", "P"):
raise ValueError("illegal image mode")
@@ -1234,31 +1258,36 @@ class Image:
if isinstance(data, ImagePalette.ImagePalette):
palette = ImagePalette.raw(data.rawmode, data.palette)
else:
- if not isStringType(data):
- data = string.join(map(chr, data), "")
+ if not isinstance(data, bytes):
+ if bytes is str:
+ data = "".join(chr(x) for x in data)
+ else:
+ data = bytes(data)
palette = ImagePalette.raw(rawmode, data)
self.mode = "P"
self.palette = palette
self.palette.mode = "RGB"
self.load() # install new palette
- ##
- # Modifies the pixel at the given position. The colour is given as
- # a single numerical value for single-band images, and a tuple for
- # multi-band images.
- #
- # Note that this method is relatively slow. For more extensive
- # changes, use {@link #Image.paste} or the ImageDraw module
- # instead.
- #
- # @param xy The pixel coordinate, given as (x, y).
- # @param value The pixel value.
- # @see #Image.paste
- # @see #Image.putdata
- # @see ImageDraw
-
def putpixel(self, xy, value):
- "Set pixel value"
+ """
+ Modifies the pixel at the given position. The color is given as
+ a single numerical value for single-band images, and a tuple for
+ multi-band images.
+
+ Note that this method is relatively slow. For more extensive changes,
+ use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw`
+ module instead.
+
+ See:
+
+ * :py:meth:`~PIL.Image.Image.paste`
+ * :py:meth:`~PIL.Image.Image.putdata`
+ * :py:mod:`~PIL.ImageDraw`
+
+ :param xy: The pixel coordinate, given as (x, y).
+ :param value: The pixel value.
+ """
self.load()
if self.readonly:
@@ -1266,23 +1295,22 @@ class Image:
return self.im.putpixel(xy, value)
- ##
- # Returns a resized copy of this image.
- #
- # @def resize(size, filter=NEAREST)
- # @param size The requested size in pixels, as a 2-tuple:
- # (width, height).
- # @param filter An optional resampling filter. This can be
- # one of NEAREST (use nearest neighbour), BILINEAR
- # (linear interpolation in a 2x2 environment), BICUBIC
- # (cubic spline interpolation in a 4x4 environment), or
- # ANTIALIAS (a high-quality downsampling filter).
- # If omitted, or if the image has mode "1" or "P", it is
- # set NEAREST.
- # @return An Image object.
-
def resize(self, size, resample=NEAREST):
- "Resize image"
+ """
+ Returns a resized copy of this image.
+
+ :param size: The requested size in pixels, as a 2-tuple:
+ (width, height).
+ :param resample: An optional resampling filter. This can be
+ one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
+ :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
+ environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline
+ interpolation in a 4x4 environment), or
+ :py:attr:`PIL.Image.ANTIALIAS` (a high-quality downsampling filter).
+ If omitted, or if the image has mode "1" or "P", it is
+ set :py:attr:`PIL.Image.NEAREST`.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS):
raise ValueError("unknown resampling filter")
@@ -1292,6 +1320,9 @@ class Image:
if self.mode in ("1", "P"):
resample = NEAREST
+ if self.mode == 'RGBA':
+ return self.convert('RGBa').resize(size, resample).convert('RGBA')
+
if resample == ANTIALIAS:
# requires stretch support (imToolkit & PIL 1.1.3)
try:
@@ -1303,27 +1334,26 @@ class Image:
return self._new(im)
- ##
- # Returns a rotated copy of this image. This method returns a
- # copy of this image, rotated the given number of degrees counter
- # clockwise around its centre.
- #
- # @def rotate(angle, filter=NEAREST)
- # @param angle In degrees counter clockwise.
- # @param filter An optional resampling filter. This can be
- # one of NEAREST (use nearest neighbour), BILINEAR
- # (linear interpolation in a 2x2 environment), or BICUBIC
- # (cubic spline interpolation in a 4x4 environment).
- # If omitted, or if the image has mode "1" or "P", it is
- # set NEAREST.
- # @param expand Optional expansion flag. If true, expands the output
- # image to make it large enough to hold the entire rotated image.
- # If false or omitted, make the output image the same size as the
- # input image.
- # @return An Image object.
-
def rotate(self, angle, resample=NEAREST, expand=0):
- "Rotate image. Angle given as degrees counter-clockwise."
+ """
+ Returns a rotated copy of this image. This method returns a
+ copy of this image, rotated the given number of degrees counter
+ clockwise around its centre.
+
+ :param angle: In degrees counter clockwise.
+ :param filter: An optional resampling filter. This can be
+ one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
+ :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
+ environment), or :py:attr:`PIL.Image.BICUBIC`
+ (cubic spline interpolation in a 4x4 environment).
+ If omitted, or if the image has mode "1" or "P", it is
+ set :py:attr:`PIL.Image.NEAREST`.
+ :param expand: Optional expansion flag. If true, expands the output
+ image to make it large enough to hold the entire rotated image.
+ If false or omitted, make the output image the same size as the
+ input image.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if expand:
import math
@@ -1332,7 +1362,8 @@ class Image:
math.cos(angle), math.sin(angle), 0.0,
-math.sin(angle), math.cos(angle), 0.0
]
- def transform(x, y, (a, b, c, d, e, f)=matrix):
+ def transform(x, y, matrix=matrix):
+ (a, b, c, d, e, f) = matrix
return a*x + b*y + c, d*x + e*y + f
# calculate output size
@@ -1363,41 +1394,39 @@ class Image:
return self._new(self.im.rotate(angle, resample))
- ##
- # Saves this image under the given filename. If no format is
- # specified, the format to use is determined from the filename
- # extension, if possible.
- #
- # Keyword options can be used to provide additional instructions
- # to the writer. If a writer doesn't recognise an option, it is
- # silently ignored. The available options are described later in
- # this handbook.
- #
- # You can use a file object instead of a filename. In this case,
- # you must always specify the format. The file object must
- # implement the seek, tell, and write
- # methods, and be opened in binary mode.
- #
- # @def save(file, format=None, **options)
- # @param file File name or file object.
- # @param format Optional format override. If omitted, the
- # format to use is determined from the filename extension.
- # If a file object was used instead of a filename, this
- # parameter should always be used.
- # @param **options Extra parameters to the image writer.
- # @return None
- # @exception KeyError If the output format could not be determined
- # from the file name. Use the format option to solve this.
- # @exception IOError If the file could not be written. The file
- # may have been created, and may contain partial data.
-
def save(self, fp, format=None, **params):
- "Save image to file or stream"
+ """
+ Saves this image under the given filename. If no format is
+ specified, the format to use is determined from the filename
+ extension, if possible.
- if isStringType(fp):
+ Keyword options can be used to provide additional instructions
+ to the writer. If a writer doesn't recognise an option, it is
+ silently ignored. The available options are described later in
+ this handbook.
+
+ You can use a file object instead of a filename. In this case,
+ you must always specify the format. The file object must
+ implement the **seek**, **tell**, and **write**
+ methods, and be opened in binary mode.
+
+ :param file: File name or file object.
+ :param format: Optional format override. If omitted, the
+ format to use is determined from the filename extension.
+ If a file object was used instead of a filename, this
+ parameter should always be used.
+ :param options: Extra parameters to the image writer.
+ :returns: None
+ :exception KeyError: If the output format could not be determined
+ from the file name. Use the format option to solve this.
+ :exception IOError: If the file could not be written. The file
+ may have been created, and may contain partial data.
+ """
+
+ if isPath(fp):
filename = fp
else:
- if hasattr(fp, "name") and isStringType(fp.name):
+ if hasattr(fp, "name") and isPath(fp.name):
filename = fp.name
else:
filename = ""
@@ -1410,7 +1439,7 @@ class Image:
preinit()
- ext = string.lower(os.path.splitext(filename)[1])
+ ext = os.path.splitext(filename)[1].lower()
if not format:
try:
@@ -1423,14 +1452,13 @@ class Image:
raise KeyError(ext) # unknown extension
try:
- save_handler = SAVE[string.upper(format)]
+ save_handler = SAVE[format.upper()]
except KeyError:
init()
- save_handler = SAVE[string.upper(format)] # unknown format
+ save_handler = SAVE[format.upper()] # unknown format
- if isStringType(fp):
- import __builtin__
- fp = __builtin__.open(fp, "wb")
+ if isPath(fp):
+ fp = builtins.open(fp, "wb")
close = 1
else:
close = 0
@@ -1442,113 +1470,108 @@ class Image:
if close:
fp.close()
- ##
- # Seeks to the given frame in this sequence file. If you seek
- # beyond the end of the sequence, the method raises an
- # EOFError exception. When a sequence file is opened, the
- # library automatically seeks to frame 0.
- #
- # Note that in the current version of the library, most sequence
- # formats only allows you to seek to the next frame.
- #
- # @param frame Frame number, starting at 0.
- # @exception EOFError If the call attempts to seek beyond the end
- # of the sequence.
- # @see #Image.tell
-
def seek(self, frame):
- "Seek to given frame in sequence file"
+ """
+ Seeks to the given frame in this sequence file. If you seek
+ beyond the end of the sequence, the method raises an
+ **EOFError** exception. When a sequence file is opened, the
+ library automatically seeks to frame 0.
+
+ Note that in the current version of the library, most sequence
+ formats only allows you to seek to the next frame.
+
+ See :py:meth:`~PIL.Image.Image.tell`.
+
+ :param frame: Frame number, starting at 0.
+ :exception EOFError: If the call attempts to seek beyond the end
+ of the sequence.
+ """
# overridden by file handlers
if frame != 0:
raise EOFError
- ##
- # Displays this image. This method is mainly intended for
- # debugging purposes.
- #
- # On Unix platforms, this method saves the image to a temporary
- # PPM file, and calls the xv utility.
- #
- # On Windows, it saves the image to a temporary BMP file, and uses
- # the standard BMP display utility to show it (usually Paint).
- #
- # @def show(title=None)
- # @param title Optional title to use for the image window,
- # where possible.
-
def show(self, title=None, command=None):
- "Display image (for debug purposes only)"
+ """
+ Displays this image. This method is mainly intended for
+ debugging purposes.
+
+ On Unix platforms, this method saves the image to a temporary
+ PPM file, and calls the **xv** utility.
+
+ On Windows, it saves the image to a temporary BMP file, and uses
+ the standard BMP display utility to show it (usually Paint).
+
+ :param title: Optional title to use for the image window,
+ where possible.
+ :param command: command used to show the image
+ """
_show(self, title=title, command=command)
- ##
- # Split this image into individual bands. This method returns a
- # tuple of individual image bands from an image. For example,
- # splitting an "RGB" image creates three new images each
- # containing a copy of one of the original bands (red, green,
- # blue).
- #
- # @return A tuple containing bands.
-
def split(self):
- "Split image into bands"
+ """
+ Split this image into individual bands. This method returns a
+ tuple of individual image bands from an image. For example,
+ splitting an "RGB" image creates three new images each
+ containing a copy of one of the original bands (red, green,
+ blue).
+ :returns: A tuple containing bands.
+ """
+
+ self.load()
if self.im.bands == 1:
ims = [self.copy()]
else:
ims = []
- self.load()
for i in range(self.im.bands):
ims.append(self._new(self.im.getband(i)))
return tuple(ims)
- ##
- # Returns the current frame number.
- #
- # @return Frame number, starting with 0.
- # @see #Image.seek
-
def tell(self):
- "Return current frame number"
+ """
+ Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`.
+ :returns: Frame number, starting with 0.
+ """
return 0
- ##
- # Make this image into a thumbnail. This method modifies the
- # image to contain a thumbnail version of itself, no larger than
- # the given size. This method calculates an appropriate thumbnail
- # size to preserve the aspect of the image, calls the {@link
- # #Image.draft} method to configure the file reader (where
- # applicable), and finally resizes the image.
- #
- # Note that the bilinear and bicubic filters in the current
- # version of PIL are not well-suited for thumbnail generation.
- # You should use ANTIALIAS unless speed is much more
- # important than quality.
- #
- # Also note that this function modifies the Image object in place.
- # If you need to use the full resolution image as well, apply this
- # method to a {@link #Image.copy} of the original image.
- #
- # @param size Requested size.
- # @param resample Optional resampling filter. This can be one
- # of NEAREST, BILINEAR, BICUBIC, or
- # ANTIALIAS (best quality). If omitted, it defaults
- # to NEAREST (this will be changed to ANTIALIAS in a
- # future version).
- # @return None
-
def thumbnail(self, size, resample=NEAREST):
- "Create thumbnail representation (modifies image in place)"
+ """
+ Make this image into a thumbnail. This method modifies the
+ image to contain a thumbnail version of itself, no larger than
+ the given size. This method calculates an appropriate thumbnail
+ size to preserve the aspect of the image, calls the
+ :py:meth:`~PIL.Image.Image.draft` method to configure the file reader
+ (where applicable), and finally resizes the image.
+
+ Note that the bilinear and bicubic filters in the current
+ version of PIL are not well-suited for thumbnail generation.
+ You should use :py:attr:`PIL.Image.ANTIALIAS` unless speed is much more
+ important than quality.
+
+ Also note that this function modifies the :py:class:`~PIL.Image.Image`
+ object in place. If you need to use the full resolution image as well, apply
+ this method to a :py:meth:`~PIL.Image.Image.copy` of the original image.
+
+ :param size: Requested size.
+ :param resample: Optional resampling filter. This can be one
+ of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`,
+ :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS`
+ (best quality). If omitted, it defaults to
+ :py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a
+ future version).
+ :returns: None
+ """
# FIXME: the default resampling filter will be changed
# to ANTIALIAS in future versions
# preserve aspect ratio
x, y = self.size
- if x > size[0]: y = max(y * size[0] / x, 1); x = size[0]
- if y > size[1]: x = max(x * size[1] / y, 1); y = size[1]
+ if x > size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0])
+ if y > size[1]: x = int(max(x * size[1] / y, 1)); y = int(size[1])
size = x, y
if size == self.size:
@@ -1573,31 +1596,32 @@ class Image:
# FIXME: the different tranform methods need further explanation
# instead of bloating the method docs, add a separate chapter.
-
- ##
- # Transforms this image. This method creates a new image with the
- # given size, and the same mode as the original, and copies data
- # to the new image using the given transform.
- #
- # @def transform(size, method, data, resample=NEAREST)
- # @param size The output size.
- # @param method The transformation method. This is one of
- # EXTENT (cut out a rectangular subregion), AFFINE
- # (affine transform), PERSPECTIVE (perspective
- # transform), QUAD (map a quadrilateral to a
- # rectangle), or MESH (map a number of source quadrilaterals
- # in one operation).
- # @param data Extra data to the transformation method.
- # @param resample Optional resampling filter. It can be one of
- # NEAREST (use nearest neighbour), BILINEAR
- # (linear interpolation in a 2x2 environment), or
- # BICUBIC (cubic spline interpolation in a 4x4
- # environment). If omitted, or if the image has mode
- # "1" or "P", it is set to NEAREST.
- # @return An Image object.
-
def transform(self, size, method, data=None, resample=NEAREST, fill=1):
- "Transform image"
+ """
+ Transforms this image. This method creates a new image with the
+ given size, and the same mode as the original, and copies data
+ to the new image using the given transform.
+
+ :param size: The output size.
+ :param method: The transformation method. This is one of
+ :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion),
+ :py:attr:`PIL.Image.AFFINE` (affine transform),
+ :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform),
+ :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or
+ :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals
+ in one operation).
+ :param data: Extra data to the transformation method.
+ :param resample: Optional resampling filter. It can be one of
+ :py:attr:`PIL.Image.NEAREST` (use nearest neighbour),
+ :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2
+ environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline
+ interpolation in a 4x4 environment). If omitted, or if the image
+ has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
+
+ if self.mode == 'RGBA':
+ return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA')
if isinstance(method, ImageTransformHandler):
return method.transform(size, self, resample=resample, fill=fill)
@@ -1606,6 +1630,7 @@ class Image:
method, data = method.getdata()
if data is None:
raise ValueError("missing method data")
+
im = new(self.mode, size, None)
if method == MESH:
# list of quads
@@ -1664,14 +1689,15 @@ class Image:
self.im.transform2(box, image.im, method, data, resample, fill)
- ##
- # Returns a flipped or rotated copy of this image.
- #
- # @param method One of FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM,
- # ROTATE_90, ROTATE_180, or ROTATE_270.
-
def transpose(self, method):
- "Transpose image (flip or rotate in 90 degree steps)"
+ """
+ Transpose image (flip or rotate in 90 degree steps)
+
+ :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`,
+ :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`,
+ :py:attr:`PIL.Image.ROTATE_180`, or :py:attr:`PIL.Image.ROTATE_270`.
+ :returns: Returns a flipped or rotated copy of this image.
+ """
self.load()
im = self.im.transpose(method)
@@ -1734,21 +1760,21 @@ def _wedge():
return Image()._new(core.wedge("L"))
-##
-# Creates a new image with the given mode and size.
-#
-# @param mode The mode to use for the new image.
-# @param size A 2-tuple, containing (width, height) in pixels.
-# @param color What colour to use for the image. Default is black.
-# If given, this should be a single integer or floating point value
-# for single-band modes, and a tuple for multi-band modes (one value
-# per band). When creating RGB images, you can also use colour
-# strings as supported by the ImageColor module. If the colour is
-# None, the image is not initialised.
-# @return An Image object.
def new(mode, size, color=0):
- "Create a new image"
+ """
+ Creates a new image with the given mode and size.
+
+ :param mode: The mode to use for the new image.
+ :param size: A 2-tuple, containing (width, height) in pixels.
+ :param color: What color to use for the image. Default is black.
+ If given, this should be a single integer or floating point value
+ for single-band modes, and a tuple for multi-band modes (one value
+ per band). When creating RGB images, you can also use color
+ strings as supported by the ImageColor module. If the color is
+ None, the image is not initialised.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if color is None:
# don't initialize
@@ -1757,84 +1783,98 @@ def new(mode, size, color=0):
if isStringType(color):
# css3-style specifier
- import ImageColor
+ from PIL import ImageColor
color = ImageColor.getcolor(color, mode)
return Image()._new(core.fill(mode, size, color))
-##
-# Creates an image memory from pixel data in a string.
-#
-# In its simplest form, this function takes three arguments
-# (mode, size, and unpacked pixel data).
-#
-# You can also use any pixel decoder supported by PIL. For more
-# information on available decoders, see the section Writing Your Own File Decoder.
-#
-# Note that this function decodes pixel data only, not entire images.
-# If you have an entire image in a string, wrap it in a
-# StringIO object, and use {@link #open} to load it.
-#
-# @param mode The image mode.
-# @param size The image size.
-# @param data An 8-bit string containing raw data for the given mode.
-# @param decoder_name What decoder to use.
-# @param *args Additional parameters for the given decoder.
-# @return An Image object.
-def fromstring(mode, size, data, decoder_name="raw", *args):
- "Load image from string"
+def frombytes(mode, size, data, decoder_name="raw", *args):
+ """
+ Creates a copy of an image memory from pixel data in a buffer.
+
+ In its simplest form, this function takes three arguments
+ (mode, size, and unpacked pixel data).
+
+ You can also use any pixel decoder supported by PIL. For more
+ information on available decoders, see the section
+ **Writing Your Own File Decoder**.
+
+ Note that this function decodes pixel data only, not entire images.
+ If you have an entire image in a string, wrap it in a
+ :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load
+ it.
+
+ :param mode: The image mode.
+ :param size: The image size.
+ :param data: A byte buffer containing raw data for the given mode.
+ :param decoder_name: What decoder to use.
+ :param args: Additional parameters for the given decoder.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
# may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
+ if len(args) == 1 and isinstance(args[0], tuple):
args = args[0]
if decoder_name == "raw" and args == ():
args = mode
im = new(mode, size)
- im.fromstring(data, decoder_name, args)
+ im.frombytes(data, decoder_name, args)
return im
-##
-# (New in 1.1.4) Creates an image memory from pixel data in a string
-# or byte buffer.
-#
-# This function is similar to {@link #fromstring}, but uses data in
-# the byte buffer, where possible. This means that changes to the
-# original buffer object are reflected in this image). Not all modes
-# can share memory; supported modes include "L", "RGBX", "RGBA", and
-# "CMYK".
-#
-# Note that this function decodes pixel data only, not entire images.
-# If you have an entire image file in a string, wrap it in a
-# StringIO object, and use {@link #open} to load it.
-#
-# In the current version, the default parameters used for the "raw"
-# decoder differs from that used for {@link fromstring}. This is a
-# bug, and will probably be fixed in a future release. The current
-# release issues a warning if you do this; to disable the warning,
-# you should provide the full set of parameters. See below for
-# details.
-#
-# @param mode The image mode.
-# @param size The image size.
-# @param data An 8-bit string or other buffer object containing raw
-# data for the given mode.
-# @param decoder_name What decoder to use.
-# @param *args Additional parameters for the given decoder. For the
-# default encoder ("raw"), it's recommended that you provide the
-# full set of parameters:
-# frombuffer(mode, size, data, "raw", mode, 0, 1).
-# @return An Image object.
-# @since 1.1.4
+def fromstring(*args, **kw):
+ """Deprecated alias to frombytes.
+
+ .. deprecated:: 2.0
+ """
+ warnings.warn(
+ 'fromstring() is deprecated. Please call frombytes() instead.',
+ DeprecationWarning,
+ stacklevel=2
+ )
+ return frombytes(*args, **kw)
+
def frombuffer(mode, size, data, decoder_name="raw", *args):
- "Load image from string or buffer"
+ """
+ Creates an image memory referencing pixel data in a byte buffer.
+
+ This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data
+ in the byte buffer, where possible. This means that changes to the
+ original buffer object are reflected in this image). Not all modes can
+ share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK".
+
+ Note that this function decodes pixel data only, not entire images.
+ If you have an entire image file in a string, wrap it in a
+ **BytesIO** object, and use :py:func:`~PIL.Image.open` to load it.
+
+ In the current version, the default parameters used for the "raw" decoder
+ differs from that used for :py:func:`~PIL.Image.fromstring`. This is a
+ bug, and will probably be fixed in a future release. The current release
+ issues a warning if you do this; to disable the warning, you should provide
+ the full set of parameters. See below for details.
+
+ :param mode: The image mode.
+ :param size: The image size.
+ :param data: A bytes or other buffer object containing raw
+ data for the given mode.
+ :param decoder_name: What decoder to use.
+ :param args: Additional parameters for the given decoder. For the
+ default encoder ("raw"), it's recommended that you provide the
+ full set of parameters::
+
+ frombuffer(mode, size, data, "raw", mode, 0, 1)
+
+ :returns: An :py:class:`~PIL.Image.Image` object.
+
+ .. versionadded:: 1.1.4
+ """
+ "Load image from bytes or buffer"
# may pass tuple instead of argument list
- if len(args) == 1 and isTupleType(args[0]):
+ if len(args) == 1 and isinstance(args[0], tuple):
args = args[0]
if decoder_name == "raw":
@@ -1855,21 +1895,23 @@ def frombuffer(mode, size, data, decoder_name="raw", *args):
im.readonly = 1
return im
- return fromstring(mode, size, data, decoder_name, args)
+ return frombytes(mode, size, data, decoder_name, args)
-##
-# (New in 1.1.6) Creates an image memory from an object exporting
-# the array interface (using the buffer protocol).
-#
-# If obj is not contiguous, then the tostring method is called
-# and {@link frombuffer} is used.
-#
-# @param obj Object with array interface
-# @param mode Mode to use (will be determined from type if None)
-# @return An image memory.
-
def fromarray(obj, mode=None):
+ """
+ Creates an image memory from an object exporting the array interface
+ (using the buffer protocol).
+
+ If obj is not contiguous, then the tobytes method is called
+ and :py:func:`~PIL.Image.frombuffer` is used.
+
+ :param obj: Object with array interface
+ :param mode: Mode to use (will be determined from type if None)
+ :returns: An image memory.
+
+ .. versionadded:: 1.1.6
+ """
arr = obj.__array_interface__
shape = arr['shape']
ndim = len(shape)
@@ -1897,7 +1939,10 @@ def fromarray(obj, mode=None):
size = shape[1], shape[0]
if strides is not None:
- obj = obj.tostring()
+ if hasattr(obj, 'tobytes'):
+ obj = obj.tobytes()
+ else:
+ obj = obj.tostring()
return frombuffer(mode, size, obj, "raw", rawmode, 0, 1)
@@ -1923,33 +1968,31 @@ _fromarray_typemap = {
_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I")
_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F")
-##
-# Opens and identifies the given image file.
-#
-# This is a lazy operation; this function identifies the file, but the
-# actual image data is not read from the file until you try to process
-# the data (or call the {@link #Image.load} method).
-#
-# @def open(file, mode="r")
-# @param file A filename (string) or a file object. The file object
-# must implement read, seek, and tell methods,
-# and be opened in binary mode.
-# @param mode The mode. If given, this argument must be "r".
-# @return An Image object.
-# @exception IOError If the file cannot be found, or the image cannot be
-# opened and identified.
-# @see #new
def open(fp, mode="r"):
- "Open an image file, without loading the raster data"
+ """
+ Opens and identifies the given image file.
+
+ This is a lazy operation; this function identifies the file, but the
+ actual image data is not read from the file until you try to process
+ the data (or call the :py:meth:`~PIL.Image.Image.load` method).
+ See :py:func:`~PIL.Image.new`.
+
+ :param file: A filename (string) or a file object. The file object
+ must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and
+ :py:meth:`~file.tell` methods, and be opened in binary mode.
+ :param mode: The mode. If given, this argument must be "r".
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ :exception IOError: If the file cannot be found, or the image cannot be
+ opened and identified.
+ """
if mode != "r":
raise ValueError("bad mode")
- if isStringType(fp):
- import __builtin__
+ if isPath(fp):
filename = fp
- fp = __builtin__.open(fp, "rb")
+ fp = builtins.open(fp, "rb")
else:
filename = ""
@@ -1964,6 +2007,8 @@ def open(fp, mode="r"):
fp.seek(0)
return factory(fp, filename)
except (SyntaxError, IndexError, TypeError):
+ #import traceback
+ #traceback.print_exc()
pass
if init():
@@ -1975,6 +2020,8 @@ def open(fp, mode="r"):
fp.seek(0)
return factory(fp, filename)
except (SyntaxError, IndexError, TypeError):
+ #import traceback
+ #traceback.print_exc()
pass
raise IOError("cannot identify image file")
@@ -1982,77 +2029,87 @@ def open(fp, mode="r"):
#
# Image processing.
-##
-# Creates a new image by interpolating between two input images, using
-# a constant alpha.
-#
-#
-# out = image1 * (1.0 - alpha) + image2 * alpha
-#
-#
-# @param im1 The first image.
-# @param im2 The second image. Must have the same mode and size as
-# the first image.
-# @param alpha The interpolation alpha factor. If alpha is 0.0, a
-# copy of the first image is returned. If alpha is 1.0, a copy of
-# the second image is returned. There are no restrictions on the
-# alpha value. If necessary, the result is clipped to fit into
-# the allowed output range.
-# @return An Image object.
+def alpha_composite(im1, im2):
+ """
+ Alpha composite im2 over im1.
+
+ :param im1: The first image.
+ :param im2: The second image. Must have the same mode and size as
+ the first image.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
+
+ im1.load()
+ im2.load()
+ return im1._new(core.alpha_composite(im1.im, im2.im))
+
def blend(im1, im2, alpha):
- "Interpolate between images."
+ """
+ Creates a new image by interpolating between two input images, using
+ a constant alpha.::
+
+ out = image1 * (1.0 - alpha) + image2 * alpha
+
+ :param im1: The first image.
+ :param im2: The second image. Must have the same mode and size as
+ the first image.
+ :param alpha: The interpolation alpha factor. If alpha is 0.0, a
+ copy of the first image is returned. If alpha is 1.0, a copy of
+ the second image is returned. There are no restrictions on the
+ alpha value. If necessary, the result is clipped to fit into
+ the allowed output range.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
im1.load()
im2.load()
return im1._new(core.blend(im1.im, im2.im, alpha))
-##
-# Creates a new image by interpolating between two input images,
-# using the mask as alpha.
-#
-# @param image1 The first image.
-# @param image2 The second image. Must have the same mode and
-# size as the first image.
-# @param mask A mask image. This image can can have mode
-# "1", "L", or "RGBA", and must have the same size as the
-# other two images.
def composite(image1, image2, mask):
- "Create composite image by blending images using a transparency mask"
+ """
+ Create composite image by blending images using a transparency mask.
+
+ :param image1: The first image.
+ :param image2: The second image. Must have the same mode and
+ size as the first image.
+ :param mask: A mask image. This image can can have mode
+ "1", "L", or "RGBA", and must have the same size as the
+ other two images.
+ """
image = image2.copy()
image.paste(image1, None, mask)
return image
-##
-# Applies the function (which should take one argument) to each pixel
-# in the given image. If the image has more than one band, the same
-# function is applied to each band. Note that the function is
-# evaluated once for each possible pixel value, so you cannot use
-# random components or other generators.
-#
-# @def eval(image, function)
-# @param image The input image.
-# @param function A function object, taking one integer argument.
-# @return An Image object.
def eval(image, *args):
- "Evaluate image expression"
+ """
+ Applies the function (which should take one argument) to each pixel
+ in the given image. If the image has more than one band, the same
+ function is applied to each band. Note that the function is
+ evaluated once for each possible pixel value, so you cannot use
+ random components or other generators.
+
+ :param image: The input image.
+ :param function: A function object, taking one integer argument.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
return image.point(args[0])
-##
-# Creates a new image from a number of single-band images.
-#
-# @param mode The mode to use for the output image.
-# @param bands A sequence containing one single-band image for
-# each band in the output image. All bands must have the
-# same size.
-# @return An Image object.
def merge(mode, bands):
- "Merge a set of single band images into a new multiband image."
+ """
+ Merge a set of single band images into a new multiband image.
+
+ :param mode: The mode to use for the output image.
+ :param bands: A sequence containing one single-band image for
+ each band in the output image. All bands must have the
+ same size.
+ :returns: An :py:class:`~PIL.Image.Image` object.
+ """
if getmodebands(mode) != len(bands) or "*" in mode:
raise ValueError("wrong number of bands")
@@ -2070,49 +2127,52 @@ def merge(mode, bands):
# --------------------------------------------------------------------
# Plugin registry
-##
-# Register an image file plugin. This function should not be used
-# in application code.
-#
-# @param id An image format identifier.
-# @param factory An image file factory method.
-# @param accept An optional function that can be used to quickly
-# reject images having another format.
-
def register_open(id, factory, accept=None):
- id = string.upper(id)
+ """
+ Register an image file plugin. This function should not be used
+ in application code.
+
+ :param id: An image format identifier.
+ :param factory: An image file factory method.
+ :param accept: An optional function that can be used to quickly
+ reject images having another format.
+ """
+ id = id.upper()
ID.append(id)
OPEN[id] = factory, accept
-##
-# Registers an image MIME type. This function should not be used
-# in application code.
-#
-# @param id An image format identifier.
-# @param mimetype The image MIME type for this format.
def register_mime(id, mimetype):
- MIME[string.upper(id)] = mimetype
+ """
+ Registers an image MIME type. This function should not be used
+ in application code.
+
+ :param id: An image format identifier.
+ :param mimetype: The image MIME type for this format.
+ """
+ MIME[id.upper()] = mimetype
-##
-# Registers an image save function. This function should not be
-# used in application code.
-#
-# @param id An image format identifier.
-# @param driver A function to save images in this format.
def register_save(id, driver):
- SAVE[string.upper(id)] = driver
+ """
+ Registers an image save function. This function should not be
+ used in application code.
+
+ :param id: An image format identifier.
+ :param driver: A function to save images in this format.
+ """
+ SAVE[id.upper()] = driver
-##
-# Registers an image extension. This function should not be
-# used in application code.
-#
-# @param id An image format identifier.
-# @param extension An extension used for this format.
def register_extension(id, extension):
- EXTENSION[string.lower(extension)] = string.upper(id)
+ """
+ Registers an image extension. This function should not be
+ used in application code.
+
+ :param id: An image format identifier.
+ :param extension: An extension used for this format.
+ """
+ EXTENSION[extension.lower()] = id.upper()
# --------------------------------------------------------------------
@@ -2120,8 +2180,8 @@ def register_extension(id, extension):
def _show(image, **options):
# override me, as necessary
- apply(_showxv, (image,), options)
+ _showxv(image, **options)
def _showxv(image, title=None, **options):
- import ImageShow
- apply(ImageShow.show, (image, title), options)
+ from PIL import ImageShow
+ ImageShow.show(image, title, **options)
diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py
index 82861fc7a..ba5350e02 100644
--- a/PIL/ImageChops.py
+++ b/PIL/ImageChops.py
@@ -15,287 +15,268 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from PIL import Image
-##
-# The 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 b8a6dca71..20ba6a11f 100644
--- a/PIL/ImageCms.py
+++ b/PIL/ImageCms.py
@@ -15,6 +15,8 @@
# below for the original description.
#
+from __future__ import print_function
+
DESCRIPTION = """
pyCMS
@@ -40,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
@@ -75,12 +79,13 @@ pyCMS
"""
-VERSION = "0.1.0 pil"
+VERSION = "1.0.0 pil"
# --------------------------------------------------------------------.
-import Image
-import _imagingcms
+from PIL import Image
+from PIL import _imagingcms
+from PIL._util import isStringType
core = _imagingcms
@@ -122,7 +127,7 @@ FLAGS = {
_MAX_FLAG = 0
for flag in FLAGS.values():
- if isinstance(flag, type(0)):
+ if isinstance(flag, int):
_MAX_FLAG = _MAX_FLAG | flag
# --------------------------------------------------------------------.
@@ -137,10 +142,10 @@ class ImageCmsProfile:
def __init__(self, profile):
# accepts a string (filename), a file-like object, or a low-level
# profile object
- if Image.isStringType(profile):
+ if isStringType(profile):
self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"):
- self._set(core.profile_fromstring(profile.read()))
+ self._set(core.profile_frombytes(profile.read()))
else:
self._set(profile) # assume it's already a profile
@@ -148,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
@@ -200,12 +205,12 @@ class ImageCmsTransform(Image.ImagePointHandler):
##
# (experimental) Fetches the profile for the current display device.
-# Returns None if the profile is not known.
+# @return None if the profile is not known.
def get_display_profile(handle=None):
import sys
if sys.platform == "win32":
- import ImageWin
+ from PIL import ImageWin
if isinstance(handle, ImageWin.HDC):
profile = core.get_display_profile_win32(handle, 1)
else:
@@ -232,66 +237,57 @@ class PyCMSError(Exception):
##
# (pyCMS) Applies an ICC transformation to a given image, mapping from
# inputProfile to outputProfile.
+#
+# If the input or output profiles specified are not valid filenames, a
+# PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode,
+# a PyCMSError will be raised. If an error occurs during application of
+# the profiles, a PyCMSError will be raised. If outputMode is not a mode
+# supported by the outputProfile (or by pyCMS), a PyCMSError will be
+# raised.
+#
+# This function applies an ICC transformation to im from inputProfile's
+# color space to outputProfile's color space using the specified rendering
+# intent to decide how to handle out-of-gamut colors.
+#
+# OutputMode can be used to specify that a color mode conversion is to
+# be done using these profiles, but the specified profiles must be able
+# to handle that mode. I.e., if converting im from RGB to CMYK using
+# profiles, the input profile must handle RGB data, and the output
+# profile must handle CMYK data.
+#
+# @param im An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.)
+# @param inputProfile String, as a valid filename path to the ICC input profile
+# you wish to use for this image, or a profile object
+# @param outputProfile String, as a valid filename path to the ICC output
+# profile you wish to use for this image, or a profile object
+# @param renderingIntent Integer (0-3) specifying the rendering intent you wish
+# to use for the transform
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @param outputMode A valid PIL mode for the output image (i.e. "RGB", "CMYK",
+# etc.). Note: if rendering the image "inPlace", outputMode MUST be the
+# same mode as the input, or omitted completely. If omitted, the outputMode
+# will be the same as the mode of the input image (im.mode)
+# @param inPlace Boolean (1 = True, None or 0 = False). If True, the original
+# image is modified in-place, and None is returned. If False (default), a
+# new Image object is returned with the transform applied.
+# @param flags Integer (0-...) specifying additional flags
+# @return Either None or a new PIL image object, depending on value of inPlace
+# @exception PyCMSError
def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0):
- """
- ImageCms.profileToProfile(im, inputProfile, outputProfile,
- [renderingIntent], [outputMode], [inPlace])
-
- Returns either None or a new PIL image object, depending on value of
- inPlace (see below).
-
- im = an open PIL image object (i.e. Image.new(...) or
- Image.open(...), etc.)
- inputProfile = string, as a valid filename path to the ICC input
- profile you wish to use for this image, or a profile object
- outputProfile = string, as a valid filename path to the ICC output
- profile you wish to use for this image, or a profile object
- renderingIntent = integer (0-3) specifying the rendering intent you
- wish to use for the transform
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
-
- see the pyCMS documentation for details on rendering intents and
- what they do.
- outputMode = a valid PIL mode for the output image (i.e. "RGB", "CMYK",
- etc.). Note: if rendering the image "inPlace", outputMode MUST be
- the same mode as the input, or omitted completely. If omitted, the
- outputMode will be the same as the mode of the input image (im.mode)
- inPlace = BOOL (1 = TRUE, None or 0 = FALSE). If TRUE, the original
- image is modified in-place, and None is returned. If FALSE
- (default), a new Image object is returned with the transform
- applied.
- flags = integer (0-...) specifying additional flags
-
- If the input or output profiles specified are not valid filenames, a
- PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode,
- a PyCMSError will be raised. If an error occurs during application of
- the profiles, a PyCMSError will be raised. If outputMode is not a mode
- supported by the outputProfile (or by pyCMS), a PyCMSError will be
- raised.
-
- This function applies an ICC transformation to im from inputProfile's
- color space to outputProfile's color space using the specified rendering
- intent to decide how to handle out-of-gamut colors.
-
- OutputMode can be used to specify that a color mode conversion is to
- be done using these profiles, but the specified profiles must be able
- to handle that mode. I.e., if converting im from RGB to CMYK using
- profiles, the input profile must handle RGB data, and the output
- profile must handle CMYK data.
-
- """
-
if outputMode is None:
outputMode = im.mode
- if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3):
+ if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
- if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
@@ -307,99 +303,89 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER
imOut = None
else:
imOut = transform.apply(im)
- except (IOError, TypeError, ValueError), v:
+ except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
return imOut
##
# (pyCMS) Opens an ICC profile file.
+#
+# The PyCMSProfile object can be passed back into pyCMS for use in creating
+# transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
+#
+# If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
+# will be raised.
+#
+# @param profileFilename String, as a valid filename path to the ICC profile you
+# wish to open, or a file-like object.
+# @return A CmsProfile class object.
+# @exception PyCMSError
def getOpenProfile(profileFilename):
- """
- ImageCms.getOpenProfile(profileFilename)
-
- Returns a CmsProfile class object.
-
- profileFilename = string, as a valid filename path to the ICC profile
- you wish to open, or a file-like object.
-
- The PyCMSProfile object can be passed back into pyCMS for use in creating
- transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
-
- If profileFilename is not a vaild filename for an ICC profile, a
- PyCMSError will be raised.
-
- """
-
try:
return ImageCmsProfile(profileFilename)
- except (IOError, TypeError, ValueError), v:
+ except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
##
# (pyCMS) Builds an ICC transform mapping from the inputProfile to the
# outputProfile. Use applyTransform to apply the transform to a given
# image.
+#
+# If the input or output profiles specified are not valid filenames, a
+# PyCMSError will be raised. If an error occurs during creation of the
+# transform, a PyCMSError will be raised.
+#
+# If inMode or outMode are not a mode supported by the outputProfile (or
+# by pyCMS), a PyCMSError will be raised.
+#
+# This function builds and returns an ICC transform from the inputProfile
+# to the outputProfile using the renderingIntent to determine what to do
+# with out-of-gamut colors. It will ONLY work for converting images that
+# are in inMode to images that are in outMode color format (PIL mode,
+# i.e. "RGB", "RGBA", "CMYK", etc.).
+#
+# Building the transform is a fair part of the overhead in
+# ImageCms.profileToProfile(), so if you're planning on converting multiple
+# images using the same input/output settings, this can save you time.
+# Once you have a transform object, it can be used with
+# ImageCms.applyProfile() to convert images without the need to re-compute
+# the lookup table for the transform.
+#
+# The reason pyCMS returns a class object rather than a handle directly
+# to the transform is that it needs to keep track of the PIL input/output
+# modes that the transform is meant for. These attributes are stored in
+# the "inMode" and "outMode" attributes of the object (which can be
+# manually overridden if you really want to, but I don't know of any
+# time that would be of use, or would even work).
+#
+# @param inputProfile String, as a valid filename path to the ICC input profile
+# you wish to use for this transform, or a profile object
+# @param outputProfile String, as a valid filename path to the ICC output
+# profile you wish to use for this transform, or a profile object
+# @param inMode String, as a valid PIL mode that the appropriate profile also
+# supports (i.e. "RGB", "RGBA", "CMYK", etc.)
+# @param outMode String, as a valid PIL mode that the appropriate profile also
+# supports (i.e. "RGB", "RGBA", "CMYK", etc.)
+# @param renderingIntent Integer (0-3) specifying the rendering intent you
+# wish to use for the transform
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @param flags Integer (0-...) specifying additional flags
+# @return A CmsTransform class object.
+# @exception PyCMSError
def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0):
- """
- ImageCms.buildTransform(inputProfile, outputProfile, inMode, outMode,
- [renderingIntent])
-
- Returns a CmsTransform class object.
-
- inputProfile = string, as a valid filename path to the ICC input
- profile you wish to use for this transform, or a profile object
- outputProfile = string, as a valid filename path to the ICC output
- profile you wish to use for this transform, or a profile object
- inMode = string, as a valid PIL mode that the appropriate profile also
- supports (i.e. "RGB", "RGBA", "CMYK", etc.)
- outMode = string, as a valid PIL mode that the appropriate profile also
- supports (i.e. "RGB", "RGBA", "CMYK", etc.)
- renderingIntent = integer (0-3) specifying the rendering intent you
- wish to use for the transform
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
- see the pyCMS documentation for details on rendering intents and
- what they do.
- flags = integer (0-...) specifying additional flags
-
- If the input or output profiles specified are not valid filenames, a
- PyCMSError will be raised. If an error occurs during creation of the
- transform, a PyCMSError will be raised.
-
- If inMode or outMode are not a mode supported by the outputProfile (or
- by pyCMS), a PyCMSError will be raised.
-
- This function builds and returns an ICC transform from the inputProfile
- to the outputProfile using the renderingIntent to determine what to do
- with out-of-gamut colors. It will ONLY work for converting images that
- are in inMode to images that are in outMode color format (PIL mode,
- i.e. "RGB", "RGBA", "CMYK", etc.).
-
- Building the transform is a fair part of the overhead in
- ImageCms.profileToProfile(), so if you're planning on converting multiple
- images using the same input/output settings, this can save you time.
- Once you have a transform object, it can be used with
- ImageCms.applyProfile() to convert images without the need to re-compute
- the lookup table for the transform.
-
- The reason pyCMS returns a class object rather than a handle directly
- to the transform is that it needs to keep track of the PIL input/output
- modes that the transform is meant for. These attributes are stored in
- the "inMode" and "outMode" attributes of the object (which can be
- manually overridden if you really want to, but I don't know of any
- time that would be of use, or would even work).
-
- """
-
- if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3):
+ if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
- if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
@@ -408,89 +394,85 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent
if not isinstance(outputProfile, ImageCmsProfile):
outputProfile = ImageCmsProfile(outputProfile)
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags)
- except (IOError, TypeError, ValueError), v:
+ except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
##
# (pyCMS) Builds an ICC transform mapping from the inputProfile to the
# outputProfile, but tries to simulate the result that would be
# obtained on the proofProfile device.
+#
+# If the input, output, or proof profiles specified are not valid
+# filenames, a PyCMSError will be raised.
+#
+# If an error occurs during creation of the transform, a PyCMSError will
+# be raised.
+#
+# If inMode or outMode are not a mode supported by the outputProfile
+# (or by pyCMS), a PyCMSError will be raised.
+#
+# This function builds and returns an ICC transform from the inputProfile
+# to the outputProfile, but tries to simulate the result that would be
+# obtained on the proofProfile device using renderingIntent and
+# proofRenderingIntent to determine what to do with out-of-gamut
+# colors. This is known as "soft-proofing". It will ONLY work for
+# converting images that are in inMode to images that are in outMode
+# color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
+#
+# Usage of the resulting transform object is exactly the same as with
+# ImageCms.buildTransform().
+#
+# Proof profiling is generally used when using an output device to get a
+# good idea of what the final printed/displayed image would look like on
+# the proofProfile device when it's quicker and easier to use the
+# output device for judging color. Generally, this means that the
+# output device is a monitor, or a dye-sub printer (etc.), and the simulated
+# device is something more expensive, complicated, or time consuming
+# (making it difficult to make a real print for color judgement purposes).
+#
+# Soft-proofing basically functions by adjusting the colors on the
+# output device to match the colors of the device being simulated. However,
+# when the simulated device has a much wider gamut than the output
+# device, you may obtain marginal results.
+#
+# @param inputProfile String, as a valid filename path to the ICC input profile
+# you wish to use for this transform, or a profile object
+# @param outputProfile String, as a valid filename path to the ICC output
+# (monitor, usually) profile you wish to use for this transform, or a
+# profile object
+# @param proofProfile String, as a valid filename path to the ICC proof profile
+# you wish to use for this transform, or a profile object
+# @param inMode String, as a valid PIL mode that the appropriate profile also
+# supports (i.e. "RGB", "RGBA", "CMYK", etc.)
+# @param outMode String, as a valid PIL mode that the appropriate profile also
+# supports (i.e. "RGB", "RGBA", "CMYK", etc.)
+# @param renderingIntent Integer (0-3) specifying the rendering intent you
+# wish to use for the input->proof (simulated) transform
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @param proofRenderingIntent Integer (0-3) specifying the rendering intent you
+# wish to use for proof->output transform
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @param flags Integer (0-...) specifying additional flags
+# @return A CmsTransform class object.
+# @exception PyCMSError
def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]):
- """
- ImageCms.buildProofTransform(inputProfile, outputProfile, proofProfile,
- inMode, outMode, [renderingIntent], [proofRenderingIntent])
-
- Returns a CmsTransform class object.
-
- inputProfile = string, as a valid filename path to the ICC input
- profile you wish to use for this transform, or a profile object
- outputProfile = string, as a valid filename path to the ICC output
- (monitor, usually) profile you wish to use for this transform,
- or a profile object
- proofProfile = string, as a valid filename path to the ICC proof
- profile you wish to use for this transform, or a profile object
- inMode = string, as a valid PIL mode that the appropriate profile also
- supports (i.e. "RGB", "RGBA", "CMYK", etc.)
- outMode = string, as a valid PIL mode that the appropriate profile also
- supports (i.e. "RGB", "RGBA", "CMYK", etc.)
- renderingIntent = integer (0-3) specifying the rendering intent you
- wish to use for the input->proof (simulated) transform
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
- see the pyCMS documentation for details on rendering intents and
- what they do.
- proofRenderingIntent = integer (0-3) specifying the rendering intent
- you wish to use for proof->output transform
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
- see the pyCMS documentation for details on rendering intents and
- what they do.
- flags = integer (0-...) specifying additional flags
-
- If the input, output, or proof profiles specified are not valid
- filenames, a PyCMSError will be raised.
-
- If an error occurs during creation of the transform, a PyCMSError will
- be raised.
-
- If inMode or outMode are not a mode supported by the outputProfile
- (or by pyCMS), a PyCMSError will be raised.
-
- This function builds and returns an ICC transform from the inputProfile
- to the outputProfile, but tries to simulate the result that would be
- obtained on the proofProfile device using renderingIntent and
- proofRenderingIntent to determine what to do with out-of-gamut
- colors. This is known as "soft-proofing". It will ONLY work for
- converting images that are in inMode to images that are in outMode
- color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
-
- Usage of the resulting transform object is exactly the same as with
- ImageCms.buildTransform().
-
- Proof profiling is generally used when using an output device to get a
- good idea of what the final printed/displayed image would look like on
- the proofProfile device when it's quicker and easier to use the
- output device for judging color. Generally, this means that the
- output device is a monitor, or a dye-sub printer (etc.), and the simulated
- device is something more expensive, complicated, or time consuming
- (making it difficult to make a real print for color judgement purposes).
-
- Soft-proofing basically functions by adjusting the colors on the
- output device to match the colors of the device being simulated. However,
- when the simulated device has a much wider gamut than the output
- device, you may obtain marginal results.
-
- """
-
- if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3):
+ if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3):
raise PyCMSError("renderingIntent must be an integer between 0 and 3")
- if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
try:
@@ -501,7 +483,7 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo
if not isinstance(proofProfile, ImageCmsProfile):
proofProfile = ImageCmsProfile(proofProfile)
return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags)
- except (IOError, TypeError, ValueError), v:
+ except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
buildTransformFromOpenProfiles = buildTransform
@@ -509,240 +491,349 @@ buildProofTransformFromOpenProfiles = buildProofTransform
##
# (pyCMS) Applies a transform to a given image.
+#
+# If im.mode != transform.inMode, a PyCMSError is raised.
+#
+# If inPlace == TRUE and transform.inMode != transform.outMode, a
+# PyCMSError is raised.
+#
+# If im.mode, transfer.inMode, or transfer.outMode is not supported by
+# pyCMSdll or the profiles you used for the transform, a PyCMSError is
+# raised.
+#
+# If an error occurs while the transform is being applied, a PyCMSError
+# is raised.
+#
+# This function applies a pre-calculated transform (from
+# ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an
+# image. The transform can be used for multiple images, saving
+# considerable calcuation time if doing the same conversion multiple times.
+#
+# If you want to modify im in-place instead of receiving a new image as
+# the return value, set inPlace to TRUE. This can only be done if
+# transform.inMode and transform.outMode are the same, because we can't
+# change the mode in-place (the buffer sizes for some modes are
+# different). The default behavior is to return a new Image object of
+# the same dimensions in mode transform.outMode.
+#
+# @param im A PIL Image object, and im.mode must be the same as the inMode
+# supported by the transform.
+# @param transform A valid CmsTransform class object
+# @param inPlace Bool (1 == True, 0 or None == False). If True, im is modified
+# in place and None is returned, if False, a new Image object with the
+# transform applied is returned (and im is not changed). The default is False.
+# @return Either None, or a new PIL Image object, depending on the value of inPlace
+# @exception PyCMSError
def applyTransform(im, transform, inPlace=0):
- """
- ImageCms.applyTransform(im, transform, [inPlace])
-
- Returns either None, or a new PIL Image object, depending on the value
- of inPlace (see below)
-
- im = a PIL Image object, and im.mode must be the same as the inMode
- supported by the transform.
- transform = a valid CmsTransform class object
- inPlace = BOOL (1 == TRUE, 0 or None == FALSE). If TRUE, im is
- modified in place and None is returned, if FALSE, a new Image
- object with the transform applied is returned (and im is not
- changed). The default is FALSE.
-
- If im.mode != transform.inMode, a PyCMSError is raised.
-
- If inPlace == TRUE and transform.inMode != transform.outMode, a
- PyCMSError is raised.
-
- If im.mode, transfer.inMode, or transfer.outMode is not supported by
- pyCMSdll or the profiles you used for the transform, a PyCMSError is
- raised.
-
- If an error occurs while the transform is being applied, a PyCMSError
- is raised.
-
- This function applies a pre-calculated transform (from
- ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an
- image. The transform can be used for multiple images, saving
- considerable calcuation time if doing the same conversion multiple times.
-
- If you want to modify im in-place instead of receiving a new image as
- the return value, set inPlace to TRUE. This can only be done if
- transform.inMode and transform.outMode are the same, because we can't
- change the mode in-place (the buffer sizes for some modes are
- different). The default behavior is to return a new Image object of
- the same dimensions in mode transform.outMode.
-
- """
-
try:
if inPlace:
transform.apply_in_place(im)
imOut = None
else:
imOut = transform.apply(im)
- except (TypeError, ValueError), v:
+ except (TypeError, ValueError) as v:
raise PyCMSError(v)
return imOut
##
# (pyCMS) Creates a profile.
+#
+# If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised
+#
+# If using LAB and colorTemp != a positive integer, a PyCMSError is raised.
+#
+# If an error occurs while creating the profile, a PyCMSError is raised.
+#
+# Use this function to create common profiles on-the-fly instead of
+# having to supply a profile on disk and knowing the path to it. It
+# returns a normal CmsProfile object that can be passed to
+# ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
+# to images.
+#
+# @param colorSpace String, the color space of the profile you wish to create.
+# Currently only "LAB", "XYZ", and "sRGB" are supported.
+# @param colorTemp Positive integer for the white point for the profile, in
+# degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
+# illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles,
+# and is ignored for XYZ and sRGB.
+# @return A CmsProfile class object
+# @exception PyCMSError
def createProfile(colorSpace, colorTemp=-1):
- """
- ImageCms.createProfile(colorSpace, [colorTemp])
-
- Returns a CmsProfile class object
-
- colorSpace = string, the color space of the profile you wish to create.
- Currently only "LAB", "XYZ", and "sRGB" are supported.
- colorTemp = positive integer for the white point for the profile, in
- degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for
- D50 illuminant if omitted (5000k). colorTemp is ONLY applied to
- LAB profiles, and is ignored for XYZ and sRGB.
-
- If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised
-
- If using LAB and colorTemp != a positive integer, a PyCMSError is raised.
-
- If an error occurs while creating the profile, a PyCMSError is raised.
-
- Use this function to create common profiles on-the-fly instead of
- having to supply a profile on disk and knowing the path to it. It
- returns a normal CmsProfile object that can be passed to
- ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
- to images.
-
- """
if colorSpace not in ["LAB", "XYZ", "sRGB"]:
raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace)
if colorSpace == "LAB":
- if type(colorTemp) == type(5000.0):
- colorTemp = int(colorTemp + 0.5)
- if type (colorTemp) != type (5000):
- 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)
- except (TypeError, ValueError), v:
+ except (TypeError, ValueError) as v:
raise PyCMSError(v)
##
# (pyCMS) Gets the internal product name for the given profile.
+#
+# If profile isn't a valid CmsProfile object or filename to a profile,
+# a PyCMSError is raised If an error occurs while trying to obtain the
+# name tag, a PyCMSError is raised.
+#
+# Use this function to obtain the INTERNAL name of the profile (stored
+# in an ICC tag in the profile itself), usually the one used when the
+# profile was originally created. Sometimes this tag also contains
+# additional information supplied by the creator.
+#
+# @param profile EITHER a valid CmsProfile object, OR a string of the filename
+# of an ICC profile.
+# @return A string containing the internal name of the profile as stored in an
+# ICC tag.
+# @exception PyCMSError
def getProfileName(profile):
- """
- ImageCms.getProfileName(profile)
-
- Returns a string containing the internal name of the profile as stored
- in an ICC tag.
-
- profile = EITHER a valid CmsProfile object, OR a string of the
- filename of an ICC profile.
-
- If profile isn't a valid CmsProfile object or filename to a profile,
- a PyCMSError is raised If an error occurs while trying to obtain the
- name tag, a PyCMSError is raised.
-
- Use this function to obtain the INTERNAL name of the profile (stored
- in an ICC tag in the profile itself), usually the one used when the
- profile was originally created. Sometimes this tag also contains
- additional information supplied by the creator.
-
- """
try:
# add an extra newline to preserve pyCMS compatibility
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
- return profile.profile.product_name + "\n"
- except (AttributeError, IOError, TypeError, ValueError), v:
+ # 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)
##
# (pyCMS) Gets the internal product information for the given profile.
+#
+# If profile isn't a valid CmsProfile object or filename to a profile,
+# a PyCMSError is raised.
+#
+# If an error occurs while trying to obtain the info tag, a PyCMSError
+# is raised
+#
+# Use this function to obtain the information stored in the profile's
+# info tag. This often contains details about the profile, and how it
+# was created, as supplied by the creator.
+#
+# @param profile EITHER a valid CmsProfile object, OR a string of the filename
+# of an ICC profile.
+# @return A string containing the internal profile information stored in an ICC
+# tag.
+# @exception PyCMSError
def getProfileInfo(profile):
- """
- ImageCms.getProfileInfo(profile)
-
- Returns a string containing the internal profile information stored in
- an ICC tag.
-
- profile = EITHER a valid CmsProfile object, OR a string of the
- filename of an ICC profile.
-
- If profile isn't a valid CmsProfile object or filename to a profile,
- a PyCMSError is raised.
-
- If an error occurs while trying to obtain the info tag, a PyCMSError
- is raised
-
- Use this function to obtain the information stored in the profile's
- info tag. This often contains details about the profile, and how it
- was created, as supplied by the creator.
-
- """
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
# add an extra newline to preserve pyCMS compatibility
- return profile.product_info + "\n"
- except (AttributeError, IOError, TypeError, ValueError), v:
+ # 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.
+#
+# If profile isn't a valid CmsProfile object or filename to a profile,
+# a PyCMSError is raised.
+#
+# If an error occurs while trying to obtain the default intent, a
+# PyCMSError is raised.
+#
+# Use this function to determine the default (and usually best optomized)
+# rendering intent for this profile. Most profiles support multiple
+# rendering intents, but are intended mostly for one type of conversion.
+# If you wish to use a different intent than returned, use
+# ImageCms.isIntentSupported() to verify it will work first.
+#
+# @param profile EITHER a valid CmsProfile object, OR a string of the filename
+# of an ICC profile.
+# @return Integer 0-3 specifying the default rendering intent for this profile.
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @exception PyCMSError
def getDefaultIntent(profile):
- """
- ImageCms.getDefaultIntent(profile)
-
- Returns integer 0-3 specifying the default rendering intent for this
- profile.
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
- see the pyCMS documentation for details on rendering intents and
- what they do.
-
- profile = EITHER a valid CmsProfile object, OR a string of the
- filename of an ICC profile.
-
- If profile isn't a valid CmsProfile object or filename to a profile,
- a PyCMSError is raised.
-
- If an error occurs while trying to obtain the default intent, a
- PyCMSError is raised.
-
- Use this function to determine the default (and usually best optomized)
- rendering intent for this profile. Most profiles support multiple
- rendering intents, but are intended mostly for one type of conversion.
- If you wish to use a different intent than returned, use
- ImageCms.isIntentSupported() to verify it will work first.
- """
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
return profile.profile.rendering_intent
- except (AttributeError, IOError, TypeError, ValueError), v:
+ except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
##
# (pyCMS) Checks if a given intent is supported.
+#
+# Use this function to verify that you can use your desired
+# renderingIntent with profile, and that profile can be used for the
+# input/output/proof profile as you desire.
+#
+# Some profiles are created specifically for one "direction", can cannot
+# be used for others. Some profiles can only be used for certain
+# rendering intents... so it's best to either verify this before trying
+# to create a transform with them (using this function), or catch the
+# potential PyCMSError that will occur if they don't support the modes
+# you select.
+#
+# @param profile EITHER a valid CmsProfile object, OR a string of the filename
+# of an ICC profile.
+# @param intent Integer (0-3) specifying the rendering intent you wish to use
+# with this profile
+#
+# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
+# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
+# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
+# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
+#
+# see the pyCMS documentation for details on rendering intents and what they do.
+# @param direction Integer specifing if the profile is to be used for input,
+# output, or proof
+#
+# INPUT = 0 (or use ImageCms.DIRECTION_INPUT)
+# OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)
+# PROOF = 2 (or use ImageCms.DIRECTION_PROOF)
+#
+# @return 1 if the intent/direction are supported, -1 if they are not.
+# @exception PyCMSError
def isIntentSupported(profile, intent, direction):
- """
- ImageCms.isIntentSupported(profile, intent, direction)
-
- Returns 1 if the intent/direction are supported, -1 if they are not.
-
- profile = EITHER a valid CmsProfile object, OR a string of the
- filename of an ICC profile.
- intent = integer (0-3) specifying the rendering intent you wish to use
- with this profile
- INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL)
- INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC)
- INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION)
- INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC)
- see the pyCMS documentation for details on rendering intents and
- what they do.
- direction = integer specifing if the profile is to be used for input,
- output, or proof
- INPUT = 0 (or use ImageCms.DIRECTION_INPUT)
- OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT)
- PROOF = 2 (or use ImageCms.DIRECTION_PROOF)
-
- Use this function to verify that you can use your desired
- renderingIntent with profile, and that profile can be used for the
- input/output/proof profile as you desire.
-
- Some profiles are created specifically for one "direction", can cannot
- be used for others. Some profiles can only be used for certain
- rendering intents... so it's best to either verify this before trying
- to create a transform with them (using this function), or catch the
- potential PyCMSError that will occur if they don't support the modes
- you select.
-
- """
try:
if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile)
@@ -752,7 +843,7 @@ def isIntentSupported(profile, intent, direction):
return 1
else:
return -1
- except (AttributeError, IOError, TypeError, ValueError), v:
+ except (AttributeError, IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
##
@@ -769,18 +860,17 @@ def versions():
if __name__ == "__main__":
# create a cheap manual from the __doc__ strings for the functions above
- import ImageCms
- import string
- print __doc__
+ from PIL import ImageCms
+ print(__doc__)
for f in dir(pyCMS):
- print "="*80
- print "%s" %f
+ print("="*80)
+ print("%s" %f)
try:
exec ("doc = ImageCms.%s.__doc__" %(f))
- if string.find(doc, "pyCMS") >= 0:
+ if "pyCMS" in doc:
# so we don't get the __doc__ string for imported modules
- print doc
+ print(doc)
except AttributeError:
pass
diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py
index c3cca46db..c14257151 100644
--- a/PIL/ImageColor.py
+++ b/PIL/ImageColor.py
@@ -17,16 +17,9 @@
# See the README file for information on usage and redistribution.
#
-import Image
-import re, string
+from PIL import Image
+import re
-try:
- x = int("a", 16)
-except TypeError:
- # python 1.5.2 doesn't support int(x,b)
- str2int = string.atoi
-else:
- str2int = int
##
# Convert color string to RGB tuple.
@@ -37,18 +30,26 @@ else:
# as an RGB value.
def getrgb(color):
- # FIXME: add RGBA support
+ """
+ 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:
try:
# fall back on case-insensitive lookup
- rgb = colormap[string.lower(color)]
+ rgb = colormap[color.lower()]
except KeyError:
rgb = None
# found color in cache
if rgb:
- if isinstance(rgb, type(())):
+ if isinstance(rgb, tuple):
return rgb
colormap[color] = rgb = getrgb(rgb)
return rgb
@@ -56,30 +57,30 @@ def getrgb(color):
m = re.match("#\w\w\w$", color)
if m:
return (
- str2int(color[1]*2, 16),
- str2int(color[2]*2, 16),
- str2int(color[3]*2, 16)
+ int(color[1]*2, 16),
+ int(color[2]*2, 16),
+ int(color[3]*2, 16)
)
m = re.match("#\w\w\w\w\w\w$", color)
if m:
return (
- str2int(color[1:3], 16),
- str2int(color[3:5], 16),
- str2int(color[5:7], 16)
+ int(color[1:3], 16),
+ int(color[3:5], 16),
+ int(color[5:7], 16)
)
m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
if m:
return (
- str2int(m.group(1)),
- str2int(m.group(2)),
- str2int(m.group(3))
+ int(m.group(1)),
+ int(m.group(2)),
+ int(m.group(3))
)
m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m:
return (
- int((str2int(m.group(1)) * 255) / 100.0 + 0.5),
- int((str2int(m.group(2)) * 255) / 100.0 + 0.5),
- int((str2int(m.group(3)) * 255) / 100.0 + 0.5)
+ int((int(m.group(1)) * 255) / 100.0 + 0.5),
+ int((int(m.group(2)) * 255) / 100.0 + 0.5),
+ int((int(m.group(3)) * 255) / 100.0 + 0.5)
)
m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m:
@@ -94,19 +95,39 @@ def getrgb(color):
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
+ m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
+ if m:
+ return (
+ int(m.group(1)),
+ int(m.group(2)),
+ int(m.group(3)),
+ int(m.group(4))
+ )
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":
return color
if mode == "RGBA":
- r, g, b = color
- return r, g, b, 255
+ if len(color) == 3:
+ color = (color + (255,))
+ r, g, b, a = color
+ return r, g, b, a
if Image.getmodebase(mode) == "L":
r, g, b = color
- return (r*299 + g*587 + b*114)/1000
+ return (r*299 + g*587 + b*114)//1000
return color
colormap = {
diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py
index 5217a7366..a03d26016 100644
--- a/PIL/ImageDraw.py
+++ b/PIL/ImageDraw.py
@@ -30,7 +30,10 @@
# See the README file for information on usage and redistribution.
#
-import Image, ImageColor
+import numbers
+
+from PIL import Image, ImageColor
+from PIL._util import isStringType
try:
import warnings
@@ -96,9 +99,9 @@ class ImageDraw:
"'setink' is deprecated; use keyword arguments instead",
DeprecationWarning, stacklevel=2
)
- if Image.isStringType(ink):
+ if isStringType(ink):
ink = ImageColor.getcolor(ink, self.mode)
- if self.palette and not Image.isNumberType(ink):
+ if self.palette and not isinstance(ink, numbers.Number):
ink = self.palette.getcolor(ink)
self.ink = self.draw.draw_ink(ink, self.mode)
@@ -127,7 +130,7 @@ class ImageDraw:
def getfont(self):
if not self.font:
# FIXME: should add a font repository
- import ImageFont
+ from PIL import ImageFont
self.font = ImageFont.load_default()
return self.font
@@ -139,15 +142,15 @@ class ImageDraw:
ink = self.ink
else:
if ink is not None:
- if Image.isStringType(ink):
+ if isStringType(ink):
ink = ImageColor.getcolor(ink, self.mode)
- if self.palette and not Image.isNumberType(ink):
+ if self.palette and not isinstance(ink, numbers.Number):
ink = self.palette.getcolor(ink)
ink = self.draw.draw_ink(ink, self.mode)
if fill is not None:
- if Image.isStringType(fill):
+ if isStringType(fill):
fill = ImageColor.getcolor(fill, self.mode)
- if self.palette and not Image.isNumberType(fill):
+ if self.palette and not isinstance(fill, numbers.Number):
fill = self.palette.getcolor(fill)
fill = self.draw.draw_ink(fill, self.mode)
return ink, fill
@@ -313,13 +316,11 @@ def getdraw(im=None, hints=None):
handler = None
if not hints or "nicest" in hints:
try:
- import _imagingagg
- handler = _imagingagg
+ from PIL import _imagingagg as handler
except ImportError:
pass
if handler is None:
- import ImageDraw2
- handler = ImageDraw2
+ from PIL import ImageDraw2 as handler
if im:
im = handler.Draw(im)
return im, handler
diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py
index dbf1a1f88..146cc8b16 100644
--- a/PIL/ImageDraw2.py
+++ b/PIL/ImageDraw2.py
@@ -16,7 +16,7 @@
# See the README file for information on usage and redistribution.
#
-import Image, ImageColor, ImageDraw, ImageFont, ImagePath
+from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen:
def __init__(self, color, width=1, opacity=255):
@@ -68,7 +68,8 @@ class Draw:
else:
getattr(self.draw, op)(xy, fill=fill, outline=outline)
- def settransform(self, (xoffset, yoffset)):
+ def settransform(self, offset):
+ (xoffset, yoffset) = offset
self.transform = (1, 0, xoffset, 0, 1, yoffset)
def arc(self, xy, start, end, *options):
diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py
index 86a19e650..10433343e 100644
--- a/PIL/ImageEnhance.py
+++ b/PIL/ImageEnhance.py
@@ -18,73 +18,70 @@
# See the README file for information on usage and redistribution.
#
-import Image, ImageFilter, ImageStat
+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 8a97c1b5b..a63fe757e 100644
--- a/PIL/ImageFile.py
+++ b/PIL/ImageFile.py
@@ -27,13 +27,17 @@
# See the README file for information on usage and redistribution.
#
-import Image
-import traceback, string, os
+from PIL import Image
+from PIL._util import isPath
+import traceback, os, sys
+import io
MAXBLOCK = 65536
SAFEBLOCK = 1024*1024
+LOAD_TRUNCATED_IMAGES = False
+
ERRORS = {
-1: "image buffer overrun error",
-2: "decoding error",
@@ -55,17 +59,14 @@ def raise_ioerror(error):
# --------------------------------------------------------------------
# Helpers
-def _tilesort(t1, t2):
+def _tilesort(t):
# sort on offset
- return cmp(t1[2], t2[2])
+ return t[2]
#
# --------------------------------------------------------------------
# ImageFile base class
-##
-# Base class for image file handlers.
-
class ImageFile(Image.Image):
"Base class for image file format handlers."
@@ -78,7 +79,7 @@ class ImageFile(Image.Image):
self.decoderconfig = ()
self.decodermaxblock = MAXBLOCK
- if Image.isStringType(fp):
+ if isPath(fp):
# filename
self.fp = open(fp, "rb")
self.filename = fp
@@ -89,25 +90,25 @@ class ImageFile(Image.Image):
try:
self._open()
- except IndexError, v: # end of data
+ except IndexError as v: # end of data
if Image.DEBUG > 1:
traceback.print_exc()
- raise SyntaxError, v
- except TypeError, v: # end of data (ord)
+ raise SyntaxError(v)
+ except TypeError as v: # end of data (ord)
if Image.DEBUG > 1:
traceback.print_exc()
- raise SyntaxError, v
- except KeyError, v: # unsupported mode
+ raise SyntaxError(v)
+ except KeyError as v: # unsupported mode
if Image.DEBUG > 1:
traceback.print_exc()
- raise SyntaxError, v
- except EOFError, v: # got header but not the first frame
+ raise SyntaxError(v)
+ except EOFError as v: # got header but not the first frame
if Image.DEBUG > 1:
traceback.print_exc()
- raise SyntaxError, v
+ raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
- raise SyntaxError, "not identified by this driver"
+ raise SyntaxError("not identified by this driver")
def draft(self, mode, size):
"Set draft mode"
@@ -135,7 +136,8 @@ class ImageFile(Image.Image):
readonly = 0
- if self.filename and len(self.tile) == 1:
+ 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.
# try memory mapping
d, e, o, a = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
@@ -177,13 +179,13 @@ class ImageFile(Image.Image):
if not self.map:
# sort tiles in file order
- self.tile.sort(_tilesort)
+ self.tile.sort(key=_tilesort)
try:
# FIXME: This is a hack to handle TIFF's JpegTables tag.
prefix = self.tile_prefix
except AttributeError:
- prefix = ""
+ prefix = b""
for d, e, o, a in self.tile:
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
@@ -194,11 +196,27 @@ class ImageFile(Image.Image):
continue
b = prefix
t = len(b)
- while 1:
- s = read(self.decodermaxblock)
- if not s:
+ while True:
+ try:
+ s = read(self.decodermaxblock)
+ except IndexError as ie: # truncated png/gif
+ if LOAD_TRUNCATED_IMAGES:
+ break
+ else:
+ raise IndexError(ie)
+
+ if not s: # truncated jpeg
self.tile = []
- raise IOError("image file is truncated (%d bytes not processed)" % len(b))
+
+ # JpegDecode needs to clean things up here either way
+ # If we don't destroy the decompressor, we have a memory leak.
+ d.cleanup()
+
+ if LOAD_TRUNCATED_IMAGES:
+ break
+ else:
+ raise IOError("image file is truncated (%d bytes not processed)" % len(b))
+
b = b + s
n, e = d.decode(b)
if n < 0:
@@ -211,7 +229,8 @@ class ImageFile(Image.Image):
self.fp = None # might be shared
- if not self.map and e < 0:
+ if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0:
+ # still raised if decoder fails to return anything
raise_ioerror(e)
# post processing
@@ -245,14 +264,14 @@ class ImageFile(Image.Image):
# def load_read(self, bytes):
# pass
-##
-# Base class for stub image loaders.
-#
-# A stub loader is an image loader that can identify files of a
-# certain format, but relies on external code to load the file.
class StubImageFile(ImageFile):
- "Base class for stub image loaders."
+ """
+ Base class for stub image loaders.
+
+ A stub loader is an image loader that can identify files of a
+ certain format, but relies on external code to load the file.
+ """
def _open(self):
raise NotImplementedError(
@@ -269,87 +288,41 @@ class StubImageFile(ImageFile):
self.__class__ = image.__class__
self.__dict__ = image.__dict__
- ##
- # (Hook) Find actual image loader.
-
def _load(self):
+ "(Hook) Find actual image loader."
raise NotImplementedError(
"StubImageFile subclass must implement _load"
)
-##
-# (Internal) Support class for the Parser file.
-
-class _ParserFile:
- # parser support class.
-
- def __init__(self, data):
- self.data = data
- self.offset = 0
-
- def close(self):
- self.data = self.offset = None
-
- def tell(self):
- return self.offset
-
- def seek(self, offset, whence=0):
- if whence == 0:
- self.offset = offset
- elif whence == 1:
- self.offset = self.offset + offset
- else:
- # force error in Image.open
- raise IOError("illegal argument to seek")
-
- def read(self, bytes=0):
- pos = self.offset
- if bytes:
- data = self.data[pos:pos+bytes]
- else:
- data = self.data[pos:]
- self.offset = pos + len(data)
- return data
-
- def readline(self):
- # FIXME: this is slow!
- s = ""
- while 1:
- c = self.read(1)
- if not c:
- break
- s = s + c
- if c == "\n":
- break
- return s
-
-##
-# Incremental image parser. This class implements the standard
-# feed/close consumer interface.
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
data = None
decoder = None
finished = 0
- ##
- # (Consumer) Reset the parser. Note that you can only call this
- # method immediately after you've created a parser; parser
- # instances cannot be reused.
-
def reset(self):
+ """
+ (Consumer) Reset the parser. Note that you can only call this
+ method immediately after you've created a parser; parser
+ instances cannot be reused.
+ """
assert self.data is None, "cannot reuse parsers"
- ##
- # (Consumer) Feed data to the parser.
- #
- # @param data A string buffer.
- # @exception IOError If the parser failed to parse the image file.
-
def feed(self, data):
+ """
+ (Consumer) Feed data to the parser.
+
+ :param data: A string buffer.
+ :exception IOError: If the parser failed to parse the image file.
+ """
# collect data
if self.finished:
@@ -398,11 +371,12 @@ class Parser:
# attempt to open this file
try:
try:
- fp = _ParserFile(self.data)
+ fp = io.BytesIO(self.data)
im = Image.open(fp)
finally:
fp.close() # explicitly close the virtual file
except IOError:
+ # traceback.print_exc()
pass # not enough data
else:
flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
@@ -427,17 +401,19 @@ class Parser:
self.image = im
- ##
- # (Consumer) Close the stream.
- #
- # @return An image object.
- # @exception IOError If the parser failed to parse the image file.
-
def close(self):
+ """
+ (Consumer) Close the stream.
+
+ :returns: An image object.
+ :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:
# get rid of what's left in the buffers
- self.feed("")
+ self.feed(b"")
self.data = self.decoder = None
if not self.finished:
raise IOError("image was incomplete")
@@ -447,7 +423,7 @@ class Parser:
# incremental parsing not possible; reopen the file
# not that we have all data
try:
- fp = _ParserFile(self.data)
+ fp = io.BytesIO(self.data)
self.image = Image.open(fp)
finally:
self.image.load()
@@ -456,33 +432,35 @@ class Parser:
# --------------------------------------------------------------------
-##
-# (Helper) Save image body to file.
-#
-# @param im Image object.
-# @param fp File object.
-# @param tile Tile list.
+def _save(im, fp, tile, bufsize=0):
+ """Helper to save image based on tile list
-def _save(im, fp, tile):
- "Helper to save image based on tile list"
+ :param im: Image object.
+ :param fp: File object.
+ :param tile: Tile list.
+ :param bufsize: Optional buffer size
+ """
im.load()
if not hasattr(im, "encoderconfig"):
im.encoderconfig = ()
- tile.sort(_tilesort)
+ tile.sort(key=_tilesort)
# FIXME: make MAXBLOCK a configuration parameter
- bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c
+ # It would be great if we could have the encoder specifiy what it needs
+ # But, it would need at least the image size in most cases. RawEncode is
+ # a tricky case.
+ bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c
try:
fh = fp.fileno()
fp.flush()
- except AttributeError:
+ except (AttributeError, io.UnsupportedOperation):
# compress to Python file-compatible object
for e, b, o, a in tile:
e = Image._getencoder(im.mode, e, a, im.encoderconfig)
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
- while 1:
+ while True:
l, s, d = e.encode(bufsize)
fp.write(d)
if s:
@@ -504,18 +482,18 @@ def _save(im, fp, tile):
except: pass
-##
-# Reads large blocks in a safe way. Unlike fp.read(n), this function
-# doesn't trust the user. If the requested size is larger than
-# SAFEBLOCK, the file is read block by block.
-#
-# @param fp File handle. Must implement a read method.
-# @param size Number of bytes to read.
-# @return A string containing up to size bytes of data.
-
def _safe_read(fp, size):
+ """
+ Reads large blocks in a safe way. Unlike fp.read(n), this function
+ doesn't trust the user. If the requested size is larger than
+ SAFEBLOCK, the file is read block by block.
+
+ :param fp: File handle. Must implement a read method.
+ :param size: Number of bytes to read.
+ :returns: A string containing up to size bytes of data.
+ """
if size <= 0:
- return ""
+ return b""
if size <= SAFEBLOCK:
return fp.read(size)
data = []
@@ -525,4 +503,4 @@ def _safe_read(fp, size):
break
data.append(block)
size = size - len(block)
- return string.join(data, "")
+ return b"".join(data)
diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py
index c12a25738..e57d3f43e 100644
--- a/PIL/ImageFileIO.py
+++ b/PIL/ImageFileIO.py
@@ -11,29 +11,30 @@
#
# See the README file for information on usage and redistribution.
#
+"""
+The **ImageFileIO** module can be used to read an image from a
+socket, or any other stream device.
-from StringIO import StringIO
+Deprecated. New code should use the :class:`PIL.ImageFile.Parser`
+class in the :mod:`PIL.ImageFile` module instead.
-##
-# The ImageFileIO module can be used to read an image from a
-# socket, or any other stream device.
-#
-# This module is deprecated. New code should use the Parser
-# class in the ImageFile module instead.
-#
-# @see ImageFile#Parser
+.. seealso:: modules :class:`PIL.ImageFile.Parser`
+"""
-class ImageFileIO(StringIO):
+from io import BytesIO
- ##
- # Adds buffering to a stream file object, in order to
- # provide seek and tell methods required
- # by the Image.open method. The stream object must
- # implement read and close methods.
- #
- # @param fp Stream file handle.
- # @see Image#open
+class ImageFileIO(BytesIO):
def __init__(self, fp):
+ """
+ Adds buffering to a stream file object, in order to
+ provide **seek** and **tell** methods required
+ by the :func:`PIL.Image.Image.open` method. The stream object must
+ implement **read** and **close** methods.
+
+ :param fp: Stream file handle.
+
+ .. seealso:: modules :func:`PIL.Image.open`
+ """
data = fp.read()
- StringIO.__init__(self, data)
+ BytesIO.__init__(self, data)
diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py
index 918bab177..ac8fe9f19 100644
--- a/PIL/ImageFilter.py
+++ b/PIL/ImageFilter.py
@@ -15,31 +15,30 @@
# See the README file for information on usage and redistribution.
#
-class Filter:
+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:
@@ -52,26 +51,25 @@ class Kernel(Filter):
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
- return apply(image.filter, self.filterargs)
+ 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
@@ -80,102 +78,102 @@ class RankFilter(Filter):
def filter(self, image):
if image.mode == "P":
raise ValueError("cannot filter palette images")
- image = image.expand(self.size/2, self.size/2)
+ 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
+ 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 = 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 = 2
+ 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"
@@ -187,8 +185,6 @@ class BLUR(BuiltinFilter):
1, 1, 1, 1, 1
)
-##
-# Simple contour filter.
class CONTOUR(BuiltinFilter):
name = "Contour"
@@ -198,8 +194,6 @@ class CONTOUR(BuiltinFilter):
-1, -1, -1
)
-##
-# Simple detail filter.
class DETAIL(BuiltinFilter):
name = "Detail"
@@ -209,8 +203,6 @@ class DETAIL(BuiltinFilter):
0, -1, 0
)
-##
-# Simple edge enhancement filter.
class EDGE_ENHANCE(BuiltinFilter):
name = "Edge-enhance"
@@ -220,8 +212,6 @@ class EDGE_ENHANCE(BuiltinFilter):
-1, -1, -1
)
-##
-# Simple stronger edge enhancement filter.
class EDGE_ENHANCE_MORE(BuiltinFilter):
name = "Edge-enhance More"
@@ -231,8 +221,6 @@ class EDGE_ENHANCE_MORE(BuiltinFilter):
-1, -1, -1
)
-##
-# Simple embossing filter.
class EMBOSS(BuiltinFilter):
name = "Emboss"
@@ -242,8 +230,6 @@ class EMBOSS(BuiltinFilter):
0, 0, 0
)
-##
-# Simple edge-finding filter.
class FIND_EDGES(BuiltinFilter):
name = "Find Edges"
@@ -253,8 +239,6 @@ class FIND_EDGES(BuiltinFilter):
-1, -1, -1
)
-##
-# Simple smoothing filter.
class SMOOTH(BuiltinFilter):
name = "Smooth"
@@ -264,8 +248,6 @@ class SMOOTH(BuiltinFilter):
1, 1, 1
)
-##
-# Simple stronger smoothing filter.
class SMOOTH_MORE(BuiltinFilter):
name = "Smooth More"
@@ -277,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 3ea2f2f8e..9366937dd 100644
--- a/PIL/ImageFont.py
+++ b/PIL/ImageFont.py
@@ -25,8 +25,16 @@
# See the README file for information on usage and redistribution.
#
-import Image
-import os, string, sys
+from __future__ import print_function
+
+from PIL import Image
+from PIL._util import isDirectory, isPath
+import os, sys
+
+try:
+ import warnings
+except ImportError:
+ warnings = None
class _imagingft_not_installed:
# module placeholder
@@ -34,9 +42,7 @@ class _imagingft_not_installed:
raise ImportError("The _imagingft C module is not installed")
try:
- import _imagingft
- core = _imagingft
- del _imagingft
+ from PIL import _imagingft as core
except ImportError:
core = _imagingft_not_installed()
@@ -55,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"
@@ -97,13 +88,13 @@ class ImageFont:
def _load_pilfont_data(self, file, image):
# read PILfont header
- if file.readline() != "PILfont\n":
+ if file.readline() != b"PILfont\n":
raise SyntaxError("Not a PILfont file")
- d = string.split(file.readline(), ";")
+ d = file.readline().split(b";")
self.info = [] # FIXME: should be a dictionary
while True:
s = file.readline()
- if not s or s == "DATA\n":
+ if not s or s == b"DATA\n":
break
self.info.append(s)
@@ -129,9 +120,18 @@ class ImageFont:
class FreeTypeFont:
"FreeType font wrapper (requires _imagingft service)"
- def __init__(self, file, size, index=0, encoding=""):
+ def __init__(self, font=None, size=10, index=0, encoding="", file=None):
# FIXME: use service provider instead
- self.font = core.getfont(file, size, index, encoding)
+ if file:
+ if warnings:
+ warnings.warn('file parameter deprecated, please use font parameter instead.', DeprecationWarning)
+ font = file
+
+ if isPath(font):
+ self.font = core.getfont(font, size, index, encoding)
+ else:
+ self.font_bytes = font.read()
+ self.font = core.getfont("", size, index, encoding, self.font_bytes)
def getname(self):
return self.font.family, self.font.style
@@ -142,6 +142,9 @@ class FreeTypeFont:
def getsize(self, text):
return self.font.getsize(text)[0]
+ def getoffset(self, text):
+ return self.font.getsize(text)[1]
+
def getmask(self, text, mode=""):
return self.getmask2(text, mode)[0]
@@ -179,43 +182,50 @@ 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(filename, size, index=0, encoding=""):
- "Load a truetype font file."
+def truetype(font=None, size=10, index=0, encoding="", filename=None):
+ """
+ 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:
+ warnings.warn('filename parameter deprecated, please use font parameter instead.', DeprecationWarning)
+ font = filename
+
try:
- return FreeTypeFont(filename, size, index, encoding)
+ return FreeTypeFont(font, size, index, encoding)
except IOError:
if sys.platform == "win32":
# check the windows font repository
@@ -223,42 +233,47 @@ def truetype(filename, size, index=0, encoding=""):
# 1.5.2's os.environ.get()
windir = os.environ.get("WINDIR")
if windir:
- filename = os.path.join(windir, "fonts", filename)
+ filename = os.path.join(windir, "fonts", font)
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 Image.isDirectory(dir):
+ if isDirectory(dir):
+ if not isinstance(filename, str):
+ if bytes is str:
+ filename = filename.encode("utf-8")
+ else:
+ filename = filename.decode("utf-8")
try:
return load(os.path.join(dir, filename))
except IOError:
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."
- from StringIO import StringIO
+ """Load a "better than nothing" default font.
+
+ .. versionadded:: 1.1.4
+
+ :return: A font object.
+ """
+ from io import BytesIO
import base64
f = ImageFont()
f._load_pilfont_data(
# courB08
- StringIO(base64.decodestring('''
+ BytesIO(base64.decodestring(b'''
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
@@ -350,7 +365,7 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA
pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
+QAGAAIAzgAKANUAEw==
-''')), Image.open(StringIO(base64.decodestring('''
+''')), Image.open(BytesIO(base64.decodestring(b'''
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
@@ -377,14 +392,15 @@ w7IkEbzhVQAAAABJRU5ErkJggg==
'''))))
return f
+
if __name__ == "__main__":
# create font data chunk for embedding
import base64, os, sys
font = "../Images/courB08"
- print " f._load_pilfont_data("
- print " # %s" % os.path.basename(font)
- print " StringIO(base64.decodestring('''"
+ print(" f._load_pilfont_data(")
+ print(" # %s" % os.path.basename(font))
+ print(" BytesIO(base64.decodestring(b'''")
base64.encode(open(font + ".pil", "rb"), sys.stdout)
- print "''')), Image.open(StringIO(base64.decodestring('''"
+ print("''')), Image.open(BytesIO(base64.decodestring(b'''")
base64.encode(open(font + ".pbm", "rb"), sys.stdout)
- print "'''))))"
+ print("'''))))")
diff --git a/PIL/ImageGL.py b/PIL/ImageGL.py
deleted file mode 100644
index 5c58b6ca9..000000000
--- a/PIL/ImageGL.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# The Python Imaging Library.
-# $Id$
-#
-# OpenGL pixmap/texture interface (requires imToolkit OpenGL extensions)
-#
-# History:
-# 2003-09-13 fl Added
-#
-# Copyright (c) Secret Labs AB 2003.
-#
-# See the README file for information on usage and redistribution.
-#
-
-##
-# OpenGL pixmap/texture interface (requires imToolkit OpenGL
-# extensions.)
-##
-
-import _imaginggl
-
-##
-# Texture factory.
-
-class TextureFactory:
- pass # overwritten by the _imaginggl module
-
-from _imaginggl import *
diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py
index 9bcd804d3..9bb190934 100644
--- a/PIL/ImageGrab.py
+++ b/PIL/ImageGrab.py
@@ -15,16 +15,8 @@
# See the README file for information on usage and redistribution.
#
-import Image
+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,18 +26,10 @@ 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()
- im = Image.fromstring(
+ im = Image.frombytes(
"RGB", size, data,
# RGB, 32-bit line padding, origo in lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1
@@ -54,18 +38,12 @@ 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
data = Image.core.grabclipboard(debug)
- if Image.isStringType(data):
- import BmpImagePlugin, StringIO
- return BmpImagePlugin.DibImageFile(StringIO.StringIO(data))
+ if isinstance(data, bytes):
+ from PIL import BmpImagePlugin
+ import io
+ return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py
index 56c42a45a..b2355ed1d 100644
--- a/PIL/ImageMath.py
+++ b/PIL/ImageMath.py
@@ -15,13 +15,20 @@
# See the README file for information on usage and redistribution.
#
-import Image
-import _imagingmath
+from PIL import Image
+from PIL import _imagingmath
+import sys
+
+try:
+ import builtins
+except ImportError:
+ import __builtin__
+ builtins = __builtin__
VERBOSE = 0
def _isconstant(v):
- return isinstance(v, type(0)) or isinstance(v, type(0.0))
+ return isinstance(v, int) or isinstance(v, float)
class _Operand:
# wraps an image operand, providing standard operators
@@ -38,7 +45,7 @@ class _Operand:
elif im1.im.mode in ("I", "F"):
return im1.im
else:
- raise ValueError, "unsupported mode: %s" % im1.im.mode
+ raise ValueError("unsupported mode: %s" % im1.im.mode)
else:
# argument was a constant
if _isconstant(im1) and self.im.mode in ("1", "L", "I"):
@@ -55,7 +62,7 @@ class _Operand:
try:
op = getattr(_imagingmath, op+"_"+im1.mode)
except AttributeError:
- raise TypeError, "bad operand type for '%s'" % op
+ raise TypeError("bad operand type for '%s'" % op)
_imagingmath.unop(op, out.im.id, im1.im.id)
else:
# binary operation
@@ -65,7 +72,7 @@ class _Operand:
if im1.mode != "F": im1 = im1.convert("F")
if im2.mode != "F": im2 = im2.convert("F")
if im1.mode != im2.mode:
- raise ValueError, "mode mismatch"
+ raise ValueError("mode mismatch")
if im1.size != im2.size:
# crop both arguments to a common size
size = (min(im1.size[0], im2.size[0]),
@@ -79,14 +86,20 @@ class _Operand:
try:
op = getattr(_imagingmath, op+"_"+im1.mode)
except AttributeError:
- raise TypeError, "bad operand type for '%s'" % op
+ raise TypeError("bad operand type for '%s'" % op)
_imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id)
return _Operand(out)
# unary operators
- def __nonzero__(self):
+ def __bool__(self):
# an image is "true" if it contains at least one non-zero pixel
return self.im.getbbox() is not None
+
+ if bytes is str:
+ # Provide __nonzero__ for pre-Py3k
+ __nonzero__ = __bool__
+ del __bool__
+
def __abs__(self):
return self.apply("abs", self)
def __pos__(self):
@@ -107,9 +120,9 @@ class _Operand:
return self.apply("mul", self, other)
def __rmul__(self, other):
return self.apply("mul", other, self)
- def __div__(self, other):
+ def __truediv__(self, other):
return self.apply("div", self, other)
- def __rdiv__(self, other):
+ def __rtruediv__(self, other):
return self.apply("div", other, self)
def __mod__(self, other):
return self.apply("mod", self, other)
@@ -120,6 +133,13 @@ class _Operand:
def __rpow__(self, other):
return self.apply("pow", other, self)
+ if bytes is str:
+ # Provide __div__ and __rdiv__ for pre-Py3k
+ __div__ = __truediv__
+ __rdiv__ = __rtruediv__
+ del __truediv__
+ del __rtruediv__
+
# bitwise
def __invert__(self):
return self.apply("invert", self)
@@ -175,32 +195,33 @@ def imagemath_convert(self, mode):
return _Operand(self.im.convert(mode))
ops = {}
-for k, v in globals().items():
+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()
args.update(_dict)
args.update(kw)
- for k, v in args.items():
+ for k, v in list(args.items()):
if hasattr(v, "im"):
args[k] = _Operand(v)
- import __builtin__
- out =__builtin__.eval(expression, args)
+ out = builtins.eval(expression, args)
try:
return out.im
except AttributeError:
diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py
index 1d5df1c6d..c3931b525 100644
--- a/PIL/ImageMode.py
+++ b/PIL/ImageMode.py
@@ -36,7 +36,7 @@ class ModeDescriptor:
def getmode(mode):
if not _modes:
# initialize mode cache
- import Image
+ from PIL import Image
# core modes
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
_modes[m] = ModeDescriptor(m, bands, basemode, basetype)
diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py
index b51d78e25..0d22f8c64 100644
--- a/PIL/ImageOps.py
+++ b/PIL/ImageOps.py
@@ -17,22 +17,16 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from PIL import Image
+from PIL._util import isStringType
import operator
-
-##
-# (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
-##
+from functools import reduce
#
# helpers
def _border(border):
- if type(border) is type(()):
+ if isinstance(border, tuple):
if len(border) == 2:
left, top = right, bottom = border
elif len(border) == 4:
@@ -42,8 +36,8 @@ def _border(border):
return left, top, right, bottom
def _color(color, mode):
- if Image.isStringType(color):
- import ImageColor
+ if isStringType(color):
+ from PIL import ImageColor
color = ImageColor.getcolor(color, mode)
return color
@@ -56,25 +50,25 @@ def _lut(image, lut):
lut = lut + lut + lut
return image.point(lut)
else:
- raise IOError, "not supported for this image mode"
+ raise IOError("not supported for this image mode")
#
# 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):
@@ -94,7 +88,7 @@ def autocontrast(image, cutoff=0, ignore=None):
for ix in range(256):
n = n + h[ix]
# remove cutoff% pixels from the low end
- cut = n * cutoff / 100
+ cut = n * cutoff // 100
for lo in range(256):
if cut > h[lo]:
cut = cut - h[lo]
@@ -105,7 +99,7 @@ def autocontrast(image, cutoff=0, ignore=None):
if cut <= 0:
break
# remove cutoff% samples from the hi end
- cut = n * cutoff / 100
+ cut = n * cutoff // 100
for hi in range(255, -1, -1):
if cut > h[hi]:
cut = cut - h[hi]
@@ -124,7 +118,7 @@ def autocontrast(image, cutoff=0, ignore=None):
break
if hi <= lo:
# don't bother
- lut.extend(range(256))
+ lut.extend(list(range(256)))
else:
scale = 255.0 / (hi - lo)
offset = -lo * scale
@@ -137,101 +131,103 @@ 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")
red = []; green = []; blue = []
for i in range(256):
- red.append(black[0]+i*(white[0]-black[0])/255)
- green.append(black[1]+i*(white[1]-black[1])/255)
- blue.append(black[2]+i*(white[2]-black[2])/255)
+ red.append(black[0]+i*(white[0]-black[0])//255)
+ green.append(black[1]+i*(white[1]-black[1])//255)
+ blue.append(black[2]+i*(white[2]-black[2])//255)
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)
lut = []
for b in range(0, len(h), 256):
- histo = filter(None, h[b:b+256])
+ histo = [_f for _f in h[b:b+256] if _f]
if len(histo) <= 1:
- lut.extend(range(256))
+ lut.extend(list(range(256)))
else:
- step = (reduce(operator.add, histo) - histo[-1]) / 255
+ step = (reduce(operator.add, histo) - histo[-1]) // 255
if not step:
- lut.extend(range(256))
+ lut.extend(list(range(256)))
else:
- n = step / 2
+ n = step // 2
for i in range(256):
- lut.append(n / step)
+ lut.append(n // step)
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
@@ -240,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
@@ -274,7 +269,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
# http://www.cazabon.com
# ensure inputs are valid
- if type(centering) != type([]):
+ if not isinstance(centering, list):
centering = [centering[0], centering[1]]
if centering[0] > 1.0 or centering[0] < 0.0:
@@ -294,10 +289,12 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
int((float(bleed) * float(image.size[1])) + 0.5)
)
- liveArea = (
- bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1,
- image.size[1] - bleedPixels[1] - 1
- )
+ liveArea = (0, 0, image.size[0], image.size[1])
+ if bleed > 0.0:
+ liveArea = (
+ bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1,
+ image.size[1] - bleedPixels[1] - 1
+ )
liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1])
@@ -332,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 6efee2998..61affdb19 100644
--- a/PIL/ImagePalette.py
+++ b/PIL/ImagePalette.py
@@ -17,49 +17,64 @@
#
import array
-import Image, ImageColor
+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
self.rawmode = None # if set, palette contains raw data
- self.palette = palette or range(256)*len(self.mode)
+ self.palette = palette or list(range(256))*len(self.mode)
self.colors = {}
self.dirty = None
if len(self.mode)*256 != len(self.palette):
- raise ValueError, "wrong palette size"
+ 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.tostring()
+ return self.mode + ";L", self.tobytes()
- def tostring(self):
- # experimental: convert palette to string
+ def tobytes(self):
+ """Convert palette to bytes.
+
+ .. warning:: This method is experimental.
+ """
if self.rawmode:
raise ValueError("palette contains raw palette data")
- if Image.isStringType(self.palette):
+ if isinstance(self.palette, bytes):
return self.palette
- return array.array("B", self.palette).tostring()
+ arr = array.array("B", self.palette)
+ if hasattr(arr, 'tobytes'):
+ #py3k has a tobytes, tostring is deprecated.
+ return arr.tobytes()
+ return arr.tostring()
+
+ # Declare tostring as an alias for tobytes
+ 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 Image.isTupleType(color):
+ if isinstance(color, tuple):
try:
return self.colors[color]
except KeyError:
# allocate new color slot
- if Image.isStringType(self.palette):
- self.palette = map(int, self.palette)
+ if isinstance(self.palette, bytes):
+ self.palette = [int(x) for x in self.palette]
index = len(self.colors)
if index >= 256:
raise ValueError("cannot allocate more than 256 colors")
@@ -73,10 +88,13 @@ 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 type(fp) == type(""):
+ if isinstance(fp, str):
fp = open(fp, "w")
fp.write("# Palette\n")
fp.write("# Mode: %s\n" % self.mode)
@@ -104,7 +122,7 @@ def _make_linear_lut(black, white):
lut = []
if black == 0:
for i in range(256):
- lut.append(white*i/255)
+ lut.append(white*i//255)
else:
raise NotImplementedError # FIXME
return lut
@@ -119,7 +137,7 @@ def new(mode, data):
return Image.core.new_palette(mode, data)
def negative(mode="RGB"):
- palette = range(256)
+ palette = list(range(256))
palette.reverse()
return ImagePalette(mode, palette * len(mode))
@@ -138,7 +156,7 @@ def sepia(white="#fff0c0"):
return ImagePalette("RGB", r + g + b)
def wedge(mode="RGB"):
- return ImagePalette(mode, range(256) * len(mode))
+ return ImagePalette(mode, list(range(256)) * len(mode))
def load(filename):
@@ -150,35 +168,38 @@ def load(filename):
if not lut:
try:
- import GimpPaletteFile
+ from PIL import GimpPaletteFile
fp.seek(0)
p = GimpPaletteFile.GimpPaletteFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
+ #import traceback
+ #traceback.print_exc()
pass
if not lut:
try:
- import GimpGradientFile
+ from PIL import GimpGradientFile
fp.seek(0)
p = GimpGradientFile.GimpGradientFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
+ #import traceback
+ #traceback.print_exc()
pass
if not lut:
try:
- import PaletteFile
+ from PIL import PaletteFile
fp.seek(0)
p = PaletteFile.PaletteFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
+ import traceback
+ traceback.print_exc()
pass
if not lut:
- raise IOError, "cannot load palette"
+ 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 721fd9449..656d5ce61 100644
--- a/PIL/ImagePath.py
+++ b/PIL/ImagePath.py
@@ -14,19 +14,14 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from PIL import Image
+
+
+# the Python class below is overridden by the C implementation.
-##
-# Path wrapper.
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/ImageQt.py b/PIL/ImageQt.py
index 50ee07d6c..ca8b14b5c 100644
--- a/PIL/ImageQt.py
+++ b/PIL/ImageQt.py
@@ -8,6 +8,7 @@
# 2006-06-03 fl: created
# 2006-06-04 fl: inherit from QImage instead of wrapping it
# 2006-06-05 fl: removed toimage helper; move string support to ImageQt
+# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com)
#
# Copyright (c) 2006 by Secret Labs AB
# Copyright (c) 2006 by Fredrik Lundh
@@ -15,17 +16,21 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from PIL import Image
+from PIL._util import isPath
-from PyQt4.QtGui import QImage, qRgb
+try:
+ from PyQt5.QtGui import QImage, qRgba
+except:
+ from PyQt4.QtGui import QImage, qRgba
##
# (Internal) Turns an RGB color into a Qt compatible color integer.
-def rgb(r, g, b):
+def rgb(r, g, b, a=255):
# use qRgb to pack the colors, and then turn the resulting long
# into a negative integer with the same bitpattern.
- return (qRgb(r, g, b) & 0xffffff) - 0x1000000
+ return (qRgba(r, g, b, a) & 0xffffffff)
##
# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage
@@ -45,7 +50,7 @@ class ImageQt(QImage):
if hasattr(im, "toUtf8"):
# FIXME - is this really the best way to do this?
im = unicode(im.toUtf8(), "utf-8")
- if Image.isStringType(im):
+ if isPath(im):
im = Image.open(im)
if im.mode == "1":
@@ -62,11 +67,11 @@ class ImageQt(QImage):
for i in range(0, len(palette), 3):
colortable.append(rgb(*palette[i:i+3]))
elif im.mode == "RGB":
- data = im.tostring("raw", "BGRX")
+ data = im.tobytes("raw", "BGRX")
format = QImage.Format_RGB32
elif im.mode == "RGBA":
try:
- data = im.tostring("raw", "BGRA")
+ data = im.tobytes("raw", "BGRA")
except SystemError:
# workaround for earlier versions
r, g, b, a = im.split()
@@ -76,7 +81,7 @@ class ImageQt(QImage):
raise ValueError("unsupported image mode %r" % im.mode)
# must keep a reference, or Qt will crash!
- self.__data = data or im.tostring()
+ self.__data = data or im.tobytes()
QImage.__init__(self, self.__data, im.size[0], im.size[1], format)
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/ImageShow.py b/PIL/ImageShow.py
index 05270f440..e81866bac 100644
--- a/PIL/ImageShow.py
+++ b/PIL/ImageShow.py
@@ -12,9 +12,16 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from __future__ import print_function
+
+from PIL import Image
import os, sys
+if(sys.version_info >= (3, 3)):
+ from shlex import quote
+else:
+ from pipes import quote
+
_viewers = []
def register(viewer, order=1):
@@ -63,7 +70,7 @@ class Viewer:
if base != image.mode and image.mode != "1":
image = image.convert(base)
- self.show_image(image, **options)
+ return self.show_image(image, **options)
# hook methods
@@ -96,7 +103,9 @@ if sys.platform == "win32":
class WindowsViewer(Viewer):
format = "BMP"
def get_command(self, file, **options):
- return "start /wait %s && del /f %s" % (file, file)
+ return ('start "Pillow" /WAIT "%s" '
+ '&& ping -n 2 127.0.0.1 >NUL '
+ '&& del /f "%s"' % (file, file))
register(WindowsViewer)
@@ -108,7 +117,7 @@ elif sys.platform == "darwin":
# on darwin open returns immediately resulting in the temp
# file removal while app is opening
command = "open -a /Applications/Preview.app"
- command = "(%s %s; sleep 20; rm -f %s)&" % (command, file, file)
+ command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file))
return command
register(MacViewer)
@@ -131,7 +140,7 @@ else:
class UnixViewer(Viewer):
def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options)
- command = "(%s %s; rm -f %s)&" % (command, file, file)
+ command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file))
os.system(command)
return 1
@@ -151,8 +160,7 @@ else:
# imagemagick's display command instead.
command = executable = "xv"
if title:
- # FIXME: do full escaping
- command = command + " -name \"%s\"" % title
+ command = command + " -name %s" % quote(title)
return command, executable
if which("xv"):
@@ -160,4 +168,4 @@ else:
if __name__ == "__main__":
# usage: python ImageShow.py imagefile [title]
- print show(Image.open(sys.argv[1]), *sys.argv[2:])
+ print(show(Image.open(sys.argv[1]), *sys.argv[2:]))
diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py
index 9ebdab030..ef63b7857 100644
--- a/PIL/ImageStat.py
+++ b/PIL/ImageStat.py
@@ -21,28 +21,12 @@
# See the README file for information on usage and redistribution.
#
-import Image
+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:
@@ -52,14 +36,14 @@ class Stat:
self.h = image_or_list.histogram()
except AttributeError:
self.h = image_or_list # assume it to be a histogram list
- if type(self.h) != type([]):
- raise TypeError, "first argument must be image or list"
- self.bands = range(len(self.h) / 256)
+ if not isinstance(self.h, list):
+ raise TypeError("first argument must be image or list")
+ self.bands = list(range(len(self.h) // 256))
def __getattr__(self, id):
"Calculate missing attribute"
if id[:4] == "_get":
- raise AttributeError, id
+ raise AttributeError(id)
# calculate missing attribute
v = getattr(self, "_get" + id)()
setattr(self, id, v)
@@ -126,7 +110,7 @@ class Stat:
v = []
for i in self.bands:
s = 0
- l = self.count[i]/2
+ l = self.count[i]//2
b = i * 256
for j in range(256):
s = s + self.h[b+j]
diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py
index 8618139a5..1e81d240e 100644
--- a/PIL/ImageTk.py
+++ b/PIL/ImageTk.py
@@ -25,15 +25,15 @@
# See the README file for information on usage and redistribution.
#
-import Tkinter, Image
+try:
+ import tkinter
+except ImportError:
+ import Tkinter
+ tkinter = Tkinter
+ del Tkinter
+
+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
@@ -45,48 +45,45 @@ def _pilbitmap_check():
if _pilbitmap_ok is None:
try:
im = Image.new("1", (1,1))
- Tkinter.BitmapImage(data="PIL:%d" % im.im.id)
+ tkinter.BitmapImage(data="PIL:%d" % im.im.id)
_pilbitmap_ok = 1
- except Tkinter.TclError:
+ except tkinter.TclError:
_pilbitmap_ok = 0
return _pilbitmap_ok
# --------------------------------------------------------------------
# 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):
# Tk compatibility: file or data
if image is None:
- if kw.has_key("file"):
+ if "file" in kw:
image = Image.open(kw["file"])
del kw["file"]
- elif kw.has_key("data"):
- from StringIO import StringIO
- image = Image.open(StringIO(kw["data"]))
+ elif "data" in kw:
+ from io import BytesIO
+ image = Image.open(BytesIO(kw["data"]))
del kw["data"]
if hasattr(image, "mode") and hasattr(image, "size"):
@@ -110,7 +107,7 @@ class PhotoImage:
self.__mode = mode
self.__size = size
- self.__photo = apply(Tkinter.PhotoImage, (), kw)
+ self.__photo = tkinter.PhotoImage(**kw)
self.tk = self.__photo.tk
if image:
self.paste(image)
@@ -123,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()
@@ -175,50 +176,47 @@ class PhotoImage:
try:
tk.call("PyImagingPhoto", self.__photo, block.id)
- except Tkinter.TclError, v:
+ except tkinter.TclError as v:
# activate Tkinter hook
try:
- import _imagingtk
+ from PIL import _imagingtk
try:
_imagingtk.tkinit(tk.interpaddr(), 1)
except AttributeError:
_imagingtk.tkinit(id(tk), 0)
tk.call("PyImagingPhoto", self.__photo, block.id)
- except (ImportError, AttributeError, Tkinter.TclError):
+ except (ImportError, AttributeError, tkinter.TclError):
raise # configuration problem; cannot attach to Tkinter
# --------------------------------------------------------------------
# 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):
# Tk compatibility: file or data
if image is None:
- if kw.has_key("file"):
+ if "file" in kw:
image = Image.open(kw["file"])
del kw["file"]
- elif kw.has_key("data"):
- from StringIO import StringIO
- image = Image.open(StringIO(kw["data"]))
+ elif "data" in kw:
+ from io import BytesIO
+ image = Image.open(BytesIO(kw["data"]))
del kw["data"]
self.__mode = image.mode
@@ -232,7 +230,7 @@ class BitmapImage:
else:
# slow but safe way
kw["data"] = image.tobitmap()
- self.__photo = apply(Tkinter.BitmapImage, (), kw)
+ self.__photo = tkinter.BitmapImage(**kw)
def __del__(self):
name = self.__photo.name
@@ -242,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)
# --------------------------------------------------------------------
@@ -279,18 +279,18 @@ def getimage(photo):
def _show(image, title):
- class UI(Tkinter.Label):
+ class UI(tkinter.Label):
def __init__(self, master, im):
if im.mode == "1":
self.image = BitmapImage(im, foreground="white", master=master)
else:
self.image = PhotoImage(im, master=master)
- Tkinter.Label.__init__(self, master, image=self.image,
+ tkinter.Label.__init__(self, master, image=self.image,
bg="black", bd=0)
- if not Tkinter._default_root:
- raise IOError, "tkinter not initialized"
- top = Tkinter.Toplevel()
+ if not tkinter._default_root:
+ raise IOError("tkinter not initialized")
+ top = tkinter.Toplevel()
if title:
top.title(title)
UI(top, image).pack()
diff --git a/PIL/ImageTransform.py b/PIL/ImageTransform.py
index cc323d3b9..5a8f9e9ec 100644
--- a/PIL/ImageTransform.py
+++ b/PIL/ImageTransform.py
@@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution.
#
-import Image
+from PIL import Image
class Transform(Image.ImageTransformHandler):
def __init__(self, data):
diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py
index f98725f74..aa90b887b 100644
--- a/PIL/ImageWin.py
+++ b/PIL/ImageWin.py
@@ -17,46 +17,52 @@
# See the README file for information on usage and redistribution.
#
-import Image
+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"):
@@ -73,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:
@@ -93,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):
@@ -105,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:
@@ -131,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)
@@ -150,23 +166,43 @@ class Dib:
else:
self.image.paste(im.im)
- ##
- # Load display memory contents from string buffer.
- #
- # @param buffer A string buffer containing display data (usually
- # data returned from tostring)
- def fromstring(self, buffer):
- return self.image.fromstring(buffer)
+ 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)
+
+
+ def tobytes(self):
+ """
+ Copy display memory contents to bytes object.
+
+ :return: A bytes object containing display data.
+ """
+ return self.image.tobytes()
##
- # Copy display memory contents to string buffer.
- #
- # @return A string buffer containing display data.
+ # Deprecated aliases to frombytes & tobytes.
+
+ def fromstring(self, *args, **kw):
+ warnings.warn(
+ 'fromstring() is deprecated. Please call frombytes() instead.',
+ DeprecationWarning,
+ stacklevel=2
+ )
+ return self.frombytes(*args, **kw)
def tostring(self):
- return self.image.tostring()
-
+ warnings.warn(
+ 'tostring() is deprecated. Please call tobytes() instead.',
+ DeprecationWarning,
+ stacklevel=2
+ )
+ return self.tobytes()
##
# Create a Window with the given title size.
@@ -179,7 +215,7 @@ class Window:
)
def __dispatcher(self, action, *args):
- return apply(getattr(self, "ui_handle_" + action), args)
+ return getattr(self, "ui_handle_" + action)(*args)
def ui_handle_clear(self, dc, x0, y0, x1, y1):
pass
diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py
index bf5611b8a..e68b00344 100644
--- a/PIL/ImtImagePlugin.py
+++ b/PIL/ImtImagePlugin.py
@@ -19,12 +19,12 @@ __version__ = "0.2"
import re
-import Image, ImageFile
+from PIL import Image, ImageFile
#
# --------------------------------------------------------------------
-field = re.compile(r"([a-z]*) ([^ \r\n]*)")
+field = re.compile(br"([a-z]*) ([^ \r\n]*)")
##
# Image plugin for IM Tools images.
@@ -39,19 +39,19 @@ class ImtImageFile(ImageFile.ImageFile):
# Quick rejection: if there's not a LF among the first
# 100 bytes, this is (probably) not a text header.
- if not "\n" in self.fp.read(100):
- raise SyntaxError, "not an IM file"
+ if not b"\n" in self.fp.read(100):
+ raise SyntaxError("not an IM file")
self.fp.seek(0)
xsize = ysize = 0
- while 1:
+ while True:
s = self.fp.read(1)
if not s:
break
- if s == chr(12):
+ if s == b'\x0C':
# image data begins
self.tile = [("raw", (0,0)+self.size,
@@ -67,7 +67,7 @@ class ImtImageFile(ImageFile.ImageFile):
s = s + self.fp.readline()
if len(s) == 1 or len(s) > 100:
break
- if s[0] == "*":
+ if s[0] == b"*":
continue # comment
m = field.match(s)
diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py
index acea5d18b..157b73509 100644
--- a/PIL/IptcImagePlugin.py
+++ b/PIL/IptcImagePlugin.py
@@ -15,37 +15,36 @@
# See the README file for information on usage and redistribution.
#
+from __future__ import print_function
__version__ = "0.3"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
import os, tempfile
+i8 = _binary.i8
+i16 = _binary.i16be
+i32 = _binary.i32be
+o8 = _binary.o8
COMPRESSION = {
1: "raw",
5: "jpeg"
}
-PAD = chr(0) * 4
+PAD = o8(0) * 4
#
# Helpers
-def i16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-
-def i32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
-
def i(c):
return i32((PAD + c)[-4:])
def dump(c):
for i in c:
- print "%02x" % ord(i),
- print
+ print("%02x" % i8(i), end=' ')
+ print()
##
# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
@@ -66,16 +65,16 @@ class IptcImageFile(ImageFile.ImageFile):
if not len(s):
return None, 0
- tag = ord(s[1]), ord(s[2])
+ tag = i8(s[1]), i8(s[2])
# syntax
- if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
- raise SyntaxError, "invalid IPTC/NAA file"
+ if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
+ raise SyntaxError("invalid IPTC/NAA file")
# field size
- size = ord(s[3])
+ size = i8(s[3])
if size > 132:
- raise IOError, "illegal field length in IPTC/NAA file"
+ raise IOError("illegal field length in IPTC/NAA file")
elif size == 128:
size = 0
elif size > 128:
@@ -97,7 +96,7 @@ class IptcImageFile(ImageFile.ImageFile):
if sz != size[0]:
return 0
y = 1
- while 1:
+ while True:
self.fp.seek(sz, 1)
t, s = self.field()
if t != (8, 10):
@@ -110,7 +109,7 @@ class IptcImageFile(ImageFile.ImageFile):
def _open(self):
# load descriptive fields
- while 1:
+ while True:
offset = self.fp.tell()
tag, size = self.field()
if not tag or tag == (8,10):
@@ -119,7 +118,7 @@ class IptcImageFile(ImageFile.ImageFile):
tagdata = self.fp.read(size)
else:
tagdata = None
- if tag in self.info.keys():
+ if tag in list(self.info.keys()):
if isinstance(self.info[tag], list):
self.info[tag].append(tagdata)
else:
@@ -130,10 +129,10 @@ class IptcImageFile(ImageFile.ImageFile):
# print tag, self.info[tag]
# mode
- layers = ord(self.info[(3,60)][0])
- component = ord(self.info[(3,60)][1])
- if self.info.has_key((3,65)):
- id = ord(self.info[(3,65)][0])-1
+ layers = i8(self.info[(3,60)][0])
+ component = i8(self.info[(3,60)][1])
+ if (3,65) in self.info:
+ id = i8(self.info[(3,65)][0])-1
else:
id = 0
if layers == 1 and not component:
@@ -150,7 +149,7 @@ class IptcImageFile(ImageFile.ImageFile):
try:
compression = COMPRESSION[self.getint((3,120))]
except KeyError:
- raise IOError, "Unknown IPTC image compression"
+ raise IOError("Unknown IPTC image compression")
# tile
if tag == (8,10):
@@ -179,7 +178,7 @@ class IptcImageFile(ImageFile.ImageFile):
# To simplify access to the extracted file,
# prepend a PPM header
o.write("P5\n%d %d\n255\n" % self.size)
- while 1:
+ while True:
type, size = self.field()
if type != (8, 10):
break
@@ -218,8 +217,8 @@ Image.register_extension("IPTC", ".iim")
def getiptcinfo(im):
- import TiffImagePlugin, JpegImagePlugin
- import StringIO
+ from PIL import TiffImagePlugin, JpegImagePlugin
+ import io
data = None
@@ -241,7 +240,7 @@ def getiptcinfo(im):
code = JpegImagePlugin.i16(app, offset)
offset = offset + 2
# resource name (usually empty)
- name_len = ord(app[offset])
+ name_len = i8(app[offset])
name = app[offset+1:offset+1+name_len]
offset = 1 + offset + name_len
if offset & 1:
@@ -263,7 +262,7 @@ def getiptcinfo(im):
# get raw data from the IPTC/NAA tag (PhotoShop tags the data
# as 4-byte integers, so we cannot use the get method...)
try:
- type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
+ data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK]
except (AttributeError, KeyError):
pass
@@ -278,7 +277,7 @@ def getiptcinfo(im):
# parse the IPTC information chunk
im.info = {}
- im.fp = StringIO.StringIO(data)
+ im.fp = io.BytesIO(data)
try:
im._open()
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py
index 933abf3c2..90d3b5194 100644
--- a/PIL/JpegImagePlugin.py
+++ b/PIL/JpegImagePlugin.py
@@ -35,14 +35,14 @@
__version__ = "0.6"
import array, struct
-import string
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
+from PIL.JpegPresets import presets
+from PIL._util import isStringType
-def i16(c,o=0):
- return ord(c[o+1]) + (ord(c[o])<<8)
-
-def i32(c,o=0):
- return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24)
+i8 = _binary.i8
+o8 = _binary.o8
+i16 = _binary.i16be
+i32 = _binary.i32be
#
# Parser
@@ -64,13 +64,13 @@ def APP(self, marker):
self.app[app] = s # compatibility
self.applist.append((app, s))
- if marker == 0xFFE0 and s[:4] == "JFIF":
+ if marker == 0xFFE0 and s[:4] == b"JFIF":
# extract JFIF information
self.info["jfif"] = version = i16(s, 5) # version
self.info["jfif_version"] = divmod(version, 256)
# extract JFIF properties
try:
- jfif_unit = ord(s[7])
+ jfif_unit = i8(s[7])
jfif_density = i16(s, 8), i16(s, 10)
except:
pass
@@ -79,13 +79,13 @@ def APP(self, marker):
self.info["dpi"] = jfif_density
self.info["jfif_unit"] = jfif_unit
self.info["jfif_density"] = jfif_density
- elif marker == 0xFFE1 and s[:5] == "Exif\0":
+ elif marker == 0xFFE1 and s[:5] == b"Exif\0":
# extract Exif information (incomplete)
self.info["exif"] = s # FIXME: value will change
- elif marker == 0xFFE2 and s[:5] == "FPXR\0":
+ elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
# extract FlashPix information (incomplete)
self.info["flashpix"] = s # FIXME: value will change
- elif marker == 0xFFE2 and s[:12] == "ICC_PROFILE\0":
+ elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0":
# Since an ICC profile can be larger than the maximum size of
# a JPEG marker (64K), we need provisions to split it into
# multiple markers. The format defined by the ICC specifies
@@ -98,11 +98,11 @@ def APP(self, marker):
# reassemble the profile, rather than assuming that the APP2
# markers appear in the correct sequence.
self.icclist.append(s)
- elif marker == 0xFFEE and s[:5] == "Adobe":
+ elif marker == 0xFFEE and s[:5] == b"Adobe":
self.info["adobe"] = i16(s, 5)
# extract Adobe custom properties
try:
- adobe_transform = ord(s[1])
+ adobe_transform = i8(s[1])
except:
pass
else:
@@ -130,11 +130,11 @@ def SOF(self, marker):
s = ImageFile._safe_read(self.fp, n)
self.size = i16(s[3:]), i16(s[1:])
- self.bits = ord(s[0])
+ self.bits = i8(s[0])
if self.bits != 8:
raise SyntaxError("cannot handle %d-bit layers" % self.bits)
- self.layers = ord(s[5])
+ self.layers = i8(s[5])
if self.layers == 1:
self.mode = "L"
elif self.layers == 3:
@@ -150,11 +150,11 @@ def SOF(self, marker):
if self.icclist:
# fixup icc profile
self.icclist.sort() # sort by sequence number
- if ord(self.icclist[0][13]) == len(self.icclist):
+ if i8(self.icclist[0][13]) == len(self.icclist):
profile = []
for p in self.icclist:
profile.append(p[14:])
- icc_profile = string.join(profile, "")
+ icc_profile = b"".join(profile)
else:
icc_profile = None # wrong number of fragments
self.info["icc_profile"] = icc_profile
@@ -163,7 +163,7 @@ def SOF(self, marker):
for i in range(6, len(s), 3):
t = s[i:i+3]
# 4-tuples: id, vsamp, hsamp, qtable
- self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2])))
+ self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2])))
def DQT(self, marker):
#
@@ -179,8 +179,8 @@ def DQT(self, marker):
while len(s):
if len(s) < 65:
raise SyntaxError("bad quantization table marker")
- v = ord(s[0])
- if v/16 == 0:
+ v = i8(s[0])
+ if v//16 == 0:
self.quantization[v&15] = array.array("b", s[1:65])
s = s[65:]
else:
@@ -259,7 +259,7 @@ MARKER = {
def _accept(prefix):
- return prefix[0] == "\377"
+ return prefix[0:1] == b"\377"
##
# Image plugin for JPEG and JFIF images.
@@ -273,7 +273,7 @@ class JpegImageFile(ImageFile.ImageFile):
s = self.fp.read(1)
- if ord(s[0]) != 255:
+ if i8(s[0]) != 255:
raise SyntaxError("not a JPEG file")
# Create attributes
@@ -288,13 +288,13 @@ class JpegImageFile(ImageFile.ImageFile):
self.applist = []
self.icclist = []
- while 1:
+ while True:
s = s + self.fp.read(1)
i = i16(s)
- if MARKER.has_key(i):
+ if i in MARKER:
name, description, handler = MARKER[i]
# print hex(i), name, description
if handler is not None:
@@ -326,12 +326,12 @@ class JpegImageFile(ImageFile.ImageFile):
a = mode, ""
if size:
- scale = max(self.size[0] / size[0], self.size[1] / size[1])
+ scale = max(self.size[0] // size[0], self.size[1] // size[1])
for s in [8, 4, 2, 1]:
if scale >= s:
break
- e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1]
- self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s)
+ e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1]
+ self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s)
scale = s
self.tile = [(d, e, o, a)]
@@ -359,50 +359,55 @@ class JpegImageFile(ImageFile.ImageFile):
self.tile = []
def _getexif(self):
- # Extract EXIF information. This method is highly experimental,
- # and is likely to be replaced with something better in a future
- # version.
- import TiffImagePlugin, StringIO
- def fixup(value):
- if len(value) == 1:
- return value[0]
- return value
- # The EXIF record consists of a TIFF file embedded in a JPEG
- # application marker (!).
- try:
- data = self.info["exif"]
- except KeyError:
- return None
- file = StringIO.StringIO(data[6:])
- head = file.read(8)
- exif = {}
- # process dictionary
+ return _getexif(self)
+
+
+def _getexif(self):
+ # Extract EXIF information. This method is highly experimental,
+ # and is likely to be replaced with something better in a future
+ # version.
+ from PIL import TiffImagePlugin
+ import io
+ def fixup(value):
+ if len(value) == 1:
+ return value[0]
+ return value
+ # The EXIF record consists of a TIFF file embedded in a JPEG
+ # application marker (!).
+ try:
+ data = self.info["exif"]
+ except KeyError:
+ return None
+ file = io.BytesIO(data[6:])
+ head = file.read(8)
+ exif = {}
+ # process dictionary
+ info = TiffImagePlugin.ImageFileDirectory(head)
+ info.load(file)
+ for key, value in info.items():
+ exif[key] = fixup(value)
+ # get exif extension
+ try:
+ file.seek(exif[0x8769])
+ except KeyError:
+ pass
+ else:
info = TiffImagePlugin.ImageFileDirectory(head)
info.load(file)
for key, value in info.items():
exif[key] = fixup(value)
- # get exif extension
- try:
- file.seek(exif[0x8769])
- except KeyError:
- pass
- else:
- info = TiffImagePlugin.ImageFileDirectory(head)
- info.load(file)
- for key, value in info.items():
- exif[key] = fixup(value)
- # get gpsinfo extension
- try:
- file.seek(exif[0x8825])
- except KeyError:
- pass
- else:
- info = TiffImagePlugin.ImageFileDirectory(head)
- info.load(file)
- exif[0x8825] = gps = {}
- for key, value in info.items():
- gps[key] = fixup(value)
- return exif
+ # get gpsinfo extension
+ try:
+ file.seek(exif[0x8825])
+ except KeyError:
+ pass
+ else:
+ info = TiffImagePlugin.ImageFileDirectory(head)
+ info.load(file)
+ exif[0x8825] = gps = {}
+ for key, value in info.items():
+ gps[key] = fixup(value)
+ return exif
# --------------------------------------------------------------------
# stuff to save JPEG files
@@ -417,6 +422,31 @@ RAWMODE = {
"YCbCr": "YCbCr",
}
+zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63)
+
+samplings = {
+ (1, 1, 1, 1, 1, 1): 0,
+ (2, 1, 1, 1, 1, 1): 1,
+ (2, 2, 1, 1, 1, 1): 2,
+ }
+
+def convert_dict_qtables(qtables):
+ qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)]
+ for idx, table in enumerate(qtables):
+ qtables[idx] = [table[i] for i in zigzag_index]
+ return qtables
+
+def get_sampling(im):
+ sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
+ return samplings.get(sampling, -1)
+
def _save(im, fp, filename):
try:
@@ -428,15 +458,74 @@ def _save(im, fp, filename):
dpi = info.get("dpi", (0, 0))
+ quality = info.get("quality", 0)
subsampling = info.get("subsampling", -1)
+ qtables = info.get("qtables")
+
+ if quality == "keep":
+ quality = 0
+ subsampling = "keep"
+ qtables = "keep"
+ elif quality in presets:
+ preset = presets[quality]
+ quality = 0
+ subsampling = preset.get('subsampling', -1)
+ qtables = preset.get('quantization')
+ elif not isinstance(quality, int):
+ raise ValueError("Invalid quality setting")
+ else:
+ if subsampling in presets:
+ subsampling = presets[subsampling].get('subsampling', -1)
+ if qtables in presets:
+ qtables = presets[qtables].get('quantization')
+
if subsampling == "4:4:4":
subsampling = 0
elif subsampling == "4:2:2":
subsampling = 1
elif subsampling == "4:1:1":
subsampling = 2
+ elif subsampling == "keep":
+ if im.format != "JPEG":
+ raise ValueError("Cannot use 'keep' when original image is not a JPEG")
+ subsampling = get_sampling(im)
- extra = ""
+ def validate_qtables(qtables):
+ if qtables is None:
+ return qtables
+ if isStringType(qtables):
+ try:
+ lines = [int(num) for line in qtables.splitlines()
+ for num in line.split('#', 1)[0].split()]
+ except ValueError:
+ raise ValueError("Invalid quantization table")
+ else:
+ qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)]
+ if isinstance(qtables, (tuple, list, dict)):
+ if isinstance(qtables, dict):
+ qtables = convert_dict_qtables(qtables)
+ elif isinstance(qtables, tuple):
+ qtables = list(qtables)
+ if not (0 < len(qtables) < 5):
+ raise ValueError("None or too many quantization tables")
+ for idx, table in enumerate(qtables):
+ try:
+ if len(table) != 64:
+ raise
+ table = array.array('b', table)
+ except TypeError:
+ raise ValueError("Invalid quantization table")
+ else:
+ qtables[idx] = list(table)
+ return qtables
+
+ if qtables == "keep":
+ if im.format != "JPEG":
+ raise ValueError("Cannot use 'keep' when original image is not a JPEG")
+ qtables = getattr(im, "quantization", None)
+ qtables = validate_qtables(qtables)
+
+ extra = b""
icc_profile = info.get("icc_profile")
if icc_profile:
@@ -450,25 +539,40 @@ def _save(im, fp, filename):
i = 1
for marker in markers:
size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker))
- extra = extra + ("\xFF\xE2" + size + "ICC_PROFILE\0" + chr(i) + chr(len(markers)) + marker)
+ extra = extra + (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + marker)
i = i + 1
# get keyword arguments
im.encoderconfig = (
- info.get("quality", 0),
+ quality,
# "progressive" is the official name, but older documentation
# says "progression"
# FIXME: issue a warning if the wrong form is used (post-1.1.7)
- info.has_key("progressive") or info.has_key("progression"),
+ "progressive" in info or "progression" in info,
info.get("smooth", 0),
- info.has_key("optimize"),
+ "optimize" in info,
info.get("streamtype", 0),
dpi[0], dpi[1],
subsampling,
+ qtables,
extra,
+ info.get("exif", b"")
)
- ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)])
+
+ # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot.
+ # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this
+ # is a value that's been used in a django patch.
+ # https://github.com/jdriscoll/django-imagekit/issues/50
+ bufsize=0
+ if "optimize" in info or "progressive" in info or "progression" in info:
+ bufsize = im.size[0]*im.size[1]
+
+ # The exif info needs to be written as one block, + APP1, + one spare byte.
+ # Ensure that our buffer is big enough
+ bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 )
+
+ ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize)
def _save_cjpeg(im, fp, filename):
# ALTERNATIVE: handle JPEGs via the IJG command line utilities.
diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py
new file mode 100644
index 000000000..e7bec148a
--- /dev/null
+++ b/PIL/JpegPresets.py
@@ -0,0 +1,241 @@
+"""
+JPEG quality settings equivalent to the Photoshop settings.
+
+More presets can be added to the presets dict if needed.
+
+Can be use when saving JPEG file.
+
+To apply the preset, specify::
+
+ quality="preset_name"
+
+To apply only the quantization table::
+
+ qtables="preset_name"
+
+To apply only the subsampling setting::
+
+ subsampling="preset_name"
+
+Example::
+
+ im.save("image_name.jpg", quality="web_high")
+
+
+Subsampling
+-----------
+
+Subsampling is the practice of encoding images by implementing less resolution
+for chroma information than for luma information.
+(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling)
+
+Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and
+4:1:1 (or 4:2:0?).
+
+You can get the subsampling of a JPEG with the
+`JpegImagePlugin.get_subsampling(im)` function.
+
+
+Quantization tables
+-------------------
+
+They are values use by the DCT (Discrete cosine transform) to remove
+*unnecessary* information from the image (the lossy part of the compression).
+(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices,
+http://en.wikipedia.org/wiki/JPEG#Quantization)
+
+You can get the quantization tables of a JPEG with::
+
+ im.quantization
+
+This will return a dict with a number of arrays. You can pass this dict directly
+as the qtables argument when saving a JPEG.
+
+The tables format between im.quantization and quantization in presets differ in
+3 ways:
+
+1. The base container of the preset is a list with sublists instead of dict.
+ dict[0] -> list[0], dict[1] -> list[1], ...
+2. Each table in a preset is a list instead of an array.
+3. The zigzag order is remove in the preset (needed by libjpeg >= 6a).
+
+You can convert the dict format to the preset format with the
+`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
+
+Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html
+
+"""
+
+presets = {
+ 'web_low': {'subsampling': 2, # "4:1:1"
+ 'quantization': [
+ [20, 16, 25, 39, 50, 46, 62, 68,
+ 16, 18, 23, 38, 38, 53, 65, 68,
+ 25, 23, 31, 38, 53, 65, 68, 68,
+ 39, 38, 38, 53, 65, 68, 68, 68,
+ 50, 38, 53, 65, 68, 68, 68, 68,
+ 46, 53, 65, 68, 68, 68, 68, 68,
+ 62, 65, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68],
+ [21, 25, 32, 38, 54, 68, 68, 68,
+ 25, 28, 24, 38, 54, 68, 68, 68,
+ 32, 24, 32, 43, 66, 68, 68, 68,
+ 38, 38, 43, 53, 68, 68, 68, 68,
+ 54, 54, 66, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68]
+ ]},
+ 'web_medium': {'subsampling': 2, # "4:1:1"
+ 'quantization': [
+ [16, 11, 11, 16, 23, 27, 31, 30,
+ 11, 12, 12, 15, 20, 23, 23, 30,
+ 11, 12, 13, 16, 23, 26, 35, 47,
+ 16, 15, 16, 23, 26, 37, 47, 64,
+ 23, 20, 23, 26, 39, 51, 64, 64,
+ 27, 23, 26, 37, 51, 64, 64, 64,
+ 31, 23, 35, 47, 64, 64, 64, 64,
+ 30, 30, 47, 64, 64, 64, 64, 64],
+ [17, 15, 17, 21, 20, 26, 38, 48,
+ 15, 19, 18, 17, 20, 26, 35, 43,
+ 17, 18, 20, 22, 26, 30, 46, 53,
+ 21, 17, 22, 28, 30, 39, 53, 64,
+ 20, 20, 26, 30, 39, 48, 64, 64,
+ 26, 26, 30, 39, 48, 63, 64, 64,
+ 38, 35, 46, 53, 64, 64, 64, 64,
+ 48, 43, 53, 64, 64, 64, 64, 64]
+ ]},
+ 'web_high': {'subsampling': 0, # "4:4:4"
+ 'quantization': [
+ [ 6, 4, 4, 6, 9, 11, 12, 16,
+ 4, 5, 5, 6, 8, 10, 12, 12,
+ 4, 5, 5, 6, 10, 12, 14, 19,
+ 6, 6, 6, 11, 12, 15, 19, 28,
+ 9, 8, 10, 12, 16, 20, 27, 31,
+ 11, 10, 12, 15, 20, 27, 31, 31,
+ 12, 12, 14, 19, 27, 31, 31, 31,
+ 16, 12, 19, 28, 31, 31, 31, 31],
+ [ 7, 7, 13, 24, 26, 31, 31, 31,
+ 7, 12, 16, 21, 31, 31, 31, 31,
+ 13, 16, 17, 31, 31, 31, 31, 31,
+ 24, 21, 31, 31, 31, 31, 31, 31,
+ 26, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31]
+ ]},
+ 'web_very_high': {'subsampling': 0, # "4:4:4"
+ 'quantization': [
+ [ 2, 2, 2, 2, 3, 4, 5, 6,
+ 2, 2, 2, 2, 3, 4, 5, 6,
+ 2, 2, 2, 2, 4, 5, 7, 9,
+ 2, 2, 2, 4, 5, 7, 9, 12,
+ 3, 3, 4, 5, 8, 10, 12, 12,
+ 4, 4, 5, 7, 10, 12, 12, 12,
+ 5, 5, 7, 9, 12, 12, 12, 12,
+ 6, 6, 9, 12, 12, 12, 12, 12],
+ [ 3, 3, 5, 9, 13, 15, 15, 15,
+ 3, 4, 6, 11, 14, 12, 12, 12,
+ 5, 6, 9, 14, 12, 12, 12, 12,
+ 9, 11, 14, 12, 12, 12, 12, 12,
+ 13, 14, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12]
+ ]},
+ 'web_maximum': {'subsampling': 0, # "4:4:4"
+ 'quantization': [
+ [ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 2,
+ 1, 1, 1, 1, 1, 1, 2, 2,
+ 1, 1, 1, 1, 1, 2, 2, 3,
+ 1, 1, 1, 1, 2, 2, 3, 3,
+ 1, 1, 1, 2, 2, 3, 3, 3,
+ 1, 1, 2, 2, 3, 3, 3, 3],
+ [ 1, 1, 1, 2, 2, 3, 3, 3,
+ 1, 1, 1, 2, 3, 3, 3, 3,
+ 1, 1, 1, 3, 3, 3, 3, 3,
+ 2, 2, 3, 3, 3, 3, 3, 3,
+ 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3]
+ ]},
+ 'low': {'subsampling': 2, # "4:1:1"
+ 'quantization': [
+ [18, 14, 14, 21, 30, 35, 34, 17,
+ 14, 16, 16, 19, 26, 23, 12, 12,
+ 14, 16, 17, 21, 23, 12, 12, 12,
+ 21, 19, 21, 23, 12, 12, 12, 12,
+ 30, 26, 23, 12, 12, 12, 12, 12,
+ 35, 23, 12, 12, 12, 12, 12, 12,
+ 34, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12],
+ [20, 19, 22, 27, 20, 20, 17, 17,
+ 19, 25, 23, 14, 14, 12, 12, 12,
+ 22, 23, 14, 14, 12, 12, 12, 12,
+ 27, 14, 14, 12, 12, 12, 12, 12,
+ 20, 14, 12, 12, 12, 12, 12, 12,
+ 20, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12]
+ ]},
+ 'medium': {'subsampling': 2, # "4:1:1"
+ 'quantization': [
+ [12, 8, 8, 12, 17, 21, 24, 17,
+ 8, 9, 9, 11, 15, 19, 12, 12,
+ 8, 9, 10, 12, 19, 12, 12, 12,
+ 12, 11, 12, 21, 12, 12, 12, 12,
+ 17, 15, 19, 12, 12, 12, 12, 12,
+ 21, 19, 12, 12, 12, 12, 12, 12,
+ 24, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12],
+ [13, 11, 13, 16, 20, 20, 17, 17,
+ 11, 14, 14, 14, 14, 12, 12, 12,
+ 13, 14, 14, 14, 12, 12, 12, 12,
+ 16, 14, 14, 12, 12, 12, 12, 12,
+ 20, 14, 12, 12, 12, 12, 12, 12,
+ 20, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12]
+ ]},
+ 'high': {'subsampling': 0, # "4:4:4"
+ 'quantization': [
+ [ 6, 4, 4, 6, 9, 11, 12, 16,
+ 4, 5, 5, 6, 8, 10, 12, 12,
+ 4, 5, 5, 6, 10, 12, 12, 12,
+ 6, 6, 6, 11, 12, 12, 12, 12,
+ 9, 8, 10, 12, 12, 12, 12, 12,
+ 11, 10, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 16, 12, 12, 12, 12, 12, 12, 12],
+ [ 7, 7, 13, 24, 20, 20, 17, 17,
+ 7, 12, 16, 14, 14, 12, 12, 12,
+ 13, 16, 14, 14, 12, 12, 12, 12,
+ 24, 14, 14, 12, 12, 12, 12, 12,
+ 20, 14, 12, 12, 12, 12, 12, 12,
+ 20, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12,
+ 17, 12, 12, 12, 12, 12, 12, 12]
+ ]},
+ 'maximum': {'subsampling': 0, # "4:4:4"
+ 'quantization': [
+ [ 2, 2, 2, 2, 3, 4, 5, 6,
+ 2, 2, 2, 2, 3, 4, 5, 6,
+ 2, 2, 2, 2, 4, 5, 7, 9,
+ 2, 2, 2, 4, 5, 7, 9, 12,
+ 3, 3, 4, 5, 8, 10, 12, 12,
+ 4, 4, 5, 7, 10, 12, 12, 12,
+ 5, 5, 7, 9, 12, 12, 12, 12,
+ 6, 6, 9, 12, 12, 12, 12, 12],
+ [ 3, 3, 5, 9, 13, 15, 15, 15,
+ 3, 4, 6, 10, 14, 12, 12, 12,
+ 5, 6, 9, 14, 12, 12, 12, 12,
+ 9, 10, 14, 12, 12, 12, 12, 12,
+ 13, 14, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12,
+ 15, 12, 12, 12, 12, 12, 12, 12]
+ ]},
+}
\ No newline at end of file
diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py
index 62ee52cf8..3aef10ba8 100644
--- a/PIL/McIdasImagePlugin.py
+++ b/PIL/McIdasImagePlugin.py
@@ -19,10 +19,10 @@
__version__ = "0.2"
import struct
-import Image, ImageFile
+from PIL import Image, ImageFile
def _accept(s):
- return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04"
+ return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04"
##
# Image plugin for McIdas area images.
diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py
index b1a3bba79..84e962860 100644
--- a/PIL/MicImagePlugin.py
+++ b/PIL/MicImagePlugin.py
@@ -20,8 +20,8 @@
__version__ = "0.1"
-import Image, TiffImagePlugin
-from OleFileIO import *
+from PIL import Image, TiffImagePlugin
+from PIL.OleFileIO import *
#
@@ -47,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
try:
self.ole = OleFileIO(self.fp)
except IOError:
- raise SyntaxError, "not an MIC file; invalid OLE file"
+ raise SyntaxError("not an MIC file; invalid OLE file")
# find ACI subfiles with Image members (maybe not the
# best way to identify MIC files, but what the... ;-)
@@ -60,7 +60,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
# if we didn't find any images, this is probably not
# an MIC file.
if not self.images:
- raise SyntaxError, "not an MIC file; no image entries"
+ raise SyntaxError("not an MIC file; no image entries")
self.__fp = self.fp
self.frame = 0
@@ -75,7 +75,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
try:
filename = self.images[frame]
except IndexError:
- raise EOFError, "no such frame"
+ raise EOFError("no such frame")
self.fp = self.ole.openstream(filename)
diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py
index c02edee82..9d7a0ea7a 100644
--- a/PIL/MpegImagePlugin.py
+++ b/PIL/MpegImagePlugin.py
@@ -15,7 +15,8 @@
__version__ = "0.1"
-import Image, ImageFile
+from PIL import Image, ImageFile
+from PIL._binary import i8
#
# Bitstream parser
@@ -28,7 +29,7 @@ class BitStream:
self.bitbuffer = 0
def next(self):
- return ord(self.fp.read(1))
+ return i8(self.fp.read(1))
def peek(self, bits):
while self.bits < bits:
@@ -38,11 +39,11 @@ class BitStream:
continue
self.bitbuffer = (self.bitbuffer << 8) + c
self.bits = self.bits + 8
- return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1
+ return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1
def skip(self, bits):
while self.bits < bits:
- self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1))
+ self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1))
self.bits = self.bits + 8
self.bits = self.bits - bits
@@ -65,7 +66,7 @@ class MpegImageFile(ImageFile.ImageFile):
s = BitStream(self.fp)
if s.read(32) != 0x1B3:
- raise SyntaxError, "not an MPEG file"
+ raise SyntaxError("not an MPEG file")
self.mode = "RGB"
self.size = s.read(12), s.read(12)
diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py
index 9dac36b47..743ebe172 100644
--- a/PIL/MspImagePlugin.py
+++ b/PIL/MspImagePlugin.py
@@ -19,17 +19,16 @@
__version__ = "0.1"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
#
# read MSP files
-def i16(c):
- return ord(c[0]) + (ord(c[1])<<8)
+i16 = _binary.i16le
def _accept(prefix):
- return prefix[:4] in ["DanM", "LinS"]
+ return prefix[:4] in [b"DanM", b"LinS"]
##
# Image plugin for Windows MSP images. This plugin supports both
@@ -44,20 +43,20 @@ class MspImageFile(ImageFile.ImageFile):
# Header
s = self.fp.read(32)
- if s[:4] not in ["DanM", "LinS"]:
- raise SyntaxError, "not an MSP file"
+ if s[:4] not in [b"DanM", b"LinS"]:
+ raise SyntaxError("not an MSP file")
# Header checksum
sum = 0
for i in range(0, 32, 2):
sum = sum ^ i16(s[i:i+2])
if sum != 0:
- raise SyntaxError, "bad MSP checksum"
+ raise SyntaxError("bad MSP checksum")
self.mode = "1"
self.size = i16(s[4:]), i16(s[6:])
- if s[:4] == "DanM":
+ if s[:4] == b"DanM":
self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))]
else:
self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)]
@@ -65,18 +64,17 @@ class MspImageFile(ImageFile.ImageFile):
#
# write MSP files (uncompressed only)
-def o16(i):
- return chr(i&255) + chr(i>>8&255)
+o16 = _binary.o16le
def _save(im, fp, filename):
if im.mode != "1":
- raise IOError, "cannot write mode %s as MSP" % im.mode
+ raise IOError("cannot write mode %s as MSP" % im.mode)
# create MSP header
header = [0] * 16
- header[0], header[1] = i16("Da"), i16("nM") # version 1
+ header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1
header[2], header[3] = im.size
header[4], header[5] = 1, 1
header[6], header[7] = 1, 1
diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py
index 631a8ed84..5007fd1d7 100644
--- a/PIL/OleFileIO.py
+++ b/PIL/OleFileIO.py
@@ -23,6 +23,8 @@ See source code and LICENSE.txt for information on usage and redistribution.
WARNING: THIS IS (STILL) WORK IN PROGRESS.
"""
+from __future__ import print_function
+
__author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)"
__date__ = "2013-07-24"
__version__ = '0.26'
@@ -218,11 +220,18 @@ __version__ = '0.26'
#------------------------------------------------------------------------------
-import string, StringIO, struct, array, os.path, sys, datetime
+import io
+import sys
+from PIL import _binary
+from PIL._util import isPath
+import struct, array, os.path, sys, datetime
#[PL] Define explicitly the public API to avoid private objects in pydoc:
__all__ = ['OleFileIO', 'isOleFile']
+if str is not bytes:
+ long = int
+
#[PL] workaround to fix an issue with array item size on 64 bits systems:
if array.array('L').itemsize == 4:
# on 32 bits platforms, long integers in an array are 32 bits:
@@ -280,7 +289,7 @@ def set_debug_mode(debug_mode):
debug = debug_pass
#TODO: convert this to hex
-MAGIC = '\320\317\021\340\241\261\032\341'
+MAGIC = b'\320\317\021\340\241\261\032\341'
#[PL]: added constants for Sector IDs (from AAF specifications)
MAXREGSECT = 0xFFFFFFFAL; # maximum SECT
@@ -319,7 +328,7 @@ VT_VECTOR=0x1000;
# map property id to name (for debugging purposes)
VT = {}
-for keyword, var in vars().items():
+for keyword, var in list(vars().items()):
if keyword[:3] == "VT_":
VT[var] = keyword
@@ -360,26 +369,9 @@ def isOleFile (filename):
return False
-#TODO: replace i16 and i32 with more readable struct.unpack equivalent
-def i16(c, o = 0):
- """
- Converts a 2-bytes (16 bits) string to an integer.
-
- c: string containing bytes to convert
- o: offset of bytes to convert in string
- """
- return ord(c[o])+(ord(c[o+1])<<8)
-
-
-def i32(c, o = 0):
- """
- Converts a 4-bytes (32 bits) string to an integer.
-
- c: string containing bytes to convert
- o: offset of bytes to convert in string
- """
- return int(ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24))
- # [PL]: added int() because "<<" gives long int since Python 2.4
+i8 = _binary.i8
+i16 = _binary.i16le
+i32 = _binary.i32le
def _clsid(clsid):
@@ -392,7 +384,7 @@ def _clsid(clsid):
return ""
return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) %
((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) +
- tuple(map(ord, clsid[8:16]))))
+ tuple(map(i8, clsid[8:16]))))
@@ -416,7 +408,7 @@ try:
# First the string is converted to plain Unicode:
# (assuming it is encoded as UTF-16 little-endian)
u = s.decode('UTF-16LE', errors)
- if KEEP_UNICODE_NAMES:
+ if bytes is not str or KEEP_UNICODE_NAMES:
return u
else:
# Second the unicode string is converted to Latin-1
@@ -608,7 +600,7 @@ class OleMetadata:
#--- _OleStream ---------------------------------------------------------------
-class _OleStream(StringIO.StringIO):
+class _OleStream(io.BytesIO):
"""
OLE2 Stream
@@ -718,7 +710,7 @@ class _OleStream(StringIO.StringIO):
#[PL] Last sector should be a "end of chain" marker:
if sect != ENDOFCHAIN:
raise IOError, 'incorrect last sector index in OLE stream'
- data = string.join(data, "")
+ data = b"".join(data)
# Data is truncated to the actual stream size:
if len(data) >= size:
data = data[:size]
@@ -733,7 +725,7 @@ class _OleStream(StringIO.StringIO):
debug('len(data)=%d, size=%d' % (len(data), size))
raise IOError, 'OLE stream size is less than declared'
# when all data is read in memory, StringIO constructor is called
- StringIO.StringIO.__init__(self, data)
+ io.BytesIO.__init__(self, data)
# Then the _OleStream object can be used as a read-only file object.
@@ -943,12 +935,12 @@ class _OleDirectoryEntry:
"Dump this entry, and all its subentries (for debug purposes only)"
TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)",
"(property)", "(root)"]
- print " "*tab + repr(self.name), TYPES[self.entry_type],
+ print(" "*tab + repr(self.name), TYPES[self.entry_type], end=' ')
if self.entry_type in (STGTY_STREAM, STGTY_ROOT):
- print self.size, "bytes",
- print
+ print(self.size, "bytes", end=' ')
+ print()
if self.entry_type in (STGTY_STORAGE, STGTY_ROOT) and self.clsid:
- print " "*tab + "{%s}" % self.clsid
+ print(" "*tab + "{%s}" % self.clsid)
for kid in self.kids:
kid.dump(tab + 2)
@@ -994,7 +986,7 @@ class OleFileIO:
Object names are given as a list of strings, one for each subentry
level. The root entry should be omitted. For example, the following
- code extracts all image streams from a Microsoft Image Composer file:
+ code extracts all image streams from a Microsoft Image Composer file::
ole = OleFileIO("fan.mic")
@@ -1047,7 +1039,7 @@ class OleFileIO:
"""
# added by [PL]
if defect_level >= self._raise_defects_level:
- raise exception_type, message
+ raise exception_type(message)
else:
# just record the issue, no exception raised:
self.parsing_issues.append((exception_type, message))
@@ -1070,7 +1062,7 @@ class OleFileIO:
#TODO: if larger than 1024 bytes, this could be the actual data => StringIO
self.fp = open(filename, "rb")
# old code fails if filename is not a plain string:
- #if type(filename) == type(""):
+ #if isPath(filename):
# self.fp = open(filename, "rb")
#else:
# self.fp = filename
@@ -1389,7 +1381,7 @@ class OleFileIO:
## if ix == 0xFFFFFFFEL or ix == 0xFFFFFFFFL:
## break
## s = self.getsect(ix)
-## #fat = fat + map(lambda i, s=s: i32(s, i), range(0, len(s), 4))
+## #fat = fat + [i32(s, i) for i in range(0, len(s), 4)]
## fat = fat + array.array(UINT32, s)
if self.csectDif != 0:
# [PL] There's a DIFAT because file is larger than 6.8MB
@@ -1462,7 +1454,7 @@ class OleFileIO:
# In any case, first read stream_size:
s = self._open(self.minifatsect, stream_size, force_FAT=True).read()
#[PL] Old code replaced by an array:
- #self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4))
+ #self.minifat = [i32(s, i) for i in range(0, len(s), 4)]
self.minifat = self.sect2array(s)
# Then shrink the array to used size, to avoid indexes out of MiniStream:
debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors))
@@ -1660,7 +1652,7 @@ class OleFileIO:
if kid.name.lower() == name.lower():
break
else:
- raise IOError, "file not found"
+ raise IOError("file not found")
node = kid
return node.sid
@@ -1680,7 +1672,7 @@ class OleFileIO:
sid = self._find(filename)
entry = self.direntries[sid]
if entry.entry_type != STGTY_STREAM:
- raise IOError, "this file is not a stream"
+ raise IOError("this file is not a stream")
return self._open(entry.isectStart, entry.size)
@@ -1881,9 +1873,9 @@ class OleFileIO:
else:
# legacy code kept for backward compatibility: returns a
# number of seconds since Jan 1,1601
- value = value / 10000000L # seconds
+ value = value // 10000000 # seconds
elif type == VT_UI1: # 1-byte unsigned integer
- value = ord(s[offset+4])
+ value = i8(s[offset+4])
elif type == VT_CLSID:
value = _clsid(s[offset+4:offset+20])
elif type == VT_CF:
@@ -1972,16 +1964,15 @@ Options:
continue
ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT)
- print "-" * 68
- print filename
- print "-" * 68
+ print("-" * 68)
+ print(filename)
+ print("-" * 68)
ole.dumpdirectory()
for streamname in ole.listdir():
if streamname[-1][0] == "\005":
- print streamname, ": properties"
+ print(streamname, ": properties")
props = ole.getproperties(streamname, convert_time=True)
- props = props.items()
- props.sort()
+ props = sorted(props.items())
for k, v in props:
#[PL]: avoid to display too large or binary values:
if isinstance(v, basestring):
@@ -1993,7 +1984,7 @@ Options:
if chr(c) in v:
v = '(binary data)'
break
- print " ", k, v
+ print(" ", k, v)
if check_streams:
# Read all streams to check if there are errors:
@@ -2044,5 +2035,5 @@ Options:
print '- %s: %s' % (exctype.__name__, msg)
else:
print 'None'
-## except IOError, v:
-## print "***", "cannot read", file, "-", v
+## except IOError as v:
+## print("***", "cannot read", file, "-", v)
diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py
index 7309e17b3..88593bb9d 100644
--- a/PIL/PSDraw.py
+++ b/PIL/PSDraw.py
@@ -15,13 +15,18 @@
# See the README file for information on usage and redistribution.
#
-import EpsImagePlugin
-import string
+from __future__ import print_function
+
+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:
@@ -30,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"
@@ -44,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")
@@ -52,7 +57,13 @@ class PSDraw:
self.fp.flush()
def setfont(self, font, size):
- if not self.isofont.has_key(font):
+ """
+ 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" %\
(font, font))
@@ -61,23 +72,49 @@ class PSDraw:
self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font))
def setink(self, ink):
- print "*** NOT YET IMPLEMENTED ***"
+ """
+ .. 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):
- text = string.joinfields(string.splitfields(text, "("), "\\(")
- text = string.joinfields(string.splitfields(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/PaletteFile.py b/PIL/PaletteFile.py
index 3bbd91327..5627f7b86 100644
--- a/PIL/PaletteFile.py
+++ b/PIL/PaletteFile.py
@@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution.
#
-import string
+from PIL._binary import o8
##
# File handler for Teragon-style palette files.
@@ -24,20 +24,20 @@ class PaletteFile:
def __init__(self, fp):
- self.palette = map(lambda i: (i, i, i), range(256))
+ self.palette = [(i, i, i) for i in range(256)]
- while 1:
+ while True:
s = fp.readline()
if not s:
break
- if s[0] == "#":
+ if s[0:1] == b"#":
continue
if len(s) > 100:
- raise SyntaxError, "bad palette file"
+ raise SyntaxError("bad palette file")
- v = map(int, string.split(s))
+ v = [int(x) for x in s.split()]
try:
[i, r, g, b] = v
except ValueError:
@@ -45,9 +45,9 @@ class PaletteFile:
g = b = r
if 0 <= i <= 255:
- self.palette[i] = chr(r) + chr(g) + chr(b)
+ self.palette[i] = o8(r) + o8(g) + o8(b)
- self.palette = string.join(self.palette, "")
+ self.palette = b"".join(self.palette)
def getpalette(self):
diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py
index 785023130..89f42bffc 100644
--- a/PIL/PalmImagePlugin.py
+++ b/PIL/PalmImagePlugin.py
@@ -9,7 +9,7 @@
__version__ = "1.0"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
_Palm8BitColormapValues = (
( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ),
@@ -80,7 +80,7 @@ _Palm8BitColormapValues = (
# so build a prototype image to be used for palette resampling
def build_prototype_image():
image = Image.new("L", (1,len(_Palm8BitColormapValues),))
- image.putdata(range(len(_Palm8BitColormapValues)))
+ image.putdata(list(range(len(_Palm8BitColormapValues))))
palettedata = ()
for i in range(len(_Palm8BitColormapValues)):
palettedata = palettedata + _Palm8BitColormapValues[i]
@@ -107,8 +107,8 @@ _COMPRESSION_TYPES = {
"scanline": 0x00,
}
-def o16b(i):
- return chr(i>>8&255) + chr(i&255)
+o8 = _binary.o8
+o16b = _binary.o16be
#
# --------------------------------------------------------------------
@@ -127,7 +127,7 @@ def _save(im, fp, filename, check=0):
bpp = 8
version = 1
- elif im.mode == "L" and im.encoderinfo.has_key("bpp") and im.encoderinfo["bpp"] in (1, 2, 4):
+ elif im.mode == "L" and "bpp" in im.encoderinfo and im.encoderinfo["bpp"] in (1, 2, 4):
# this is 8-bit grayscale, so we shift it to get the high-order bits, and invert it because
# Palm does greyscale from white (0) to black (1)
@@ -138,7 +138,7 @@ def _save(im, fp, filename, check=0):
rawmode = "P;" + str(bpp)
version = 1
- elif im.mode == "L" and im.info.has_key("bpp") and im.info["bpp"] in (1, 2, 4):
+ elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4):
# here we assume that even though the inherent mode is 8-bit grayscale, only
# the lower bpp bits are significant. We invert them to match the Palm.
@@ -158,7 +158,7 @@ def _save(im, fp, filename, check=0):
else:
- raise IOError, "cannot write mode %s as Palm" % im.mode
+ raise IOError("cannot write mode %s as Palm" % im.mode)
if check:
return check
@@ -172,12 +172,12 @@ def _save(im, fp, filename, check=0):
cols = im.size[0]
rows = im.size[1]
- rowbytes = ((cols + (16/bpp - 1)) / (16 / bpp)) * 2;
+ rowbytes = ((cols + (16//bpp - 1)) / (16 // bpp)) * 2;
transparent_index = 0
compression_type = _COMPRESSION_TYPES["none"]
flags = 0;
- if im.mode == "P" and im.info.has_key("custom-colormap"):
+ if im.mode == "P" and "custom-colormap" in im.info:
flags = flags & _FLAGS["custom-colormap"]
colormapsize = 4 * 256 + 2;
colormapmode = im.palette.mode
@@ -185,17 +185,17 @@ def _save(im, fp, filename, check=0):
else:
colormapsize = 0
- if im.info.has_key("offset"):
- offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4;
+ if "offset" in im.info:
+ offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4;
else:
offset = 0
fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags))
- fp.write(chr(bpp))
- fp.write(chr(version))
+ fp.write(o8(bpp))
+ fp.write(o8(version))
fp.write(o16b(offset))
- fp.write(chr(transparent_index))
- fp.write(chr(compression_type))
+ fp.write(o8(transparent_index))
+ fp.write(o8(compression_type))
fp.write(o16b(0)) # reserved by Palm
# now write colormap if necessary
@@ -203,11 +203,11 @@ def _save(im, fp, filename, check=0):
if colormapsize > 0:
fp.write(o16b(256))
for i in range(256):
- fp.write(chr(i))
+ fp.write(o8(i))
if colormapmode == 'RGB':
- fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2]))
+ fp.write(o8(colormap[3 * i]) + o8(colormap[3 * i + 1]) + o8(colormap[3 * i + 2]))
elif colormapmode == 'RGBA':
- fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2]))
+ fp.write(o8(colormap[4 * i]) + o8(colormap[4 * i + 1]) + o8(colormap[4 * i + 2]))
# now convert data to raw form
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))])
diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py
index 07bd27e9a..70066e76b 100644
--- a/PIL/PcdImagePlugin.py
+++ b/PIL/PcdImagePlugin.py
@@ -18,7 +18,9 @@
__version__ = "0.1"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
+
+i8 = _binary.i8
##
# Image plugin for PhotoCD images. This plugin only reads the 768x512
@@ -36,10 +38,10 @@ class PcdImageFile(ImageFile.ImageFile):
self.fp.seek(2048)
s = self.fp.read(2048)
- if s[:4] != "PCD_":
- raise SyntaxError, "not a PCD file"
+ if s[:4] != b"PCD_":
+ raise SyntaxError("not a PCD file")
- orientation = ord(s[1538]) & 3
+ orientation = i8(s[1538]) & 3
if orientation == 1:
self.tile_post_rotate = 90 # hack
elif orientation == 3:
diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py
index bec39e3a0..c40d3986d 100644
--- a/PIL/PcfFontFile.py
+++ b/PIL/PcfFontFile.py
@@ -16,10 +16,9 @@
# See the README file for information on usage and redistribution.
#
-import Image
-import FontFile
-
-import string
+from PIL import Image
+from PIL import FontFile
+from PIL import _binary
# --------------------------------------------------------------------
# declarations
@@ -43,19 +42,14 @@ BYTES_PER_ROW = [
lambda bits: ((bits+63) >> 3) & ~7,
]
-
-def l16(c):
- return ord(c[0]) + (ord(c[1])<<8)
-def l32(c):
- return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
-
-def b16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-def b32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
+i8 = _binary.i8
+l16 = _binary.i16le
+l32 = _binary.i32le
+b16 = _binary.i16be
+b32 = _binary.i32be
def sz(s, o):
- return s[o:string.index(s, "\0", o)]
+ return s[o:s.index(b"\0", o)]
##
# Font file plugin for the X11 PCF format.
@@ -68,7 +62,7 @@ class PcfFontFile(FontFile.FontFile):
magic = l32(fp.read(4))
if magic != PCF_MAGIC:
- raise SyntaxError, "not a PCF file"
+ raise SyntaxError("not a PCF file")
FontFile.FontFile.__init__(self)
@@ -126,7 +120,7 @@ class PcfFontFile(FontFile.FontFile):
# read property description
p = []
for i in range(nprops):
- p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4))))
+ p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4))))
if nprops & 3:
fp.seek(4 - (nprops & 3), 1) # pad
@@ -155,11 +149,11 @@ class PcfFontFile(FontFile.FontFile):
# "compressed" metrics
for i in range(i16(fp.read(2))):
- left = ord(fp.read(1)) - 128
- right = ord(fp.read(1)) - 128
- width = ord(fp.read(1)) - 128
- ascent = ord(fp.read(1)) - 128
- descent = ord(fp.read(1)) - 128
+ left = i8(fp.read(1)) - 128
+ right = i8(fp.read(1)) - 128
+ width = i8(fp.read(1)) - 128
+ ascent = i8(fp.read(1)) - 128
+ descent = i8(fp.read(1)) - 128
xsize = right - left
ysize = ascent + descent
append(
@@ -198,7 +192,7 @@ class PcfFontFile(FontFile.FontFile):
nbitmaps = i32(fp.read(4))
if nbitmaps != len(metrics):
- raise IOError, "Wrong number of bitmaps"
+ raise IOError("Wrong number of bitmaps")
offsets = []
for i in range(nbitmaps):
@@ -226,7 +220,7 @@ class PcfFontFile(FontFile.FontFile):
x, y, l, r, w, a, d, f = metrics[i]
b, e = offsets[i], offsets[i+1]
bitmaps.append(
- Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x))
+ Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x))
)
return bitmaps
diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py
index 15012d51c..42dd9be0b 100644
--- a/PIL/PcxImagePlugin.py
+++ b/PIL/PcxImagePlugin.py
@@ -27,13 +27,14 @@
__version__ = "0.6"
-import Image, ImageFile, ImagePalette
+from PIL import Image, ImageFile, ImagePalette, _binary
-def i16(c,o):
- return ord(c[o]) + (ord(c[o+1])<<8)
+i8 = _binary.i8
+i16 = _binary.i16le
+o8 = _binary.o8
def _accept(prefix):
- return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5]
+ return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5]
##
# Image plugin for Paintbrush images.
@@ -48,17 +49,17 @@ class PcxImageFile(ImageFile.ImageFile):
# header
s = self.fp.read(128)
if not _accept(s):
- raise SyntaxError, "not a PCX file"
+ raise SyntaxError("not a PCX file")
# image
bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1
if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
- raise SyntaxError, "bad PCX image size"
+ raise SyntaxError("bad PCX image size")
# format
- version = ord(s[1])
- bits = ord(s[3])
- planes = ord(s[65])
+ version = i8(s[1])
+ bits = i8(s[3])
+ planes = i8(s[65])
stride = i16(s,66)
self.info["dpi"] = i16(s,12), i16(s,14)
@@ -76,10 +77,10 @@ class PcxImageFile(ImageFile.ImageFile):
# FIXME: hey, this doesn't work with the incremental loader !!!
self.fp.seek(-769, 2)
s = self.fp.read(769)
- if len(s) == 769 and ord(s[0]) == 12:
+ if len(s) == 769 and i8(s[0]) == 12:
# check if the palette is linear greyscale
for i in range(256):
- if s[i*3+1:i*3+4] != chr(i)*3:
+ if s[i*3+1:i*3+4] != o8(i)*3:
mode = rawmode = "P"
break
if mode == "P":
@@ -91,7 +92,7 @@ class PcxImageFile(ImageFile.ImageFile):
rawmode = "RGB;L"
else:
- raise IOError, "unknown PCX mode"
+ raise IOError("unknown PCX mode")
self.mode = mode
self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
@@ -111,21 +112,20 @@ SAVE = {
"RGB": (5, 8, 3, "RGB;L"),
}
-def o16(i):
- return chr(i&255) + chr(i>>8&255)
+o16 = _binary.o16le
def _save(im, fp, filename, check=0):
try:
version, bits, planes, rawmode = SAVE[im.mode]
except KeyError:
- raise ValueError, "Cannot save %s images as PCX" % im.mode
+ raise ValueError("Cannot save %s images as PCX" % im.mode)
if check:
return check
# bytes per plane
- stride = (im.size[0] * bits + 7) / 8
+ stride = (im.size[0] * bits + 7) // 8
# under windows, we could determine the current screen size with
# "Image.core.display_mode()[1]", but I think that's overkill...
@@ -136,11 +136,11 @@ def _save(im, fp, filename, check=0):
# PCX header
fp.write(
- chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) +
+ o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) +
o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) +
- o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) +
+ o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) +
o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) +
- chr(0)*54
+ b"\0"*54
)
assert fp.tell() == 128
@@ -150,13 +150,13 @@ def _save(im, fp, filename, check=0):
if im.mode == "P":
# colour palette
- fp.write(chr(12))
+ fp.write(o8(12))
fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes
elif im.mode == "L":
# greyscale palette
- fp.write(chr(12))
+ fp.write(o8(12))
for i in range(256):
- fp.write(chr(i)*3)
+ fp.write(o8(i)*3)
# --------------------------------------------------------------------
# registry
diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py
index 4f957765c..725f22ecf 100644
--- a/PIL/PdfImagePlugin.py
+++ b/PIL/PdfImagePlugin.py
@@ -22,8 +22,9 @@
__version__ = "0.4"
-import Image, ImageFile
-import StringIO
+from PIL import Image, ImageFile
+from PIL._binary import i8
+import io
#
@@ -60,6 +61,16 @@ def _save(im, fp, filename):
xref = [0]*(5+1) # placeholders
+ class TextWriter:
+ def __init__(self, fp):
+ self.fp = fp
+ def __getattr__(self, name):
+ return getattr(self.fp, name)
+ def write(self, value):
+ self.fp.write(value.encode('latin-1'))
+
+ fp = TextWriter(fp)
+
fp.write("%PDF-1.2\n")
fp.write("% created by PIL PDF driver " + __version__ + "\n")
@@ -90,11 +101,11 @@ def _save(im, fp, filename):
colorspace = "[ /Indexed /DeviceRGB 255 <"
palette = im.im.getpalette("RGB")
for i in range(256):
- r = ord(palette[i*3])
- g = ord(palette[i*3+1])
- b = ord(palette[i*3+2])
+ r = i8(palette[i*3])
+ g = i8(palette[i*3+1])
+ b = i8(palette[i*3+2])
colorspace = colorspace + "%02x%02x%02x " % (r, g, b)
- colorspace = colorspace + "> ]"
+ colorspace = colorspace + b"> ]"
procset = "/ImageI" # indexed color
elif im.mode == "RGB":
filter = "/DCTDecode"
@@ -127,7 +138,7 @@ def _save(im, fp, filename):
#
# image
- op = StringIO.StringIO()
+ op = io.BytesIO()
if filter == "/ASCIIHexDecode":
if bits == 1:
@@ -138,7 +149,7 @@ def _save(im, fp, filename):
im.putdata(data)
ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)])
elif filter == "/DCTDecode":
- ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)])
+ Image.SAVE["JPEG"](im, op, filename)
elif filter == "/FlateDecode":
ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)])
elif filter == "/RunLengthDecode":
@@ -158,7 +169,7 @@ def _save(im, fp, filename):
ColorSpace = colorspace)
fp.write("stream\n")
- fp.write(op.getvalue())
+ fp.fp.write(op.getvalue())
fp.write("\nendstream\n")
_endobj(fp)
@@ -178,15 +189,15 @@ def _save(im, fp, filename):
#
# page contents
- op = StringIO.StringIO()
+ op = TextWriter(io.BytesIO())
op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution)))
xref[5] = fp.tell()
- _obj(fp, 5, Length = len(op.getvalue()))
+ _obj(fp, 5, Length = len(op.fp.getvalue()))
fp.write("stream\n")
- fp.write(op.getvalue())
+ fp.fp.write(op.fp.getvalue())
fp.write("\nendstream\n")
_endobj(fp)
diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py
index d4c597b61..a4c9032dc 100644
--- a/PIL/PixarImagePlugin.py
+++ b/PIL/PixarImagePlugin.py
@@ -21,16 +21,13 @@
__version__ = "0.1"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
#
# helpers
-def i16(c):
- return ord(c[0]) + (ord(c[1])<<8)
-
-def i32(c):
- return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
+i16 = _binary.i16le
+i32 = _binary.i32le
##
# Image plugin for PIXAR raster images.
@@ -44,8 +41,8 @@ class PixarImageFile(ImageFile.ImageFile):
# assuming a 4-byte magic label (FIXME: add "_accept" hook)
s = self.fp.read(4)
- if s != "\200\350\000\000":
- raise SyntaxError, "not a PIXAR file"
+ if s != b"\200\350\000\000":
+ raise SyntaxError("not a PIXAR file")
# read rest of header
s = s + self.fp.read(508)
diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py
index 0ee8589a5..7028083d7 100644
--- a/PIL/PngImagePlugin.py
+++ b/PIL/PngImagePlugin.py
@@ -31,22 +31,23 @@
# See the README file for information on usage and redistribution.
#
+from __future__ import print_function
+
__version__ = "0.9"
-import re, string
+import re
-import Image, ImageFile, ImagePalette, zlib
+from PIL import Image, ImageFile, ImagePalette, _binary
+import zlib
+
+i8 = _binary.i8
+i16 = _binary.i16be
+i32 = _binary.i32be
+
+is_cid = re.compile(b"\w\w\w\w").match
-def i16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-def i32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
-
-is_cid = re.compile("\w\w\w\w").match
-
-
-_MAGIC = "\211PNG\r\n\032\n"
+_MAGIC = b"\211PNG\r\n\032\n"
_MODES = {
@@ -69,6 +70,8 @@ _MODES = {
}
+_simple_palette = re.compile(b'^\xff+\x00\xff*$')
+
# --------------------------------------------------------------------
# Support classes. Suitable for PNG and related formats like MNG etc.
@@ -96,7 +99,7 @@ class ChunkStream:
len = i32(s)
if not is_cid(cid):
- raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
+ raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
return cid, pos, len
@@ -111,8 +114,8 @@ class ChunkStream:
"Call the appropriate chunk handler"
if Image.DEBUG:
- print "STREAM", cid, pos, len
- return getattr(self, "chunk_" + cid)(pos, len)
+ print("STREAM", cid, pos, len)
+ return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len)
def crc(self, cid, data):
"Read and verify checksum"
@@ -120,22 +123,22 @@ class ChunkStream:
crc1 = Image.core.crc32(data, Image.core.crc32(cid))
crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
if crc1 != crc2:
- raise SyntaxError, "broken PNG file"\
- "(bad header checksum in %s)" % cid
+ raise SyntaxError("broken PNG file"\
+ "(bad header checksum in %s)" % cid)
def crc_skip(self, cid, data):
"Read checksum. Used if the C module is not present"
self.fp.read(4)
- def verify(self, endchunk = "IEND"):
+ def verify(self, endchunk = b"IEND"):
# Simple approach; just calculate checksum for all remaining
# blocks. Must be called directly after open.
cids = []
- while 1:
+ while True:
cid, pos, len = self.read()
if cid == endchunk:
break
@@ -157,11 +160,18 @@ class PngInfo:
self.chunks.append((cid, data))
def add_text(self, key, value, zip=0):
+ # The tEXt chunk stores latin-1 text
+ if not isinstance(key, bytes):
+ key = key.encode('latin-1', 'strict')
+
+ if not isinstance(value, bytes):
+ value = value.encode('latin-1', 'replace')
+
if zip:
import zlib
- self.add("zTXt", key + "\0\0" + zlib.compress(value))
+ self.add(b"zTXt", key + b"\0\0" + zlib.compress(value))
else:
- self.add("tEXt", key + "\0" + value)
+ self.add(b"tEXt", key + b"\0" + value)
# --------------------------------------------------------------------
# PNG image stream (IHDR/IEND)
@@ -189,11 +199,11 @@ class PngStream(ChunkStream):
# Null separator 1 byte (null character)
# Compression method 1 byte (0)
# Compressed profile n bytes (zlib with deflate compression)
- i = string.find(s, chr(0))
+ i = s.find(b"\0")
if Image.DEBUG:
- print "iCCP profile name", s[:i]
- print "Compression method", ord(s[i])
- comp_method = ord(s[i])
+ print("iCCP profile name", s[:i])
+ print("Compression method", i8(s[i]))
+ comp_method = i8(s[i])
if comp_method != 0:
raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
try:
@@ -209,13 +219,13 @@ class PngStream(ChunkStream):
s = ImageFile._safe_read(self.fp, len)
self.im_size = i32(s), i32(s[4:])
try:
- self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))]
+ self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))]
except:
pass
- if ord(s[12]):
+ if i8(s[12]):
self.im_info["interlace"] = 1
- if ord(s[11]):
- raise SyntaxError, "unknown filter category"
+ if i8(s[11]):
+ raise SyntaxError("unknown filter category")
return s
def chunk_IDAT(self, pos, len):
@@ -243,9 +253,12 @@ class PngStream(ChunkStream):
# transparency
s = ImageFile._safe_read(self.fp, len)
if self.im_mode == "P":
- i = string.find(s, chr(0))
- if i >= 0:
- self.im_info["transparency"] = i
+ if _simple_palette.match(s):
+ i = s.find(b"\0")
+ if i >= 0:
+ self.im_info["transparency"] = i
+ else:
+ self.im_info["transparency"] = s
elif self.im_mode == "L":
self.im_info["transparency"] = i16(s)
elif self.im_mode == "RGB":
@@ -264,7 +277,7 @@ class PngStream(ChunkStream):
# pixels per unit
s = ImageFile._safe_read(self.fp, len)
px, py = i32(s), i32(s[4:])
- unit = ord(s[8])
+ unit = i8(s[8])
if unit == 1: # meter
dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
self.im_info["dpi"] = dpi
@@ -277,10 +290,14 @@ class PngStream(ChunkStream):
# text
s = ImageFile._safe_read(self.fp, len)
try:
- k, v = string.split(s, "\0", 1)
+ k, v = s.split(b"\0", 1)
except ValueError:
- k = s; v = "" # fallback for broken tEXt tags
+ k = s; v = b"" # fallback for broken tEXt tags
if k:
+ if bytes is not str:
+ k = k.decode('latin-1', 'strict')
+ v = v.decode('latin-1', 'replace')
+
self.im_info[k] = self.im_text[k] = v
return s
@@ -288,12 +305,28 @@ class PngStream(ChunkStream):
# compressed text
s = ImageFile._safe_read(self.fp, len)
- k, v = string.split(s, "\0", 1)
- comp_method = ord(v[0])
+ try:
+ k, v = s.split(b"\0", 1)
+ except ValueError:
+ k = s; v = b""
+ if v:
+ comp_method = i8(v[0])
+ else:
+ comp_method = 0
if comp_method != 0:
raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method)
import zlib
- self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:])
+ try:
+ v = zlib.decompress(v[1:])
+ except zlib.error:
+ v = b""
+
+ if k:
+ if bytes is not str:
+ k = k.decode('latin-1', 'strict')
+ v = v.decode('latin-1', 'replace')
+
+ self.im_info[k] = self.im_text[k] = v
return s
# --------------------------------------------------------------------
@@ -313,14 +346,14 @@ class PngImageFile(ImageFile.ImageFile):
def _open(self):
if self.fp.read(8) != _MAGIC:
- raise SyntaxError, "not a PNG file"
+ raise SyntaxError("not a PNG file")
#
# Parse headers up to the first IDAT chunk
self.png = PngStream(self.fp)
- while 1:
+ while True:
#
# get next chunk
@@ -333,7 +366,7 @@ class PngImageFile(ImageFile.ImageFile):
break
except AttributeError:
if Image.DEBUG:
- print cid, pos, len, "(unknown)"
+ print(cid, pos, len, "(unknown)")
s = ImageFile._safe_read(self.fp, len)
self.png.crc(cid, s)
@@ -390,9 +423,9 @@ class PngImageFile(ImageFile.ImageFile):
cid, pos, len = self.png.read()
- if cid not in ["IDAT", "DDAT"]:
+ if cid not in [b"IDAT", b"DDAT"]:
self.png.push(cid, pos, len)
- return ""
+ return b""
self.__idat = len # empty chunks are allowed
@@ -417,33 +450,31 @@ class PngImageFile(ImageFile.ImageFile):
# --------------------------------------------------------------------
# PNG writer
-def o16(i):
- return chr(i>>8&255) + chr(i&255)
-
-def o32(i):
- return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
+o8 = _binary.o8
+o16 = _binary.o16be
+o32 = _binary.o32be
_OUTMODES = {
# supported PIL modes, and corresponding rawmodes/bits/color combinations
- "1": ("1", chr(1)+chr(0)),
- "L;1": ("L;1", chr(1)+chr(0)),
- "L;2": ("L;2", chr(2)+chr(0)),
- "L;4": ("L;4", chr(4)+chr(0)),
- "L": ("L", chr(8)+chr(0)),
- "LA": ("LA", chr(8)+chr(4)),
- "I": ("I;16B", chr(16)+chr(0)),
- "P;1": ("P;1", chr(1)+chr(3)),
- "P;2": ("P;2", chr(2)+chr(3)),
- "P;4": ("P;4", chr(4)+chr(3)),
- "P": ("P", chr(8)+chr(3)),
- "RGB": ("RGB", chr(8)+chr(2)),
- "RGBA":("RGBA", chr(8)+chr(6)),
+ "1": ("1", b'\x01\x00'),
+ "L;1": ("L;1", b'\x01\x00'),
+ "L;2": ("L;2", b'\x02\x00'),
+ "L;4": ("L;4", b'\x04\x00'),
+ "L": ("L", b'\x08\x00'),
+ "LA": ("LA", b'\x08\x04'),
+ "I": ("I;16B", b'\x10\x00'),
+ "P;1": ("P;1", b'\x01\x03'),
+ "P;2": ("P;2", b'\x02\x03'),
+ "P;4": ("P;4", b'\x04\x03'),
+ "P": ("P", b'\x08\x03'),
+ "RGB": ("RGB", b'\x08\x02'),
+ "RGBA":("RGBA", b'\x08\x06'),
}
def putchunk(fp, cid, *data):
"Write a PNG chunk (including CRC field)"
- data = string.join(data, "")
+ data = b"".join(data)
fp.write(o32(len(data)) + cid)
fp.write(data)
@@ -457,7 +488,7 @@ class _idat:
self.fp = fp
self.chunk = chunk
def write(self, data):
- self.chunk(self.fp, "IDAT", data)
+ self.chunk(self.fp, b"IDAT", data)
def _save(im, fp, filename, chunk=putchunk, check=0):
# save an image to disk (called by the save method)
@@ -468,42 +499,43 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
#
# attempt to minimize storage requirements for palette images
-
- if im.encoderinfo.has_key("bits"):
-
+ if "bits" in im.encoderinfo:
# number of bits specified by user
- n = 1 << im.encoderinfo["bits"]
-
+ colors = 1 << im.encoderinfo["bits"]
else:
-
# check palette contents
- n = 256 # FIXME
+ if im.palette:
+ colors = len(im.palette.getdata()[1])//3
+ else:
+ colors = 256
- if n <= 2:
+ if colors <= 2:
bits = 1
- elif n <= 4:
+ elif colors <= 4:
bits = 2
- elif n <= 16:
+ elif colors <= 16:
bits = 4
else:
bits = 8
-
if bits != 8:
mode = "%s;%d" % (mode, bits)
# encoder options
- if im.encoderinfo.has_key("dictionary"):
+ if "dictionary" in im.encoderinfo:
dictionary = im.encoderinfo["dictionary"]
else:
- dictionary = ""
+ dictionary = b""
- im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
+ im.encoderconfig = ("optimize" in im.encoderinfo,
+ im.encoderinfo.get("compress_level", -1),
+ im.encoderinfo.get("compress_type", -1),
+ dictionary)
# get the corresponding PNG mode
try:
rawmode, mode = _OUTMODES[mode]
except KeyError:
- raise IOError, "cannot write mode %s as PNG" % mode
+ raise IOError("cannot write mode %s as PNG" % mode)
if check:
return check
@@ -513,39 +545,59 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
fp.write(_MAGIC)
- chunk(fp, "IHDR",
+ chunk(fp, b"IHDR",
o32(im.size[0]), o32(im.size[1]), # 0: size
mode, # 8: depth/type
- chr(0), # 10: compression
- chr(0), # 11: filter category
- chr(0)) # 12: interlace flag
+ b'\0', # 10: compression
+ b'\0', # 11: filter category
+ b'\0') # 12: interlace flag
if im.mode == "P":
- chunk(fp, "PLTE", im.im.getpalette("RGB"))
+ palette_byte_number = (2 ** bits) * 3
+ palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
+ while len(palette_bytes) < palette_byte_number:
+ palette_bytes += b'\0'
+ chunk(fp, b"PLTE", palette_bytes)
- if im.encoderinfo.has_key("transparency"):
+ transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None))
+
+ if transparency:
if im.mode == "P":
- transparency = max(0, min(255, im.encoderinfo["transparency"]))
- chunk(fp, "tRNS", chr(255) * transparency + chr(0))
+ # limit to actual palette size
+ alpha_bytes = 2**bits
+ if isinstance(transparency, bytes):
+ chunk(fp, b"tRNS", transparency[:alpha_bytes])
+ else:
+ transparency = max(0, min(255, transparency))
+ alpha = b'\xFF' * transparency + b'\0'
+ chunk(fp, b"tRNS", alpha[:alpha_bytes])
elif im.mode == "L":
- transparency = max(0, min(65535, im.encoderinfo["transparency"]))
- chunk(fp, "tRNS", o16(transparency))
+ transparency = max(0, min(65535, transparency))
+ chunk(fp, b"tRNS", o16(transparency))
elif im.mode == "RGB":
- red, green, blue = im.encoderinfo["transparency"]
- chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue))
+ red, green, blue = transparency
+ chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
else:
- raise IOError("cannot use transparency for this mode")
+ if "transparency" in im.encoderinfo:
+ # don't bother with transparency if it's an RGBA
+ # and it's in the info dict. It's probably just stale.
+ raise IOError("cannot use transparency for this mode")
+ else:
+ if im.mode == "P" and im.im.getpalettemode() == "RGBA":
+ alpha = im.im.getpalette("RGBA", "A")
+ alpha_bytes = 2**bits
+ chunk(fp, b"tRNS", alpha[:alpha_bytes])
if 0:
# FIXME: to be supported some day
- chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
+ chunk(fp, b"gAMA", o32(int(gamma * 100000.0)))
dpi = im.encoderinfo.get("dpi")
if dpi:
- chunk(fp, "pHYs",
+ chunk(fp, b"pHYs",
o32(int(dpi[0] / 0.0254 + 0.5)),
o32(int(dpi[1] / 0.0254 + 0.5)),
- chr(1))
+ b'\x01')
info = im.encoderinfo.get("pnginfo")
if info:
@@ -553,7 +605,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
chunk(fp, cid, data)
# ICC profile writing support -- 2008-06-06 Florian Hoech
- if im.info.has_key("icc_profile"):
+ if "icc_profile" in im.info:
# ICC profile
# according to PNG spec, the iCCP chunk contains:
# Profile name 1-79 bytes (character string)
@@ -565,13 +617,13 @@ def _save(im, fp, filename, chunk=putchunk, check=0):
p = ICCProfile.ICCProfile(im.info["icc_profile"])
name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79]
except ImportError:
- name = "ICC Profile"
- data = name + "\0\0" + zlib.compress(im.info["icc_profile"])
- chunk(fp, "iCCP", data)
+ name = b"ICC Profile"
+ data = name + b"\0\0" + zlib.compress(im.info["icc_profile"])
+ chunk(fp, b"iCCP", data)
ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
- chunk(fp, "IEND", "")
+ chunk(fp, b"IEND", b"")
try:
fp.flush()
@@ -593,7 +645,7 @@ def getchunks(im, **params):
self.data.append(chunk)
def append(fp, cid, *data):
- data = string.join(data, "")
+ data = b"".join(data)
hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
crc = o16(hi) + o16(lo)
fp.append((cid, data, crc))
diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py
index e86146c10..9aa5b1135 100644
--- a/PIL/PpmImagePlugin.py
+++ b/PIL/PpmImagePlugin.py
@@ -19,26 +19,36 @@ __version__ = "0.2"
import string
-import Image, ImageFile
+from PIL import Image, ImageFile
#
# --------------------------------------------------------------------
+b_whitespace = string.whitespace
+try:
+ import locale
+ locale_lang,locale_enc = locale.getlocale()
+ if locale_enc is None:
+ locale_lang,locale_enc = locale.getdefaultlocale()
+ b_whitespace = b_whitespace.decode(locale_enc)
+except: pass
+b_whitespace = b_whitespace.encode('ascii','ignore')
+
MODES = {
# standard
- "P4": "1",
- "P5": "L",
- "P6": "RGB",
+ b"P4": "1",
+ b"P5": "L",
+ b"P6": "RGB",
# extensions
- "P0CMYK": "CMYK",
+ b"P0CMYK": "CMYK",
# PIL extensions (for test purposes only)
- "PyP": "P",
- "PyRGBA": "RGBA",
- "PyCMYK": "CMYK"
+ b"PyP": "P",
+ b"PyRGBA": "RGBA",
+ b"PyCMYK": "CMYK"
}
def _accept(prefix):
- return prefix[0] == "P" and prefix[1] in "0456y"
+ return prefix[0:1] == b"P" and prefix[1] in b"0456y"
##
# Image plugin for PBM, PGM, and PPM images.
@@ -48,10 +58,10 @@ class PpmImageFile(ImageFile.ImageFile):
format = "PPM"
format_description = "Pbmplus image"
- def _token(self, s = ""):
- while 1: # read until next whitespace
+ def _token(self, s = b""):
+ while True: # read until next whitespace
c = self.fp.read(1)
- if not c or c in string.whitespace:
+ if not c or c in b_whitespace:
break
s = s + c
return s
@@ -60,8 +70,8 @@ class PpmImageFile(ImageFile.ImageFile):
# check magic
s = self.fp.read(1)
- if s != "P":
- raise SyntaxError, "not a PPM file"
+ if s != b"P":
+ raise SyntaxError("not a PPM file")
mode = MODES[self._token(s)]
if mode == "1":
@@ -71,12 +81,12 @@ class PpmImageFile(ImageFile.ImageFile):
self.mode = rawmode = mode
for ix in range(3):
- while 1:
- while 1:
+ while True:
+ while True:
s = self.fp.read(1)
- if s not in string.whitespace:
+ if s not in b_whitespace:
break
- if s != "#":
+ if s != b"#":
break
s = self.fp.readline()
s = int(self._token(s))
@@ -103,18 +113,18 @@ class PpmImageFile(ImageFile.ImageFile):
def _save(im, fp, filename):
if im.mode == "1":
- rawmode, head = "1;I", "P4"
+ rawmode, head = "1;I", b"P4"
elif im.mode == "L":
- rawmode, head = "L", "P5"
+ rawmode, head = "L", b"P5"
elif im.mode == "RGB":
- rawmode, head = "RGB", "P6"
+ rawmode, head = "RGB", b"P6"
elif im.mode == "RGBA":
- rawmode, head = "RGB", "P6"
+ rawmode, head = "RGB", b"P6"
else:
- raise IOError, "cannot write mode %s as PPM" % im.mode
- fp.write(head + "\n%d %d\n" % im.size)
- if head != "P4":
- fp.write("255\n")
+ raise IOError("cannot write mode %s as PPM" % im.mode)
+ fp.write(head + ("\n%d %d\n" % im.size).encode('ascii'))
+ if head != b"P4":
+ fp.write(b"255\n")
ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))])
# ALTERNATIVE: save via builtin debug function
diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py
index 9d1b17a36..f6aefe9c9 100644
--- a/PIL/PsdImagePlugin.py
+++ b/PIL/PsdImagePlugin.py
@@ -18,7 +18,7 @@
__version__ = "0.4"
-import Image, ImageFile, ImagePalette
+from PIL import Image, ImageFile, ImagePalette, _binary
MODES = {
# (photoshop mode, bits) -> (pil mode, required channels)
@@ -36,17 +36,15 @@ MODES = {
#
# helpers
-def i16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-
-def i32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
+i8 = _binary.i8
+i16 = _binary.i16be
+i32 = _binary.i32be
# --------------------------------------------------------------------.
# read PSD images
def _accept(prefix):
- return prefix[:4] == "8BPS"
+ return prefix[:4] == b"8BPS"
##
# Image plugin for Photoshop images.
@@ -64,8 +62,8 @@ class PsdImageFile(ImageFile.ImageFile):
# header
s = read(26)
- if s[:4] != "8BPS" or i16(s[4:]) != 1:
- raise SyntaxError, "not a PSD file"
+ if s[:4] != b"8BPS" or i16(s[4:]) != 1:
+ raise SyntaxError("not a PSD file")
psd_bits = i16(s[22:])
psd_channels = i16(s[12:])
@@ -74,7 +72,7 @@ class PsdImageFile(ImageFile.ImageFile):
mode, channels = MODES[(psd_mode, psd_bits)]
if channels > psd_channels:
- raise IOError, "not enough channels"
+ raise IOError("not enough channels")
self.mode = mode
self.size = i32(s[18:]), i32(s[14:])
@@ -100,7 +98,7 @@ class PsdImageFile(ImageFile.ImageFile):
while self.fp.tell() < end:
signature = read(4)
id = i16(read(2))
- name = read(ord(read(1)))
+ name = read(i8(read(1)))
if not (len(name) & 1):
read(1) # padding
data = read(i32(read(4)))
@@ -146,7 +144,7 @@ class PsdImageFile(ImageFile.ImageFile):
self.fp = self._fp
return name, bbox
except IndexError:
- raise EOFError, "no such layer"
+ raise EOFError("no such layer")
def tell(self):
# return layer number (0=image, 1..max=layers)
@@ -165,7 +163,6 @@ def _layerinfo(file):
# read layerinfo block
layers = []
read = file.read
-
for i in range(abs(i16(read(2)))):
# bounding box
@@ -175,12 +172,18 @@ def _layerinfo(file):
# image info
info = []
mode = []
- for i in range(i16(read(2))):
+ types = list(range(i16(read(2))))
+ if len(types) > 4:
+ continue
+
+ for i in types:
type = i16(read(2))
+
if type == 65535:
m = "A"
else:
- m = "RGB"[type]
+ m = "RGBA"[type]
+
mode.append(m)
size = i32(read(4))
info.append((m, size))
@@ -214,9 +217,10 @@ def _layerinfo(file):
file.seek(length, 1)
combined += length + 4
- length = ord(read(1))
+ length = i8(read(1))
if length:
- name = read(length)
+ # Don't know the proper encoding, Latin-1 should be a good guess
+ name = read(length).decode('latin-1', 'replace')
combined += length + 1
file.seek(size - combined, 1)
diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py
index c65b91aca..b60df473c 100644
--- a/PIL/SgiImagePlugin.py
+++ b/PIL/SgiImagePlugin.py
@@ -21,14 +21,11 @@
__version__ = "0.2"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
-
-def i16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-
-def i32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
+i8 = _binary.i8
+i16 = _binary.i16be
+i32 = _binary.i32be
def _accept(prefix):
@@ -50,10 +47,10 @@ class SgiImageFile(ImageFile.ImageFile):
raise SyntaxError("not an SGI image file")
# relevant header entries
- compression = ord(s[2])
+ compression = i8(s[2])
# bytes, dimension, zsize
- layout = ord(s[3]), i16(s[4:]), i16(s[10:])
+ layout = i8(s[3]), i16(s[4:]), i16(s[10:])
# determine mode from bytes/zsize
if layout == (1, 2, 1) or layout == (1, 1, 1):
diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py
index e73d55918..9e814932d 100644
--- a/PIL/SpiderImagePlugin.py
+++ b/PIL/SpiderImagePlugin.py
@@ -33,7 +33,9 @@
# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html
#
-import Image, ImageFile
+from __future__ import print_function
+
+from PIL import Image, ImageFile
import os, struct, sys
def isInt(f):
@@ -101,14 +103,14 @@ class SpiderImageFile(ImageFile.ImageFile):
t = struct.unpack('<27f',f) # little-endian
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
- raise SyntaxError, "not a valid Spider file"
+ raise SyntaxError("not a valid Spider file")
except struct.error:
- raise SyntaxError, "not a valid Spider file"
+ raise SyntaxError("not a valid Spider file")
h = (99,) + t # add 1 value : spider header index starts at 1
iform = int(h[5])
if iform != 1:
- raise SyntaxError, "not a Spider 2D image"
+ raise SyntaxError("not a Spider 2D image")
self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
self.istack = int(h[24])
@@ -131,7 +133,7 @@ class SpiderImageFile(ImageFile.ImageFile):
offset = hdrlen + self.stkoffset
self.istack = 2 # So Image knows it's still a stack
else:
- raise SyntaxError, "inconsistent stack header values"
+ raise SyntaxError("inconsistent stack header values")
if self.bigendian:
self.rawmode = "F;32BF"
@@ -154,7 +156,7 @@ class SpiderImageFile(ImageFile.ImageFile):
if self.istack == 0:
return
if frame >= self.nimages:
- raise EOFError, "attempt to seek past end of file"
+ raise EOFError("attempt to seek past end of file")
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self.__fp
self.fp.seek(self.stkoffset)
@@ -171,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile):
# returns a ImageTk.PhotoImage object, after rescaling to 0..255
def tkPhotoImage(self):
- import ImageTk
+ from PIL import ImageTk
return ImageTk.PhotoImage(self.convert2byte(), palette=256)
# --------------------------------------------------------------------
@@ -186,13 +188,13 @@ def loadImageSeries(filelist=None):
imglist = []
for img in filelist:
if not os.path.exists(img):
- print "unable to find %s" % img
+ print("unable to find %s" % img)
continue
try:
im = Image.open(img).convert2byte()
except:
if not isSpiderImage(img):
- print img + " is not a Spider image file"
+ print(img + " is not a Spider image file")
continue
im.info['filename'] = img
imglist.append(im)
@@ -239,13 +241,13 @@ def _save(im, fp, filename):
hdr = makeSpiderHeader(im)
if len(hdr) < 256:
- raise IOError, "Error creating Spider header"
+ raise IOError("Error creating Spider header")
# write the SPIDER header
try:
fp = open(filename, 'wb')
except:
- raise IOError, "Unable to open %s for writing" % filename
+ raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr)
rawmode = "F;32NF" #32-bit native floating point
@@ -267,12 +269,12 @@ Image.register_save("SPIDER", _save_spider)
if __name__ == "__main__":
if not sys.argv[1:]:
- print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]"
+ print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]")
sys.exit()
filename = sys.argv[1]
if not isSpiderImage(filename):
- print "input image must be in Spider format"
+ print("input image must be in Spider format")
sys.exit()
outfile = ""
@@ -280,15 +282,15 @@ if __name__ == "__main__":
outfile = sys.argv[2]
im = Image.open(filename)
- print "image: " + str(im)
- print "format: " + str(im.format)
- print "size: " + str(im.size)
- print "mode: " + str(im.mode)
- print "max, min: ",
- print im.getextrema()
+ print("image: " + str(im))
+ print("format: " + str(im.format))
+ print("size: " + str(im.size))
+ print("mode: " + str(im.mode))
+ print("max, min: ", end=' ')
+ print(im.getextrema())
if outfile != "":
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
- print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile)
+ print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile))
im.save(outfile, "SPIDER")
diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py
index d189562a4..0db02ad25 100644
--- a/PIL/SunImagePlugin.py
+++ b/PIL/SunImagePlugin.py
@@ -20,14 +20,10 @@
__version__ = "0.3"
-import Image, ImageFile, ImagePalette
+from PIL import Image, ImageFile, ImagePalette, _binary
-
-def i16(c):
- return ord(c[1]) + (ord(c[0])<<8)
-
-def i32(c):
- return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
+i16 = _binary.i16be
+i32 = _binary.i32be
def _accept(prefix):
@@ -46,7 +42,7 @@ class SunImageFile(ImageFile.ImageFile):
# HEAD
s = self.fp.read(32)
if i32(s) != 0x59a66a95:
- raise SyntaxError, "not an SUN raster file"
+ raise SyntaxError("not an SUN raster file")
offset = 32
@@ -60,7 +56,7 @@ class SunImageFile(ImageFile.ImageFile):
elif depth == 24:
self.mode, rawmode = "RGB", "BGR"
else:
- raise SyntaxError, "unsupported mode"
+ raise SyntaxError("unsupported mode")
compression = i32(s[20:24])
@@ -71,7 +67,7 @@ class SunImageFile(ImageFile.ImageFile):
if self.mode == "L":
self.mode = rawmode = "P"
- stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3)
+ stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)
if compression == 1:
self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))]
diff --git a/PIL/TarIO.py b/PIL/TarIO.py
index d5de729cd..bba493e8f 100644
--- a/PIL/TarIO.py
+++ b/PIL/TarIO.py
@@ -14,8 +14,7 @@
# See the README file for information on usage and redistribution.
#
-import ContainerIO
-import string
+from PIL import ContainerIO
##
# A file object that provides read access to a given member of a TAR
@@ -33,20 +32,20 @@ class TarIO(ContainerIO.ContainerIO):
fh = open(tarfile, "rb")
- while 1:
+ while True:
s = fh.read(512)
if len(s) != 512:
- raise IOError, "unexpected end of tar file"
+ raise IOError("unexpected end of tar file")
- name = s[:100]
- i = string.find(name, chr(0))
+ name = s[:100].decode('utf-8')
+ i = name.find('\0')
if i == 0:
- raise IOError, "cannot find subfile"
+ raise IOError("cannot find subfile")
if i > 0:
name = name[:i]
- size = string.atoi(s[124:136], 8)
+ size = int(s[124:135], 8)
if file == name:
break
diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py
index 3375991ca..55790db08 100644
--- a/PIL/TgaImagePlugin.py
+++ b/PIL/TgaImagePlugin.py
@@ -19,18 +19,16 @@
__version__ = "0.3"
-import Image, ImageFile, ImagePalette
+from PIL import Image, ImageFile, ImagePalette, _binary
#
# --------------------------------------------------------------------
# Read RGA file
-def i16(c):
- return ord(c[0]) + (ord(c[1])<<8)
-
-def i32(c):
- return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24)
+i8 = _binary.i8
+i16 = _binary.i16le
+i32 = _binary.i32le
MODES = {
@@ -45,7 +43,7 @@ MODES = {
def _accept(prefix):
- return prefix[0] == "\0"
+ return prefix[0:1] == b"\0"
##
# Image plugin for Targa files.
@@ -60,14 +58,14 @@ class TgaImageFile(ImageFile.ImageFile):
# process header
s = self.fp.read(18)
- id = ord(s[0])
+ id = i8(s[0])
- colormaptype = ord(s[1])
- imagetype = ord(s[2])
+ colormaptype = i8(s[1])
+ imagetype = i8(s[2])
- depth = ord(s[16])
+ depth = i8(s[16])
- flags = ord(s[17])
+ flags = i8(s[17])
self.size = i16(s[12:]), i16(s[14:])
@@ -75,7 +73,7 @@ class TgaImageFile(ImageFile.ImageFile):
if id != 0 or colormaptype not in (0, 1) or\
self.size[0] <= 0 or self.size[1] <= 0 or\
depth not in (1, 8, 16, 24, 32):
- raise SyntaxError, "not a TGA file"
+ raise SyntaxError("not a TGA file")
# image mode
if imagetype in (3, 11):
@@ -89,7 +87,7 @@ class TgaImageFile(ImageFile.ImageFile):
if depth == 32:
self.mode = "RGBA"
else:
- raise SyntaxError, "unknown TGA mode"
+ raise SyntaxError("unknown TGA mode")
# orientation
orientation = flags & 0x30
@@ -98,7 +96,7 @@ class TgaImageFile(ImageFile.ImageFile):
elif not orientation:
orientation = -1
else:
- raise SyntaxError, "unknown TGA orientation"
+ raise SyntaxError("unknown TGA orientation")
self.info["orientation"] = orientation
@@ -110,13 +108,13 @@ class TgaImageFile(ImageFile.ImageFile):
start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:])
if mapdepth == 16:
self.palette = ImagePalette.raw("BGR;16",
- "\0"*2*start + self.fp.read(2*size))
+ b"\0"*2*start + self.fp.read(2*size))
elif mapdepth == 24:
self.palette = ImagePalette.raw("BGR",
- "\0"*3*start + self.fp.read(3*size))
+ b"\0"*3*start + self.fp.read(3*size))
elif mapdepth == 32:
self.palette = ImagePalette.raw("BGRA",
- "\0"*4*start + self.fp.read(4*size))
+ b"\0"*4*start + self.fp.read(4*size))
# setup tile descriptor
try:
@@ -135,11 +133,9 @@ class TgaImageFile(ImageFile.ImageFile):
# --------------------------------------------------------------------
# Write TGA file
-def o16(i):
- return chr(i&255) + chr(i>>8&255)
-
-def o32(i):
- return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
+o8 = _binary.o8
+o16 = _binary.o16le
+o32 = _binary.o32le
SAVE = {
"1": ("1", 1, 0, 3),
@@ -173,18 +169,18 @@ def _save(im, fp, filename, check=0):
if orientation > 0:
flags = flags | 0x20
- fp.write("\000" +
- chr(colormaptype) +
- chr(imagetype) +
+ fp.write(b"\000" +
+ o8(colormaptype) +
+ o8(imagetype) +
o16(colormapfirst) +
o16(colormaplength) +
- chr(colormapentry) +
+ o8(colormapentry) +
o16(0) +
o16(0) +
o16(im.size[0]) +
o16(im.size[1]) +
- chr(bits) +
- chr(flags))
+ o8(bits) +
+ o8(flags))
if colormaptype:
fp.write(im.im.getpalette("RGB", "BGR"))
diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py
index 8a0fde9cd..3c62fd2dd 100644
--- a/PIL/TiffImagePlugin.py
+++ b/PIL/TiffImagePlugin.py
@@ -39,48 +39,49 @@
# See the README file for information on usage and redistribution.
#
+from __future__ import print_function
+
__version__ = "1.3.5"
-import Image, ImageFile
-import ImagePalette
+from PIL import Image, ImageFile
+from PIL import ImagePalette
+from PIL import _binary
+from PIL._util import isStringType
-import array, string, sys
+import warnings
+import array, sys
+import collections
+import itertools
+import os
-II = "II" # little-endian (intel-style)
-MM = "MM" # big-endian (motorola-style)
+# Set these to true to force use of libtiff for reading or writing.
+READ_LIBTIFF = False
+WRITE_LIBTIFF= False
-try:
- if sys.byteorder == "little":
- native_prefix = II
- else:
- native_prefix = MM
-except AttributeError:
- if ord(array.array("i",[1]).tostring()[0]):
- native_prefix = II
- else:
- native_prefix = MM
+II = b"II" # little-endian (intel-style)
+MM = b"MM" # big-endian (motorola-style)
+
+i8 = _binary.i8
+o8 = _binary.o8
+
+if sys.byteorder == "little":
+ native_prefix = II
+else:
+ native_prefix = MM
#
# --------------------------------------------------------------------
# Read TIFF files
-def il16(c,o=0):
- return ord(c[o]) + (ord(c[o+1])<<8)
-def il32(c,o=0):
- return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24)
-def ol16(i):
- return chr(i&255) + chr(i>>8&255)
-def ol32(i):
- return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255)
+il16 = _binary.i16le
+il32 = _binary.i32le
+ol16 = _binary.o16le
+ol32 = _binary.o32le
-def ib16(c,o=0):
- return ord(c[o+1]) + (ord(c[o])<<8)
-def ib32(c,o=0):
- return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24)
-def ob16(i):
- return chr(i>>8&255) + chr(i&255)
-def ob32(i):
- return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
+ib16 = _binary.i16be
+ib32 = _binary.i32be
+ob16 = _binary.o16be
+ob32 = _binary.o32be
# a few tag names, just to make the code below a bit more readable
IMAGEWIDTH = 256
@@ -114,6 +115,10 @@ ICCPROFILE = 34675
EXIFIFD = 34665
XMP = 700
+# https://github.com/fiji/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java
+IMAGEJ_META_DATA_BYTE_COUNTS = 50838
+IMAGEJ_META_DATA = 50839
+
COMPRESSION_INFO = {
# Compression => pil compression name
1: "raw",
@@ -123,10 +128,17 @@ COMPRESSION_INFO = {
5: "tiff_lzw",
6: "tiff_jpeg", # obsolete
7: "jpeg",
+ 8: "tiff_adobe_deflate",
32771: "tiff_raw_16", # 16-bit padding
- 32773: "packbits"
+ 32773: "packbits",
+ 32809: "tiff_thunderscan",
+ 32946: "tiff_deflate",
+ 34676: "tiff_sgilog",
+ 34677: "tiff_sgilog24",
}
+COMPRESSION_INFO_REV = dict([(v,k) for (k,v) in COMPRESSION_INFO.items()])
+
OPEN_INFO = {
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
# ExtraSamples) => mode, rawmode
@@ -139,12 +151,15 @@ OPEN_INFO = {
(II, 1, 1, 1, (8,), ()): ("L", "L"),
(II, 1, 1, 1, (8,8), (2,)): ("LA", "LA"),
(II, 1, 1, 2, (8,), ()): ("L", "L;R"),
+ (II, 1, 1, 1, (12,), ()): ("I;16", "I;12"),
(II, 1, 1, 1, (16,), ()): ("I;16", "I;16"),
(II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"),
+ (II, 1, 1, 1, (32,), ()): ("I", "I;32N"),
(II, 1, 2, 1, (32,), ()): ("I", "I;32S"),
(II, 1, 3, 1, (32,), ()): ("F", "F;32F"),
(II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"),
(II, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"),
+ (II, 2, 1, 1, (8,8,8,8), ()): ("RGBA", "RGBA"), # missing ExtraSamples
(II, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"),
(II, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"),
(II, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"),
@@ -174,7 +189,7 @@ OPEN_INFO = {
(MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"),
(MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"),
(MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"),
- (MM, 1, 3, 1, (32,), ()): ("F;32BF", "F;32BF"),
+ (MM, 1, 3, 1, (32,), ()): ("F", "F;32BF"),
(MM, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"),
(MM, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"),
(MM, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"),
@@ -196,7 +211,7 @@ OPEN_INFO = {
}
-PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"]
+PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"]
def _accept(prefix):
return prefix[:4] in PREFIXES
@@ -204,12 +219,46 @@ def _accept(prefix):
##
# Wrapper for TIFF IFDs.
-class ImageFileDirectory:
+class ImageFileDirectory(collections.MutableMapping):
+ """ This class represents a TIFF tag directory. To speed things
+ up, we don't decode tags unless they're asked for.
- # represents a TIFF tag directory. to speed things up,
- # we don't decode tags unless they're asked for.
+ Exposes a dictionary interface of the tags in the directory
+ ImageFileDirectory[key] = value
+ value = ImageFileDirectory[key]
- def __init__(self, prefix):
+ Also contains a dictionary of tag types as read from the tiff
+ image file, 'ImageFileDirectory.tagtype'
+
+
+ Data Structures:
+ 'public'
+ * self.tagtype = {} Key: numerical tiff tag number
+ Value: integer corresponding to the data type from
+ `TiffTags.TYPES`
+
+ 'internal'
+ * self.tags = {} Key: numerical tiff tag number
+ Value: Decoded data, Generally a tuple.
+ * If set from __setval__ -- always a tuple
+ * Numeric types -- always a tuple
+ * String type -- not a tuple, returned as string
+ * Undefined data -- not a tuple, returned as bytes
+ * Byte -- not a tuple, returned as byte.
+ * self.tagdata = {} Key: numerical tiff tag number
+ Value: undecoded byte string from file
+
+
+ Tags will be found in either self.tags or self.tagdata, but
+ not both. The union of the two should contain all the tags
+ from the Tiff image file. External classes shouldn't
+ reference these unless they're really sure what they're doing.
+ """
+
+ def __init__(self, prefix=II):
+ """
+ :prefix: 'II'|'MM' tiff endianness
+ """
self.prefix = prefix[:2]
if self.prefix == MM:
self.i16, self.i32 = ib16, ib32
@@ -222,21 +271,31 @@ class ImageFileDirectory:
self.reset()
def reset(self):
+ #: Tags is an incomplete dictionary of the tags of the image.
+ #: For a complete dictionary, use the as_dict method.
self.tags = {}
self.tagdata = {}
self.tagtype = {} # added 2008-06-05 by Florian Hoech
self.next = None
- # dictionary API (sort of)
+ def __str__(self):
+ return str(self.as_dict())
- def keys(self):
- return self.tagdata.keys() + self.tags.keys()
+ def as_dict(self):
+ """Return a dictionary of the image's tags."""
+ return dict(self.items())
- def items(self):
- items = self.tags.items()
- for tag in self.tagdata.keys():
- items.append((tag, self[tag]))
- return items
+ def named(self):
+ """Returns the complete tag dictionary, with named tags where posible."""
+ from PIL import TiffTags
+ result = {}
+ for tag_code, value in self.items():
+ tag_name = TiffTags.TAGS.get(tag_code, tag_code)
+ result[tag_name] = value
+ return result
+
+
+ # dictionary API
def __len__(self):
return len(self.tagdata) + len(self.tags)
@@ -245,18 +304,13 @@ class ImageFileDirectory:
try:
return self.tags[tag]
except KeyError:
- type, data = self.tagdata[tag] # unpack on the fly
+ data = self.tagdata[tag] # unpack on the fly
+ type = self.tagtype[tag]
size, handler = self.load_dispatch[type]
self.tags[tag] = data = handler(self, data)
del self.tagdata[tag]
return data
- def get(self, tag, default=None):
- try:
- return self[tag]
- except KeyError:
- return default
-
def getscalar(self, tag, default=None):
try:
value = self[tag]
@@ -265,36 +319,51 @@ class ImageFileDirectory:
# work around broken (?) matrox library
# (from Ted Wright, via Bob Klimek)
raise KeyError # use default
- raise ValueError, "not a scalar"
+ raise ValueError("not a scalar")
return value[0]
except KeyError:
if default is None:
raise
return default
- def has_key(self, tag):
- return self.tags.has_key(tag) or self.tagdata.has_key(tag)
+ def __contains__(self, tag):
+ return tag in self.tags or tag in self.tagdata
+
+ if bytes is str:
+ def has_key(self, tag):
+ return tag in self
def __setitem__(self, tag, value):
- if type(value) is not type(()):
+ # tags are tuples for integers
+ # tags are not tuples for byte, string, and undefined data.
+ # see load_*
+ if not isinstance(value, tuple):
value = (value,)
self.tags[tag] = value
+ def __delitem__(self, tag):
+ self.tags.pop(tag, self.tagdata.pop(tag, None))
+
+ def __iter__(self):
+ return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__())
+
+ def items(self):
+ keys = list(self.__iter__())
+ values = [self[key] for key in keys]
+ return zip(keys, values)
+
# load primitives
load_dispatch = {}
def load_byte(self, data):
- l = []
- for i in range(len(data)):
- l.append(ord(data[i]))
- return tuple(l)
+ return data
load_dispatch[1] = (1, load_byte)
def load_string(self, data):
- if data[-1:] == '\0':
+ if data[-1:] == b'\0':
data = data[:-1]
- return data
+ return data.decode('latin-1', 'replace')
load_dispatch[2] = (1, load_string)
def load_short(self, data):
@@ -352,17 +421,17 @@ class ImageFileDirectory:
tag, typ = i16(ifd), i16(ifd, 2)
if Image.DEBUG:
- import TiffTags
+ from PIL import TiffTags
tagname = TiffTags.TAGS.get(tag, "unknown")
typname = TiffTags.TYPES.get(typ, "unknown")
- print "tag: %s (%d)" % (tagname, tag),
- print "- type: %s (%d)" % (typname, typ),
+ print("tag: %s (%d)" % (tagname, tag), end=' ')
+ print("- type: %s (%d)" % (typname, typ), end=' ')
try:
dispatch = self.load_dispatch[typ]
except KeyError:
if Image.DEBUG:
- print "- unsupported type", typ
+ print("- unsupported type", typ)
continue # ignore unsupported type
size, handler = dispatch
@@ -379,16 +448,17 @@ class ImageFileDirectory:
data = ifd[8:8+size]
if len(data) != size:
- raise IOError, "not enough data"
+ warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" % (size, len(data), tag))
+ continue
- self.tagdata[tag] = typ, data
+ self.tagdata[tag] = data
self.tagtype[tag] = typ
if Image.DEBUG:
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
- print "- value: " % size
+ print("- value: " % size)
else:
- print "- value:", self[tag]
+ print("- value:", self[tag])
self.next = i32(fp.read(4))
@@ -402,8 +472,7 @@ class ImageFileDirectory:
fp.write(o16(len(self.tags)))
# always write in ascending tag order
- tags = self.tags.items()
- tags.sort()
+ tags = sorted(self.tags.items())
directory = []
append = directory.append
@@ -417,58 +486,76 @@ class ImageFileDirectory:
typ = None
- if self.tagtype.has_key(tag):
+ if tag in self.tagtype:
typ = self.tagtype[tag]
-
+
+ if Image.DEBUG:
+ print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value))
+
if typ == 1:
# byte data
- data = value = string.join(map(chr, value), "")
+ if isinstance(value, tuple):
+ data = value = value[-1]
+ else:
+ data = value
elif typ == 7:
# untyped data
- data = value = string.join(value, "")
- elif type(value[0]) is type(""):
+ data = value = b"".join(value)
+ elif isStringType(value[0]):
# string data
+ if isinstance(value, tuple):
+ value = value[-1]
typ = 2
- data = value = string.join(value, "\0") + "\0"
+ # was b'\0'.join(str), which led to \x00a\x00b sorts
+ # of strings which I don't see in in the wild tiffs
+ # and doesn't match the tiff spec: 8-bit byte that
+ # contains a 7-bit ASCII code; the last byte must be
+ # NUL (binary zero). Also, I don't think this was well
+ # excersized before.
+ data = value = b"" + value.encode('ascii', 'replace') + b"\0"
else:
# integer data
if tag == STRIPOFFSETS:
stripoffsets = len(directory)
typ = 4 # to avoid catch-22
- elif tag in (X_RESOLUTION, Y_RESOLUTION):
+ elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ==5:
# identify rational data fields
typ = 5
+ if isinstance(value[0], tuple):
+ # long name for flatten
+ value = tuple(itertools.chain.from_iterable(value))
elif not typ:
typ = 3
for v in value:
if v >= 65536:
typ = 4
if typ == 3:
- data = string.join(map(o16, value), "")
+ data = b"".join(map(o16, value))
else:
- data = string.join(map(o32, value), "")
+ data = b"".join(map(o32, value))
if Image.DEBUG:
- import TiffTags
+ from PIL import TiffTags
tagname = TiffTags.TAGS.get(tag, "unknown")
typname = TiffTags.TYPES.get(typ, "unknown")
- print "save: %s (%d)" % (tagname, tag),
- print "- type: %s (%d)" % (typname, typ),
+ print("save: %s (%d)" % (tagname, tag), end=' ')
+ print("- type: %s (%d)" % (typname, typ), end=' ')
if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
size = len(data)
- print "- value: " % size
+ print("- value: " % size)
else:
- print "- value:", value
+ print("- value:", value)
# figure out if data fits into the directory
if len(data) == 4:
- append((tag, typ, len(value), data, ""))
+ append((tag, typ, len(value), data, b""))
elif len(data) < 4:
- append((tag, typ, len(value), data + (4-len(data))*"\0", ""))
+ append((tag, typ, len(value), data + (4-len(data))*b"\0", b""))
else:
count = len(value)
if typ == 5:
- count = count / 2 # adjust for rational data field
+ count = count // 2 # adjust for rational data field
+
append((tag, typ, count, o32(offset), data))
offset = offset + len(data)
if offset & 1:
@@ -484,17 +571,17 @@ class ImageFileDirectory:
# pass 2: write directory to file
for tag, typ, count, value, data in directory:
if Image.DEBUG > 1:
- print tag, typ, count, repr(value), repr(data)
+ print(tag, typ, count, repr(value), repr(data))
fp.write(o16(tag) + o16(typ) + o32(count) + value)
# -- overwrite here for multi-page --
- fp.write("\0\0\0\0") # end of directory
+ fp.write(b"\0\0\0\0") # end of directory
# pass 3: write auxiliary data to file
for tag, typ, count, value, data in directory:
fp.write(data)
if len(data) & 1:
- fp.write("\0")
+ fp.write(b"\0")
return offset
@@ -513,7 +600,7 @@ class TiffImageFile(ImageFile.ImageFile):
ifh = self.fp.read(8)
if ifh[:4] not in PREFIXES:
- raise SyntaxError, "not a TIFF file"
+ raise SyntaxError("not a TIFF file")
# image file directory (tag dictionary)
self.tag = self.ifd = ImageFileDirectory(ifh[:2])
@@ -523,7 +610,12 @@ class TiffImageFile(ImageFile.ImageFile):
self.__frame = -1
self.__fp = self.fp
- # and load the first frame
+ if Image.DEBUG:
+ print ("*** TiffImageFile._open ***")
+ print ("- __first:", self.__first)
+ print ("- ifh: ", ifh)
+
+ # and load the first frame
self._seek(0)
def seek(self, frame):
@@ -547,7 +639,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.__next = self.__first
while self.__frame < frame:
if not self.__next:
- raise EOFError, "no more images in TIFF file"
+ raise EOFError("no more images in TIFF file")
self.fp.seek(self.__next)
self.tag.load(self.fp)
self.__next = self.tag.next
@@ -558,7 +650,7 @@ class TiffImageFile(ImageFile.ImageFile):
return self.__frame
- def _decoder(self, rawmode, layer):
+ def _decoder(self, rawmode, layer, tile=None):
"Setup decoder contexts"
args = None
@@ -569,27 +661,88 @@ class TiffImageFile(ImageFile.ImageFile):
args = (rawmode, 0, 1)
elif compression == "jpeg":
args = rawmode, ""
- if self.tag.has_key(JPEGTABLES):
+ if JPEGTABLES in self.tag:
# Hack to handle abbreviated JPEG headers
self.tile_prefix = self.tag[JPEGTABLES]
elif compression == "packbits":
args = rawmode
elif compression == "tiff_lzw":
args = rawmode
- if self.tag.has_key(317):
+ if 317 in self.tag:
# Section 14: Differencing Predictor
self.decoderconfig = (self.tag[PREDICTOR][0],)
- if self.tag.has_key(ICCPROFILE):
+ if ICCPROFILE in self.tag:
self.info['icc_profile'] = self.tag[ICCPROFILE]
return args
+ def _load_libtiff(self):
+ """ Overload method triggered when we detect a compressed tiff
+ Calls out to libtiff """
+
+ pixel = Image.Image.load(self)
+
+ if self.tile is None:
+ raise IOError("cannot load this image")
+ if not self.tile:
+ return pixel
+
+ self.load_prepare()
+
+ if not len(self.tile) == 1:
+ raise IOError("Not exactly one tile")
+
+ d, e, o, a = self.tile[0]
+ d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig)
+ try:
+ d.setimage(self.im, e)
+ except ValueError:
+ raise IOError("Couldn't set the image")
+
+ if hasattr(self.fp, "getvalue"):
+ # We've got a stringio like thing passed in. Yay for all in memory.
+ # The decoder needs the entire file in one shot, so there's not
+ # a lot we can do here other than give it the entire file.
+ # unless we could do something like get the address of the underlying
+ # string for stringio.
+ #
+ # Rearranging for supporting byteio items, since they have a fileno
+ # that returns an IOError if there's no underlying fp. Easier to deal
+ # with here by reordering.
+ if Image.DEBUG:
+ print ("have getvalue. just sending in a string from getvalue")
+ n,e = d.decode(self.fp.getvalue())
+ elif hasattr(self.fp, "fileno"):
+ # we've got a actual file on disk, pass in the fp.
+ if Image.DEBUG:
+ print ("have fileno, calling fileno version of the decoder.")
+ self.fp.seek(0)
+ n,e = d.decode(b"fpfp") # 4 bytes, otherwise the trace might error out
+ else:
+ # we have something else.
+ if Image.DEBUG:
+ print ("don't have fileno or getvalue. just reading")
+ # UNDONE -- so much for that buffer size thing.
+ n, e = d.decode(self.fp.read())
+
+
+ self.tile = []
+ self.readonly = 0
+ self.fp = None # might be shared
+
+ if e < 0:
+ raise IOError(e)
+
+ self.load_end()
+
+ return Image.Image.load(self)
+
def _setup(self):
"Setup this image object based on current tags"
- if self.tag.has_key(0xBC01):
- raise IOError, "Windows Media Photo files not yet supported"
+ if 0xBC01 in self.tag:
+ raise IOError("Windows Media Photo files not yet supported")
getscalar = self.tag.getscalar
@@ -604,11 +757,11 @@ class TiffImageFile(ImageFile.ImageFile):
fillorder = getscalar(FILLORDER, 1)
if Image.DEBUG:
- print "*** Summary ***"
- print "- compression:", self._compression
- print "- photometric_interpretation:", photo
- print "- planar_configuration:", self._planar_configuration
- print "- fill_order:", fillorder
+ print("*** Summary ***")
+ print("- compression:", self._compression)
+ print("- photometric_interpretation:", photo)
+ print("- planar_configuration:", self._planar_configuration)
+ print("- fill_order:", fillorder)
# size
xsize = getscalar(IMAGEWIDTH)
@@ -616,7 +769,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.size = xsize, ysize
if Image.DEBUG:
- print "- size:", self.size
+ print("- size:", self.size)
format = getscalar(SAMPLEFORMAT, 1)
@@ -627,23 +780,27 @@ class TiffImageFile(ImageFile.ImageFile):
self.tag.get(EXTRASAMPLES, ())
)
if Image.DEBUG:
- print "format key:", key
+ print("format key:", key)
try:
self.mode, rawmode = OPEN_INFO[key]
except KeyError:
if Image.DEBUG:
- print "- unsupported format"
- raise SyntaxError, "unknown pixel mode"
+ print("- unsupported format")
+ raise SyntaxError("unknown pixel mode")
if Image.DEBUG:
- print "- raw mode:", rawmode
- print "- pil mode:", self.mode
+ print("- raw mode:", rawmode)
+ print("- pil mode:", self.mode)
self.info["compression"] = self._compression
xres = getscalar(X_RESOLUTION, (1, 1))
yres = getscalar(Y_RESOLUTION, (1, 1))
+ if xres and not isinstance(xres, tuple):
+ xres = (xres, 1.)
+ if yres and not isinstance(yres, tuple):
+ yres = (yres, 1.)
if xres and yres:
xres = xres[0] / (xres[1] or 1)
yres = yres[0] / (yres[1] or 1)
@@ -658,24 +815,89 @@ class TiffImageFile(ImageFile.ImageFile):
# build tile descriptors
x = y = l = 0
self.tile = []
- if self.tag.has_key(STRIPOFFSETS):
+ if STRIPOFFSETS in self.tag:
# striped image
+ offsets = self.tag[STRIPOFFSETS]
h = getscalar(ROWSPERSTRIP, ysize)
w = self.size[0]
- a = None
- for o in self.tag[STRIPOFFSETS]:
- if not a:
- a = self._decoder(rawmode, l)
+ if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4",
+ "tiff_jpeg", "tiff_adobe_deflate",
+ "tiff_thunderscan", "tiff_deflate",
+ "tiff_sgilog", "tiff_sgilog24",
+ "tiff_raw_16"]:
+ ## if Image.DEBUG:
+ ## print "Activating g4 compression for whole file"
+
+ # Decoder expects entire file as one tile.
+ # There's a buffer size limit in load (64k)
+ # so large g4 images will fail if we use that
+ # function.
+ #
+ # Setup the one tile for the whole image, then
+ # replace the existing load function with our
+ # _load_libtiff function.
+
+ self.load = self._load_libtiff
+
+ # To be nice on memory footprint, if there's a
+ # file descriptor, use that instead of reading
+ # into a string in python.
+
+ # libtiff closes the file descriptor, so pass in a dup.
+ try:
+ fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
+ except IOError:
+ # io.BytesIO have a fileno, but returns an IOError if
+ # it doesn't use a file descriptor.
+ fp = False
+
+ # libtiff handles the fillmode for us, so 1;IR should
+ # actually be 1;I. Including the R double reverses the
+ # bits, so stripes of the image are reversed. See
+ # https://github.com/python-imaging/Pillow/issues/279
+ if fillorder == 2:
+ key = (
+ self.tag.prefix, photo, format, 1,
+ self.tag.get(BITSPERSAMPLE, (1,)),
+ self.tag.get(EXTRASAMPLES, ())
+ )
+ if Image.DEBUG:
+ print("format key:", key)
+ # this should always work, since all the
+ # 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') and 'I;16' in rawmode:
+ 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
+ a = (rawmode, self._compression, fp )
self.tile.append(
(self._compression,
- (0, min(y, ysize), w, min(y+h, ysize)),
- o, a))
- y = y + h
- if y >= self.size[1]:
- x = y = 0
- l = l + 1
+ (0, 0, w, ysize),
+ 0, a))
+ a = None
+
+ else:
+ for i in range(len(offsets)):
+ a = self._decoder(rawmode, l, i)
+ self.tile.append(
+ (self._compression,
+ (0, min(y, ysize), w, min(y+h, ysize)),
+ offsets[i], a))
+ if Image.DEBUG:
+ print ("tiles: ", self.tile)
+ y = y + h
+ if y >= self.size[1]:
+ x = y = 0
+ l = l + 1
a = None
- elif self.tag.has_key(TILEOFFSETS):
+ elif TILEOFFSETS in self.tag:
# tiled image
w = getscalar(322)
h = getscalar(323)
@@ -698,14 +920,14 @@ class TiffImageFile(ImageFile.ImageFile):
a = None
else:
if Image.DEBUG:
- print "- unsupported data organization"
+ print("- unsupported data organization")
raise SyntaxError("unknown data organization")
# fixup palette descriptor
if self.mode == "P":
- palette = map(lambda a: chr(a / 256), self.tag[COLORMAP])
- self.palette = ImagePalette.raw("RGB;L", string.join(palette, ""))
+ palette = [o8(a // 256) for a in self.tag[COLORMAP]]
+ self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
#
# --------------------------------------------------------------------
# Write TIFF files
@@ -738,10 +960,10 @@ SAVE_INFO = {
def _cvt_res(value):
# convert value to TIFF rational number -- (numerator, denominator)
- if type(value) in (type([]), type(())):
+ if isinstance(value, collections.Sequence):
assert(len(value) % 2 == 0)
return value
- if type(value) == type(1):
+ if isinstance(value, int):
return (value, 1)
value = float(value)
return (int(value * 65536), 65536)
@@ -751,12 +973,23 @@ def _save(im, fp, filename):
try:
rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode]
except KeyError:
- raise IOError, "cannot write mode %s as TIFF" % im.mode
+ raise IOError("cannot write mode %s as TIFF" % im.mode)
ifd = ImageFileDirectory(prefix)
+ compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
+
+ libtiff = WRITE_LIBTIFF or compression in ["tiff_ccitt", "group3", "group4",
+ "tiff_jpeg", "tiff_adobe_deflate",
+ "tiff_thunderscan", "tiff_deflate",
+ "tiff_sgilog", "tiff_sgilog24",
+ "tiff_raw_16"]
+
+ # required for color libtiff images
+ ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)
+
# -- multi-page -- skip TIFF header on subsequent pages
- if fp.tell() == 0:
+ if not libtiff and fp.tell() == 0:
# tiff header (write via IFD to get everything right)
# PIL always starts the first IFD at offset 8
fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8))
@@ -764,33 +997,44 @@ def _save(im, fp, filename):
ifd[IMAGEWIDTH] = im.size[0]
ifd[IMAGELENGTH] = im.size[1]
+ # write any arbitrary tags passed in as an ImageFileDirectory
+ info = im.encoderinfo.get("tiffinfo",{})
+ if Image.DEBUG:
+ print ("Tiffinfo Keys: %s"% info.keys)
+ keys = list(info.keys())
+ for key in keys:
+ ifd[key] = info.get(key)
+ try:
+ ifd.tagtype[key] = info.tagtype[key]
+ except:
+ pass # might not be an IFD, Might not have populated type
+
+
# additions written by Greg Couch, gregc@cgl.ucsf.edu
# inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com
if hasattr(im, 'tag'):
# preserve tags from original TIFF image file
- for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION):
- if im.tag.tagdata.has_key(key):
- ifd[key] = im.tag.tagdata.get(key)
- # preserve some more tags from original TIFF image file
- # -- 2008-06-06 Florian Hoech
- ifd.tagtype = im.tag.tagtype
- for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
- if im.tag.has_key(key):
+ for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION,
+ IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
+ if key in im.tag:
ifd[key] = im.tag[key]
+ ifd.tagtype[key] = im.tag.tagtype.get(key, None)
+
# preserve ICC profile (should also work when saving other formats
# which support profiles as TIFF) -- 2008-06-06 Florian Hoech
- if im.info.has_key("icc_profile"):
+ if "icc_profile" in im.info:
ifd[ICCPROFILE] = im.info["icc_profile"]
- if im.encoderinfo.has_key("description"):
+
+ if "description" in im.encoderinfo:
ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
- if im.encoderinfo.has_key("resolution"):
+ if "resolution" in im.encoderinfo:
ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
= _cvt_res(im.encoderinfo["resolution"])
- if im.encoderinfo.has_key("x resolution"):
+ if "x resolution" in im.encoderinfo:
ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
- if im.encoderinfo.has_key("y resolution"):
+ if "y resolution" in im.encoderinfo:
ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
- if im.encoderinfo.has_key("resolution unit"):
+ if "resolution unit" in im.encoderinfo:
unit = im.encoderinfo["resolution unit"]
if unit == "inch":
ifd[RESOLUTION_UNIT] = 2
@@ -798,13 +1042,13 @@ def _save(im, fp, filename):
ifd[RESOLUTION_UNIT] = 3
else:
ifd[RESOLUTION_UNIT] = 1
- if im.encoderinfo.has_key("software"):
+ if "software" in im.encoderinfo:
ifd[SOFTWARE] = im.encoderinfo["software"]
- if im.encoderinfo.has_key("date time"):
+ if "date time" in im.encoderinfo:
ifd[DATE_TIME] = im.encoderinfo["date time"]
- if im.encoderinfo.has_key("artist"):
+ if "artist" in im.encoderinfo:
ifd[ARTIST] = im.encoderinfo["artist"]
- if im.encoderinfo.has_key("copyright"):
+ if "copyright" in im.encoderinfo:
ifd[COPYRIGHT] = im.encoderinfo["copyright"]
dpi = im.encoderinfo.get("dpi")
@@ -826,24 +1070,96 @@ def _save(im, fp, filename):
if im.mode == "P":
lut = im.im.getpalette("RGB", "RGB;L")
- ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut))
+ ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)
# data orientation
- stride = len(bits) * ((im.size[0]*bits[0]+7)/8)
+ stride = len(bits) * ((im.size[0]*bits[0]+7)//8)
ifd[ROWSPERSTRIP] = im.size[1]
ifd[STRIPBYTECOUNTS] = stride * im.size[1]
ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer
- ifd[COMPRESSION] = 1 # no compression
+ ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default
- offset = ifd.save(fp)
+ if libtiff:
+ if Image.DEBUG:
+ print ("Saving using libtiff encoder")
+ print (ifd.items())
+ _fp = 0
+ if hasattr(fp, "fileno"):
+ fp.seek(0)
+ _fp = os.dup(fp.fileno())
- ImageFile._save(im, fp, [
- ("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
- ])
+ blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes.
+ atts={}
+ # bits per sample is a single short in the tiff directory, not a list.
+ atts[BITSPERSAMPLE] = bits[0]
+ # 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?
+ if type(v[0]) in (int, float):
+ atts[k] = list(v)
+ 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
+
+ if Image.DEBUG:
+ print (atts)
+
+ # libtiff always expects the bytes in native order.
+ # we're storing 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, 'libtiff', a, im.encoderconfig)
+ e.setimage(im.im, (0,0)+im.size)
+ while 1:
+ l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock
+ if not _fp:
+ fp.write(d)
+ if s:
+ break
+ if s < 0:
+ raise IOError("encoder error %d when writing image file" % s)
+
+ else:
+ offset = ifd.save(fp)
+
+ ImageFile._save(im, fp, [
+ ("raw", (0,0)+im.size, offset, (rawmode, stride, 1))
+ ])
# -- helper for multi-page save --
- if im.encoderinfo.has_key("_debug_multipage"):
+ if "_debug_multipage" in im.encoderinfo:
#just to access o32 and o16 (using correct byte order)
im._debug_multipage = ifd
diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py
index 33fd20941..9d4530051 100644
--- a/PIL/TiffTags.py
+++ b/PIL/TiffTags.py
@@ -186,6 +186,10 @@ TAGS = {
50738: "AntiAliasStrength",
50740: "DNGPrivateData",
50741: "MakerNoteSafety",
+
+ #ImageJ
+ 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe
+ 50839: "ImageJMetaData", # private tag registered with Adobe
}
##
diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py
index fb8c38ab4..a962e8a99 100644
--- a/PIL/WalImageFile.py
+++ b/PIL/WalImageFile.py
@@ -21,10 +21,17 @@
# http://www.flipcode.com/tutorials/tut_q2levels.shtml
# and has been tested with a few sample files found using google.
-import Image
+from __future__ import print_function
-def i32(c, o=0):
- return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)
+from PIL import Image, _binary
+
+try:
+ import builtins
+except ImportError:
+ import __builtin__
+ builtins = __builtin__
+
+i32 = _binary.i32le
##
# Load texture from a Quake2 WAL texture file.
@@ -42,8 +49,7 @@ def open(filename):
if hasattr(filename, "read"):
fp = filename
else:
- import __builtin__
- fp = __builtin__.open(filename, "rb")
+ fp = builtins.open(filename, "rb")
# read header fields
header = fp.read(32+24+32+12)
@@ -53,15 +59,15 @@ def open(filename):
# load pixel data
fp.seek(offset)
- im = Image.fromstring("P", size, fp.read(size[0] * size[1]))
+ im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
im.putpalette(quake2palette)
im.format = "WAL"
im.format_description = "Quake2 Texture"
# strings are null-terminated
- im.info["name"] = header[:32].split("\0", 1)[0]
- next_name = header[56:56+32].split("\0", 1)[0]
+ im.info["name"] = header[:32].split(b"\0", 1)[0]
+ next_name = header[56:56+32].split(b"\0", 1)[0]
if next_name:
im.info["next_name"] = next_name
@@ -70,57 +76,57 @@ def open(filename):
quake2palette = (
# default palette taken from piffo 0.93 by Hans Hggstrm
- "\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e"
- "\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f"
- "\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c"
- "\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b"
- "\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10"
- "\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07"
- "\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f"
- "\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16"
- "\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d"
- "\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31"
- "\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28"
- "\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07"
- "\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27"
- "\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b"
- "\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01"
- "\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21"
- "\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14"
- "\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07"
- "\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14"
- "\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f"
- "\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34"
- "\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d"
- "\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14"
- "\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01"
- "\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24"
- "\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10"
- "\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01"
- "\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27"
- "\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c"
- "\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a"
- "\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26"
- "\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d"
- "\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01"
- "\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20"
- "\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17"
- "\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07"
- "\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25"
- "\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c"
- "\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01"
- "\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23"
- "\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f"
- "\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b"
- "\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37"
- "\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b"
- "\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01"
- "\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10"
- "\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b"
- "\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20"
+ b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e"
+ b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f"
+ b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c"
+ b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b"
+ b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10"
+ b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07"
+ b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f"
+ b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16"
+ b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d"
+ b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31"
+ b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28"
+ b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07"
+ b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27"
+ b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b"
+ b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01"
+ b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21"
+ b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14"
+ b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07"
+ b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14"
+ b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f"
+ b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34"
+ b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d"
+ b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14"
+ b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01"
+ b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24"
+ b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10"
+ b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01"
+ b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27"
+ b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c"
+ b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a"
+ b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26"
+ b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d"
+ b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01"
+ b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20"
+ b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17"
+ b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07"
+ b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25"
+ b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c"
+ b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01"
+ b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23"
+ b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f"
+ b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b"
+ b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37"
+ b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b"
+ b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01"
+ b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10"
+ b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b"
+ b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20"
)
if __name__ == "__main__":
im = open("../hacks/sample.wal")
- print im.info, im.mode, im.size
+ print(im.info, im.mode, im.size)
im.save("../out.png")
diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py
new file mode 100644
index 000000000..ab60c8dfa
--- /dev/null
+++ b/PIL/WebPImagePlugin.py
@@ -0,0 +1,79 @@
+from PIL import Image
+from PIL import ImageFile
+from io import BytesIO
+from PIL import _webp
+
+
+_VALID_WEBP_MODES = {
+ "RGB": True,
+ "RGBA": True,
+ }
+
+_VP8_MODES_BY_IDENTIFIER = {
+ b"VP8 ": "RGB",
+ b"VP8X": "RGBA",
+ b"VP8L": "RGBA", # lossless
+ }
+
+
+def _accept(prefix):
+ is_riff_file_format = prefix[:4] == b"RIFF"
+ is_webp_file = prefix[8:12] == b"WEBP"
+ is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER
+
+ return is_riff_file_format and is_webp_file and is_valid_vp8_mode
+
+
+class WebPImageFile(ImageFile.ImageFile):
+
+ format = "WEBP"
+ format_description = "WebP image"
+
+ def _open(self):
+ data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode(self.fp.read())
+
+ if icc_profile:
+ self.info["icc_profile"] = icc_profile
+ if exif:
+ self.info["exif"] = exif
+
+ self.size = width, height
+ self.fp = BytesIO(data)
+ self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
+
+ def _getexif(self):
+ from PIL.JpegImagePlugin import _getexif
+ return _getexif(self)
+
+
+def _save(im, fp, filename):
+ image_mode = im.mode
+ if im.mode not in _VALID_WEBP_MODES:
+ raise IOError("cannot write mode %s as WEBP" % image_mode)
+
+ lossless = im.encoderinfo.get("lossless", False)
+ quality = im.encoderinfo.get("quality", 80)
+ icc_profile = im.encoderinfo.get("icc_profile", "")
+ exif = im.encoderinfo.get("exif", "")
+
+ data = _webp.WebPEncode(
+ im.tobytes(),
+ im.size[0],
+ im.size[1],
+ lossless,
+ float(quality),
+ im.mode,
+ icc_profile,
+ exif
+ )
+ if data is None:
+ raise IOError("cannot write file as WEBP (encoder returned None)")
+
+ fp.write(data)
+
+
+Image.register_open("WEBP", WebPImageFile, _accept)
+Image.register_save("WEBP", _save)
+
+Image.register_extension("WEBP", ".webp")
+Image.register_mime("WEBP", "image/webp")
diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py
index 5191f1e51..9a95a0713 100644
--- a/PIL/WmfImagePlugin.py
+++ b/PIL/WmfImagePlugin.py
@@ -17,10 +17,13 @@
__version__ = "0.2"
-import Image, ImageFile
+from PIL import Image, ImageFile, _binary
_handler = None
+if str != bytes:
+ long = int
+
##
# Install application-specific WMF image handler.
#
@@ -41,7 +44,7 @@ if hasattr(Image.core, "drawwmf"):
def load(self, im):
im.fp.seek(0) # rewind
- return Image.fromstring(
+ return Image.frombytes(
"RGB", im.size,
Image.core.drawwmf(im.fp.read(), im.size, self.bbox),
"raw", "BGR", (im.size[0]*3 + 3) & -4, -1
@@ -51,20 +54,15 @@ if hasattr(Image.core, "drawwmf"):
# --------------------------------------------------------------------
-def word(c, o=0):
- return ord(c[o]) + (ord(c[o+1])<<8)
+word = _binary.i16le
def short(c, o=0):
- v = ord(c[o]) + (ord(c[o+1])<<8)
+ v = word(c, o)
if v >= 32768:
v = v - 65536
return v
-def dword(c, o=0):
- return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24)
-
-def long(c, o=0):
- return dword(c, o)
+dword = _binary.i32le
#
# --------------------------------------------------------------------
@@ -72,8 +70,8 @@ def long(c, o=0):
def _accept(prefix):
return (
- prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or
- prefix[:4] == "\x01\x00\x00\x00"
+ prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or
+ prefix[:4] == b"\x01\x00\x00\x00"
)
##
@@ -89,7 +87,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# check placable header
s = self.fp.read(80)
- if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00":
+ if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00":
# placeable windows metafile
@@ -101,7 +99,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
x1 = short(s, 10); y1 = short(s, 12)
# normalize size to 72 dots per inch
- size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch
+ size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch
self.info["wmf_bbox"] = x0, y0, x1, y1
@@ -110,25 +108,25 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# print self.mode, self.size, self.info
# sanity check (standard metafile header)
- if s[22:26] != "\x01\x00\t\x00":
+ if s[22:26] != b"\x01\x00\t\x00":
raise SyntaxError("Unsupported WMF file format")
- elif long(s) == 1 and s[40:44] == " EMF":
+ elif dword(s) == 1 and s[40:44] == b" EMF":
# enhanced metafile
# get bounding box
- x0 = long(s, 8); y0 = long(s, 12)
- x1 = long(s, 16); y1 = long(s, 20)
+ x0 = dword(s, 8); y0 = dword(s, 12)
+ x1 = dword(s, 16); y1 = dword(s, 20)
# get frame (in 0.01 millimeter units)
- frame = long(s, 24), long(s, 28), long(s, 32), long(s, 36)
+ frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)
# normalize size to 72 dots per inch
size = x1 - x0, y1 - y0
# calculate dots per inch from bbox and frame
- xdpi = 2540 * (x1 - y0) / (frame[2] - frame[0])
- ydpi = 2540 * (y1 - y0) / (frame[3] - frame[1])
+ xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0])
+ ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1])
self.info["wmf_bbox"] = x0, y0, x1, y1
diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py
index ab1fb5d7a..e5bf55acf 100644
--- a/PIL/XVThumbImagePlugin.py
+++ b/PIL/XVThumbImagePlugin.py
@@ -19,15 +19,16 @@
__version__ = "0.1"
-import string
-import Image, ImageFile, ImagePalette
+from PIL import Image, ImageFile, ImagePalette, _binary
+
+o8 = _binary.o8
# standard color palette for thumbnails (RGB332)
-PALETTE = ""
+PALETTE = b""
for r in range(8):
for g in range(8):
for b in range(4):
- PALETTE = PALETTE + (chr((r*255)/7)+chr((g*255)/7)+chr((b*255)/3))
+ PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3))
##
# Image plugin for XV thumbnail images.
@@ -41,25 +42,25 @@ class XVThumbImageFile(ImageFile.ImageFile):
# check magic
s = self.fp.read(6)
- if s != "P7 332":
- raise SyntaxError, "not an XV thumbnail file"
+ if s != b"P7 332":
+ raise SyntaxError("not an XV thumbnail file")
# Skip to beginning of next line
self.fp.readline()
# skip info comments
- while 1:
+ while True:
s = self.fp.readline()
if not s:
- raise SyntaxError, "Unexpected EOF reading XV thumbnail file"
- if s[0] != '#':
+ raise SyntaxError("Unexpected EOF reading XV thumbnail file")
+ if s[0] != b'#':
break
# parse header line (already read)
- s = string.split(s.strip())
+ s = s.strip().split()
self.mode = "P"
- self.size = int(s[0]), int(s[1])
+ self.size = int(s[0:1]), int(s[1:2])
self.palette = ImagePalette.raw("RGB", PALETTE)
diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py
index a8cf1026d..799d727a0 100644
--- a/PIL/XbmImagePlugin.py
+++ b/PIL/XbmImagePlugin.py
@@ -21,22 +21,22 @@
__version__ = "0.6"
-import re, string
-import Image, ImageFile
+import re
+from PIL import Image, ImageFile
# XBM header
xbm_head = re.compile(
- "\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+"
- "#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+"
- "(?P"
- "#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+"
- "#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+"
- ")?"
- "[\\000-\\377]*_bits\\[\\]"
+ b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+"
+ b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+"
+ b"(?P"
+ b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+"
+ b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+"
+ b")?"
+ b"[\\000-\\377]*_bits\\[\\]"
)
def _accept(prefix):
- return string.lstrip(prefix)[:7] == "#define"
+ return prefix.lstrip()[:7] == b"#define"
##
# Image plugin for X11 bitmaps.
@@ -69,21 +69,21 @@ class XbmImageFile(ImageFile.ImageFile):
def _save(im, fp, filename):
if im.mode != "1":
- raise IOError, "cannot write mode %s as XBM" % im.mode
+ raise IOError("cannot write mode %s as XBM" % im.mode)
- fp.write("#define im_width %d\n" % im.size[0])
- fp.write("#define im_height %d\n" % im.size[1])
+ fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii'))
+ fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii'))
hotspot = im.encoderinfo.get("hotspot")
if hotspot:
- fp.write("#define im_x_hot %d\n" % hotspot[0])
- fp.write("#define im_y_hot %d\n" % hotspot[1])
+ fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii'))
+ fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii'))
- fp.write("static char im_bits[] = {\n")
+ fp.write(b"static char im_bits[] = {\n")
ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)])
- fp.write("};\n")
+ fp.write(b"};\n")
Image.register_open("XBM", XbmImageFile, _accept)
diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py
index a3f40f02c..701a23b64 100644
--- a/PIL/XpmImagePlugin.py
+++ b/PIL/XpmImagePlugin.py
@@ -18,15 +18,16 @@
__version__ = "0.2"
-import re, string
-import Image, ImageFile, ImagePalette
+import re
+from PIL import Image, ImageFile, ImagePalette
+from PIL._binary import i8, o8
# XPM header
-xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)")
+xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)")
def _accept(prefix):
- return prefix[:9] == "/* XPM */"
+ return prefix[:9] == b"/* XPM */"
##
# Image plugin for X11 pixel maps.
@@ -39,13 +40,13 @@ class XpmImageFile(ImageFile.ImageFile):
def _open(self):
if not _accept(self.fp.read(9)):
- raise SyntaxError, "not an XPM file"
+ raise SyntaxError("not an XPM file")
# skip forward to next string
- while 1:
+ while True:
s = self.fp.readline()
if not s:
- raise SyntaxError, "broken XPM file"
+ raise SyntaxError("broken XPM file")
m = xpm_head.match(s)
if m:
break
@@ -56,50 +57,50 @@ class XpmImageFile(ImageFile.ImageFile):
bpp = int(m.group(4))
if pal > 256 or bpp != 1:
- raise ValueError, "cannot read this XPM file"
+ raise ValueError("cannot read this XPM file")
#
# load palette description
- palette = ["\0\0\0"] * 256
+ palette = [b"\0\0\0"] * 256
for i in range(pal):
s = self.fp.readline()
- if s[-2:] == '\r\n':
+ if s[-2:] == b'\r\n':
s = s[:-2]
- elif s[-1:] in '\r\n':
+ elif s[-1:] in b'\r\n':
s = s[:-1]
- c = ord(s[1])
- s = string.split(s[2:-2])
+ c = i8(s[1])
+ s = s[2:-2].split()
for i in range(0, len(s), 2):
- if s[i] == "c":
+ if s[i] == b"c":
# process colour key
rgb = s[i+1]
- if rgb == "None":
+ if rgb == b"None":
self.info["transparency"] = c
- elif rgb[0] == "#":
+ elif rgb[0:1] == b"#":
# FIXME: handle colour names (see ImagePalette.py)
- rgb = string.atoi(rgb[1:], 16)
- palette[c] = chr((rgb >> 16) & 255) +\
- chr((rgb >> 8) & 255) +\
- chr(rgb & 255)
+ rgb = int(rgb[1:], 16)
+ palette[c] = o8((rgb >> 16) & 255) +\
+ o8((rgb >> 8) & 255) +\
+ o8(rgb & 255)
else:
# unknown colour
- raise ValueError, "cannot read this XPM file"
+ raise ValueError("cannot read this XPM file")
break
else:
# missing colour key
- raise ValueError, "cannot read this XPM file"
+ raise ValueError("cannot read this XPM file")
self.mode = "P"
- self.palette = ImagePalette.raw("RGB", string.join(palette, ""))
+ self.palette = ImagePalette.raw("RGB", b"".join(palette))
self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))]
@@ -113,11 +114,11 @@ class XpmImageFile(ImageFile.ImageFile):
s = [None] * ysize
for i in range(ysize):
- s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize)
+ s[i] = self.fp.readline()[1:xsize+1].ljust(xsize)
self.fp = None
- return string.join(s, "")
+ return b"".join(s)
#
# Registry
diff --git a/PIL/__init__.py b/PIL/__init__.py
index ed54d26f6..18bd42a5f 100644
--- a/PIL/__init__.py
+++ b/PIL/__init__.py
@@ -10,3 +10,48 @@
#
# ;-)
+
+VERSION = '1.1.7' # PIL version
+PILLOW_VERSION = '2.3.0' # Pillow
+
+_plugins = ['ArgImagePlugin',
+ 'BmpImagePlugin',
+ 'BufrStubImagePlugin',
+ 'CurImagePlugin',
+ 'DcxImagePlugin',
+ 'EpsImagePlugin',
+ 'FitsStubImagePlugin',
+ 'FliImagePlugin',
+ 'FpxImagePlugin',
+ 'GbrImagePlugin',
+ 'GifImagePlugin',
+ 'GribStubImagePlugin',
+ 'Hdf5StubImagePlugin',
+ 'IcnsImagePlugin',
+ 'IcoImagePlugin',
+ 'ImImagePlugin',
+ 'ImtImagePlugin',
+ 'IptcImagePlugin',
+ 'JpegImagePlugin',
+ 'McIdasImagePlugin',
+ 'MicImagePlugin',
+ 'MpegImagePlugin',
+ 'MspImagePlugin',
+ 'PalmImagePlugin',
+ 'PcdImagePlugin',
+ 'PcxImagePlugin',
+ 'PdfImagePlugin',
+ 'PixarImagePlugin',
+ 'PngImagePlugin',
+ 'PpmImagePlugin',
+ 'PsdImagePlugin',
+ 'SgiImagePlugin',
+ 'SpiderImagePlugin',
+ 'SunImagePlugin',
+ 'TgaImagePlugin',
+ 'TiffImagePlugin',
+ 'WebPImagePlugin',
+ 'WmfImagePlugin',
+ 'XbmImagePlugin',
+ 'XpmImagePlugin',
+ 'XVThumbImagePlugin']
diff --git a/PIL/_binary.py b/PIL/_binary.py
new file mode 100644
index 000000000..71b2b78c9
--- /dev/null
+++ b/PIL/_binary.py
@@ -0,0 +1,65 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Binary input/output support routines.
+#
+# Copyright (c) 1997-2003 by Secret Labs AB
+# Copyright (c) 1995-2003 by Fredrik Lundh
+# Copyright (c) 2012 by Brian Crowell
+#
+# See the README file for information on usage and redistribution.
+#
+
+if bytes is str:
+ def i8(c):
+ return ord(c)
+
+ def o8(i):
+ return chr(i&255)
+else:
+ def i8(c):
+ return c if c.__class__ is int else c[0]
+
+ def o8(i):
+ return bytes((i&255,))
+
+# Input, le = little endian, be = big endian
+#TODO: replace with more readable struct.unpack equivalent
+def i16le(c, o=0):
+ """
+ Converts a 2-bytes (16 bits) string to an integer.
+
+ c: string containing bytes to convert
+ o: offset of bytes to convert in string
+ """
+ return i8(c[o]) | (i8(c[o+1])<<8)
+
+def i32le(c, o=0):
+ """
+ Converts a 4-bytes (32 bits) string to an integer.
+
+ c: string containing bytes to convert
+ o: offset of bytes to convert in string
+ """
+ return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24)
+
+def i16be(c, o=0):
+ return (i8(c[o])<<8) | i8(c[o+1])
+
+def i32be(c, o=0):
+ return (i8(c[o])<<24) | (i8(c[o+1])<<16) | (i8(c[o+2])<<8) | i8(c[o+3])
+
+# Output, le = little endian, be = big endian
+def o16le(i):
+ return o8(i) + o8(i>>8)
+
+def o32le(i):
+ return o8(i) + o8(i>>8) + o8(i>>16) + o8(i>>24)
+
+def o16be(i):
+ return o8(i>>8) + o8(i)
+
+def o32be(i):
+ return o8(i>>24) + o8(i>>16) + o8(i>>8) + o8(i)
+
diff --git a/PIL/_util.py b/PIL/_util.py
new file mode 100644
index 000000000..220ac6c52
--- /dev/null
+++ b/PIL/_util.py
@@ -0,0 +1,16 @@
+import os
+
+if bytes is str:
+ def isStringType(t):
+ return isinstance(t, basestring)
+ def isPath(f):
+ return isinstance(f, basestring)
+else:
+ def isStringType(t):
+ return isinstance(t, str)
+ def isPath(f):
+ return isinstance(f, (bytes, str))
+
+# Checks if an object is a string, and that it points to a directory.
+def isDirectory(f):
+ return isPath(f) and os.path.isdir(f)
diff --git a/PIL/tests.py b/PIL/tests.py
new file mode 100644
index 000000000..eb4a8342d
--- /dev/null
+++ b/PIL/tests.py
@@ -0,0 +1,17 @@
+import unittest
+
+
+class PillowTests(unittest.TestCase):
+ """
+ Can we start moving the test suite here?
+ """
+
+ def test_suite_should_move_here(self):
+ """
+ Great idea!
+ """
+ assert True is True
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/PKG-INFO b/PKG-INFO
deleted file mode 100644
index c27993fb2..000000000
--- a/PKG-INFO
+++ /dev/null
@@ -1,18 +0,0 @@
-Metadata-Version: 1.0
-Name: PIL
-Version: 1.1.7
-Summary: Python Imaging Library
-Home-page: http://www.pythonware.com/products/pil
-Author: Secret Labs AB (PythonWare)
-Author-email: info@pythonware.com
-License: Python (MIT style)
-Download-URL: http://effbot.org/downloads/PIL-1.1.7.tar.gz
-Description: Python Imaging Library
-Platform: Python 1.5.2 and later.
-Classifier: Development Status :: 6 - Mature
-Classifier: Topic :: Multimedia :: Graphics
-Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera
-Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners
-Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
-Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
-Classifier: Topic :: Multimedia :: Graphics :: Viewers
diff --git a/README b/README
deleted file mode 100644
index 458975b3b..000000000
--- a/README
+++ /dev/null
@@ -1,300 +0,0 @@
-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/README.rst b/README.rst
new file mode 100644
index 000000000..78a256c0d
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,19 @@
+Pillow
+======
+
+*Python Imaging Library (Fork)*
+
+Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
+
+.. image:: https://travis-ci.org/python-imaging/Pillow.png
+ :target: https://travis-ci.org/python-imaging/Pillow
+
+.. 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
+
+The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more.
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 85882da91..000000000
--- a/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Pillow
-======
-
-Pillow is a fork of the Python Imaging Library
diff --git a/Sane/CHANGES b/Sane/CHANGES
index 95c14697e..47fb96cf1 100644
--- a/Sane/CHANGES
+++ b/Sane/CHANGES
@@ -12,7 +12,7 @@ _sane.c:
because it is only set when SANE_ACTION_SET_VALUE is used.
- scanDev.get_parameters() now returns the scanner mode as 'format',
no more the typical PIL codes. So 'L' became 'gray', 'RGB' is now
- 'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches
+ 'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches
the way scanDev.mode is set.
This should be the only incompatibility vs. version 1.0.
diff --git a/Sane/README b/Sane/README
index ddff0cf24..fa6c8a05f 100644
--- a/Sane/README
+++ b/Sane/README
@@ -3,13 +3,13 @@ Python SANE module V1.1 (30 Sep. 2004)
The SANE module provides an interface to the SANE scanner and frame
grabber interface for Linux. This module was contributed by Andrew
-Kuchling and is extended and currently maintained by Ralph Heinkel
+Kuchling and is extended and currently maintained by Ralph Heinkel
(rheinkel-at-email.de). If you write to me please make sure to have the
word 'SANE' or 'sane' in the subject of your mail, otherwise it might
be classified as spam in the future.
-To build this module, type (in the Sane directory):
+To build this module, type (in the Sane directory):
python setup.py build
diff --git a/Sane/_sane.c b/Sane/_sane.c
index 21e542fa5..1c62610be 100644
--- a/Sane/_sane.c
+++ b/Sane/_sane.c
@@ -7,10 +7,10 @@ documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of A.M. Kuchling and
-Ralph Heinkel not be used in advertising or publicity pertaining to
+Ralph Heinkel not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior permission.
-A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
@@ -28,6 +28,12 @@ PERFORMANCE OF THIS SOFTWARE.
#include
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_AsLong PyLong_AsLong
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_Check PyLong_Check
+#endif
+
static PyObject *ErrorObject;
typedef struct {
@@ -40,25 +46,29 @@ PyThreadState *_save;
#endif
/* Raise a SANE exception */
-PyObject *
+PyObject *
PySane_Error(SANE_Status st)
{
const char *string;
-
+
if (st==SANE_STATUS_GOOD) {Py_INCREF(Py_None); return (Py_None);}
string=sane_strstatus(st);
PyErr_SetString(ErrorObject, string);
return NULL;
}
-staticforward PyTypeObject SaneDev_Type;
+static PyTypeObject SaneDev_Type;
-#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type)
+#define SaneDevObject_Check(v) (Py_TYPE(v) == &SaneDev_Type)
static SaneDevObject *
newSaneDevObject(void)
{
SaneDevObject *self;
+
+ if (PyType_Ready(&SaneDev_Type) < 0)
+ return NULL;
+
self = PyObject_NEW(SaneDevObject, &SaneDev_Type);
if (self == NULL)
return NULL;
@@ -93,7 +103,7 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args)
SANE_Status st;
SANE_Parameters p;
char *format="unknown format";
-
+
if (!PyArg_ParseTuple(args, ""))
return NULL;
if (self->h==NULL)
@@ -104,7 +114,7 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args)
Py_BEGIN_ALLOW_THREADS
st=sane_get_parameters(self->h, &p);
Py_END_ALLOW_THREADS
-
+
if (st) return PySane_Error(st);
switch (p.format)
{
@@ -114,8 +124,8 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args)
case(SANE_FRAME_GREEN): format="green"; break;
case(SANE_FRAME_BLUE): format="blue"; break;
}
-
- return Py_BuildValue("si(ii)ii", format, p.last_frame, p.pixels_per_line,
+
+ return Py_BuildValue("si(ii)ii", format, p.last_frame, p.pixels_per_line,
p.lines, p.depth, p.bytes_per_line);
}
@@ -125,7 +135,7 @@ SaneDev_fileno(SaneDevObject *self, PyObject *args)
{
SANE_Status st;
SANE_Int fd;
-
+
if (!PyArg_ParseTuple(args, ""))
return NULL;
if (self->h==NULL)
@@ -142,7 +152,7 @@ static PyObject *
SaneDev_start(SaneDevObject *self, PyObject *args)
{
SANE_Status st;
-
+
if (!PyArg_ParseTuple(args, ""))
return NULL;
if (self->h==NULL)
@@ -184,7 +194,7 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args)
const SANE_Option_Descriptor *d;
PyObject *list, *value;
int i=1;
-
+
if (!PyArg_ParseTuple(args, ""))
return NULL;
if (self->h==NULL)
@@ -195,48 +205,52 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args)
if (!(list = PyList_New(0)))
return NULL;
- do
+ do
{
d=sane_get_option_descriptor(self->h, i);
- if (d!=NULL)
+ if (d!=NULL)
{
PyObject *constraint=NULL;
int j;
-
+
switch (d->constraint_type)
{
- case(SANE_CONSTRAINT_NONE):
+ case(SANE_CONSTRAINT_NONE):
Py_INCREF(Py_None); constraint=Py_None; break;
- case(SANE_CONSTRAINT_RANGE):
+ case(SANE_CONSTRAINT_RANGE):
if (d->type == SANE_TYPE_INT)
- constraint=Py_BuildValue("iii", d->constraint.range->min,
- d->constraint.range->max,
+ constraint=Py_BuildValue("iii", d->constraint.range->min,
+ d->constraint.range->max,
d->constraint.range->quant);
else
- constraint=Py_BuildValue("ddd",
- SANE_UNFIX(d->constraint.range->min),
- SANE_UNFIX(d->constraint.range->max),
+ constraint=Py_BuildValue("ddd",
+ SANE_UNFIX(d->constraint.range->min),
+ SANE_UNFIX(d->constraint.range->max),
SANE_UNFIX(d->constraint.range->quant));
break;
- case(SANE_CONSTRAINT_WORD_LIST):
+ case(SANE_CONSTRAINT_WORD_LIST):
constraint=PyList_New(d->constraint.word_list[0]);
if (d->type == SANE_TYPE_INT)
for (j=1; j<=d->constraint.word_list[0]; j++)
- PyList_SetItem(constraint, j-1,
+ PyList_SetItem(constraint, j-1,
PyInt_FromLong(d->constraint.word_list[j]));
else
for (j=1; j<=d->constraint.word_list[0]; j++)
- PyList_SetItem(constraint, j-1,
+ PyList_SetItem(constraint, j-1,
PyFloat_FromDouble(SANE_UNFIX(d->constraint.word_list[j])));
break;
- case(SANE_CONSTRAINT_STRING_LIST):
+ case(SANE_CONSTRAINT_STRING_LIST):
constraint=PyList_New(0);
for(j=0; d->constraint.string_list[j]!=NULL; j++)
- PyList_Append(constraint,
+ PyList_Append(constraint,
+#if PY_MAJOR_VERSION >= 3
+ PyUnicode_DecodeLatin1(d->constraint.string_list[j], strlen(d->constraint.string_list[j]), NULL));
+#else
PyString_FromString(d->constraint.string_list[j]));
+#endif
break;
}
- value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc,
+ value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc,
d->type, d->unit, d->size, d->cap, constraint);
PyList_Append(list, value);
}
@@ -253,7 +267,7 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args)
PyObject *value=NULL;
int n;
void *v;
-
+
if (!PyArg_ParseTuple(args, "i", &n))
{
return NULL;
@@ -268,12 +282,12 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args)
st=sane_control_option(self->h, n, SANE_ACTION_GET_VALUE,
v, NULL);
- if (st)
+ if (st)
{
- free(v);
+ free(v);
return PySane_Error(st);
}
-
+
switch(d->type)
{
case(SANE_TYPE_BOOL):
@@ -284,14 +298,18 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args)
value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) );
break;
case(SANE_TYPE_STRING):
+#if PY_MAJOR_VERSION >= 3
+ value=PyUnicode_DecodeLatin1((const char *) v, strlen((const char *) v), NULL);
+#else
value=Py_BuildValue("s", v);
+#endif
break;
case(SANE_TYPE_BUTTON):
case(SANE_TYPE_GROUP):
value=Py_BuildValue("O", Py_None);
break;
}
-
+
free(v);
return value;
}
@@ -305,7 +323,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
PyObject *value;
int n;
void *v;
-
+
if (!PyArg_ParseTuple(args, "iO", &n, &value))
return NULL;
if (self->h==NULL)
@@ -319,7 +337,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
switch(d->type)
{
case(SANE_TYPE_BOOL):
- if (!PyInt_Check(value))
+ if (!PyInt_Check(value))
{
PyErr_SetString(PyExc_TypeError, "SANE_BOOL requires an integer");
free(v);
@@ -327,7 +345,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
}
/* fall through */
case(SANE_TYPE_INT):
- if (!PyInt_Check(value))
+ if (!PyInt_Check(value))
{
PyErr_SetString(PyExc_TypeError, "SANE_INT requires an integer");
free(v);
@@ -336,7 +354,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
*( (SANE_Int*)v) = PyInt_AsLong(value);
break;
case(SANE_TYPE_FIXED):
- if (!PyFloat_Check(value))
+ if (!PyFloat_Check(value))
{
PyErr_SetString(PyExc_TypeError, "SANE_FIXED requires a floating point number");
free(v);
@@ -345,7 +363,25 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
*( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value));
break;
case(SANE_TYPE_STRING):
- if (!PyString_Check(value))
+#if PY_MAJOR_VERSION >= 3
+ if (!PyUnicode_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string");
+ free(v);
+ return NULL;
+ }
+ {
+ PyObject *encoded = PyUnicode_AsLatin1String(value);
+
+ if (!encoded)
+ return NULL;
+
+ strncpy(v, PyBytes_AsString(encoded), d->size-1);
+ ((char*)v)[d->size-1] = 0;
+ Py_DECREF(encoded);
+ }
+#else
+ if (!PyString_Check(value))
{
PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string");
free(v);
@@ -353,16 +389,17 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args)
}
strncpy(v, PyString_AsString(value), d->size-1);
((char*)v)[d->size-1] = 0;
+#endif
break;
- case(SANE_TYPE_BUTTON):
+ case(SANE_TYPE_BUTTON):
case(SANE_TYPE_GROUP):
break;
}
-
+
st=sane_control_option(self->h, n, SANE_ACTION_SET_VALUE,
v, &i);
if (st) {free(v); return PySane_Error(st);}
-
+
free(v);
return Py_BuildValue("i", i);
}
@@ -371,10 +408,9 @@ static PyObject *
SaneDev_set_auto_option(SaneDevObject *self, PyObject *args)
{
SANE_Status st;
- const SANE_Option_Descriptor *d;
SANE_Int i;
int n;
-
+
if (!PyArg_ParseTuple(args, "i", &n))
return NULL;
if (self->h==NULL)
@@ -382,11 +418,10 @@ SaneDev_set_auto_option(SaneDevObject *self, PyObject *args)
PyErr_SetString(ErrorObject, "SaneDev object is closed");
return NULL;
}
- d=sane_get_option_descriptor(self->h, n);
st=sane_control_option(self->h, n, SANE_ACTION_SET_AUTO,
NULL, &i);
if (st) {return PySane_Error(st);}
-
+
return Py_BuildValue("i", i);
}
@@ -395,7 +430,7 @@ SaneDev_set_auto_option(SaneDevObject *self, PyObject *args)
static PyObject *
SaneDev_snap(SaneDevObject *self, PyObject *args)
{
- SANE_Status st;
+ SANE_Status st;
/* The buffer should be a multiple of 3 in size, so each sane_read
operation will return an integral number of RGB triples. */
SANE_Byte buffer[READSIZE]; /* XXX how big should the buffer be? */
@@ -405,16 +440,16 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
int px, py, remain, cplen, bufpos, padbytes;
long L;
char errmsg[80];
- union
+ union
{ char c[2];
INT16 i16;
- }
+ }
endian;
PyObject *pyNoCancel = NULL;
int noCancel = 0;
-
+
endian.i16 = 1;
-
+
if (!PyArg_ParseTuple(args, "l|O", &L, &pyNoCancel))
return NULL;
if (self->h==NULL)
@@ -423,7 +458,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
return NULL;
}
im=(Imaging)L;
-
+
if (pyNoCancel)
noCancel = PyObject_IsTrue(pyNoCancel);
@@ -435,14 +470,14 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
we need to call sane_get_parameters here, and we can create
the result Image object here.
*/
-
+
Py_UNBLOCK_THREADS
sane_get_parameters(self->h, &p);
if (p.format == SANE_FRAME_GRAY)
{
switch (p.depth)
{
- case 1:
+ case 1:
remain = p.bytes_per_line * im->ysize;
padbytes = p.bytes_per_line - (im->xsize+7)/8;
bufpos = 0;
@@ -468,7 +503,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
px = 0;
}
}
- st=sane_read(self->h, buffer,
+ st=sane_read(self->h, buffer,
remainh, buffer,
+ st=sane_read(self->h, buffer,
remainh, buffer,
+ st=sane_read(self->h, buffer,
remainysize;
padbytes = p.bytes_per_line - ((im->xsize+7)/8) * 3;
bufpos = 0;
@@ -586,7 +621,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
{
while (len <= 0 && st == SANE_STATUS_GOOD)
{
- st=sane_read(self->h, buffer,
+ st=sane_read(self->h, buffer,
remainxsize;
bufpos = endian.c[0];
@@ -654,9 +689,9 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
- we may have padding bytes at the end of a scan line
- the number of bytes read with sane_read may be smaller
than the number of pad bytes
- - the buffer may become empty after setting any of the
+ - the buffer may become empty after setting any of the
red/green/blue pixel values
-
+
*/
while (st != SANE_STATUS_EOF && py < im->ysize)
{
@@ -685,7 +720,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
len -= bufpos;
}
if (st == SANE_STATUS_EOF) break;
- ((UINT8**)(im->image32))[py][px++] = buffer[bufpos];
+ ((UINT8**)(im->image32))[py][px++] = buffer[bufpos];
bufpos += incr;
len -= incr;
}
@@ -709,13 +744,13 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
PyErr_SetString(ErrorObject, errmsg);
return NULL;
}
-
+
}
else /* should be SANE_FRAME_RED, GREEN or BLUE */
{
int lastlen, pxa, pxmax, offset, incr, frame_count = 0;
- /* at least the Sane test backend behaves a bit weird, if
- it returns "premature EOF" for sane_read, i.e., if the
+ /* at least the Sane test backend behaves a bit weird, if
+ it returns "premature EOF" for sane_read, i.e., if the
option "return value of sane_read" is set to SANE_STATUS_EOF.
In this case, the test backend does not advance to the next frame,
so p.last_frame will never be set...
@@ -737,7 +772,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
lastlen = 0;
py = 0;
switch (p.format)
- {
+ {
case SANE_FRAME_RED:
offset = 0;
break;
@@ -771,14 +806,14 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
mask = 0x80;
for (bit = 0; bit < 8 && px < pxmax; bit++)
{
- ((UINT8**)(im->image32))[py][px]
+ ((UINT8**)(im->image32))[py][px]
= val&mask ? 0xFF : 0;
((UINT8**)(im->image32))[py][pxa] = 0;
px += 4;
pxa += 4;
mask = mask >> 1;
}
-
+
if (px >= pxmax)
{
px = offset;
@@ -891,9 +926,9 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
sane_cancel(self->h);
return PySane_Error(st);
}
-
+
st = sane_start(self->h);
- if (st)
+ if (st)
{
Py_BLOCK_THREADS
return PySane_Error(st);
@@ -912,7 +947,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args)
Py_BLOCK_THREADS
return PySane_Error(st);
}
-
+
if (!noCancel)
sane_cancel(self->h);
Py_BLOCK_THREADS
@@ -932,7 +967,7 @@ int NUMARRAY_IMPORTED = 0;
static PyObject *
SaneDev_arr_snap(SaneDevObject *self, PyObject *args)
{
- SANE_Status st;
+ SANE_Status st;
SANE_Byte buffer[READSIZE];
SANE_Int len;
SANE_Parameters p;
@@ -1000,24 +1035,24 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args)
PyErr_SetString(ErrorObject, "failed to create NumArray object");
return NULL;
}
-
+
arr_bytes_per_line = pixels_per_line * bpp;
st=SANE_STATUS_GOOD;
#ifdef WRITE_PGM
FILE *fp;
fp = fopen("sane_p5.pgm", "w");
- fprintf(fp, "P5\n%d %d\n%d\n", p.pixels_per_line,
+ fprintf(fp, "P5\n%d %d\n%d\n", p.pixels_per_line,
p.lines, (int) pow(2.0, (double) p.depth)-1);
#endif
line_index = line = 0;
remain_bytes_line = arr_bytes_per_line;
total_remain = p.bytes_per_line * p.lines;
num_pad_bytes = p.bytes_per_line - arr_bytes_per_line;
-
+
while (st!=SANE_STATUS_EOF)
{
Py_BEGIN_ALLOW_THREADS
- st = sane_read(self->h, buffer,
+ st = sane_read(self->h, buffer,
READSIZE < total_remain ? READSIZE : total_remain, &len);
Py_END_ALLOW_THREADS
#ifdef WRITE_PGM
@@ -1037,7 +1072,7 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args)
len -= cp_num_bytes;
#ifdef DEBUG
printf("copying %d bytes from b_idx %d to d_idx %d\n",
- cp_num_bytes, buffer_index,
+ cp_num_bytes, buffer_index,
line * arr_bytes_per_line + line_index);
printf("len is now %d\n", len);
#endif
@@ -1055,7 +1090,7 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args)
remain_bytes_line = arr_bytes_per_line;
line++;
line_index = 0;
- /* Skip the number of bytes in the input stream which
+ /* Skip the number of bytes in the input stream which
are not used: */
len -= num_pad_bytes;
buffer_index += num_pad_bytes;
@@ -1095,29 +1130,38 @@ static PyMethodDef SaneDev_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject *
-SaneDev_getattr(SaneDevObject *self, char *name)
-{
- return Py_FindMethod(SaneDev_methods, (PyObject *)self, name);
-}
-
-staticforward PyTypeObject SaneDev_Type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0, /*ob_size*/
+static PyTypeObject SaneDev_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
"SaneDev", /*tp_name*/
sizeof(SaneDevObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)SaneDev_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)SaneDev_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ SaneDev_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
/* --------------------------------------------------------------------- */
@@ -1127,12 +1171,12 @@ PySane_init(PyObject *self, PyObject *args)
{
SANE_Status st;
SANE_Int version;
-
+
if (!PyArg_ParseTuple(args, ""))
return NULL;
/* XXX Authorization is not yet supported */
- st=sane_init(&version, NULL);
+ st=sane_init(&version, NULL);
if (st) return PySane_Error(st);
return Py_BuildValue("iiii", version, SANE_VERSION_MAJOR(version),
SANE_VERSION_MINOR(version), SANE_VERSION_BUILD(version));
@@ -1152,28 +1196,30 @@ PySane_exit(PyObject *self, PyObject *args)
static PyObject *
PySane_get_devices(PyObject *self, PyObject *args)
{
- SANE_Device **devlist;
- SANE_Device *dev;
+ const SANE_Device **devlist;
+ const SANE_Device *dev;
SANE_Status st;
PyObject *list;
- int local_only, i;
-
+ int local_only = 0, i;
+
if (!PyArg_ParseTuple(args, "|i", &local_only))
{
return NULL;
}
-
+
+ Py_BEGIN_ALLOW_THREADS
st=sane_get_devices(&devlist, local_only);
+ Py_END_ALLOW_THREADS
if (st) return PySane_Error(st);
if (!(list = PyList_New(0)))
return NULL;
for(i=0; devlist[i]!=NULL; i++)
{
dev=devlist[i];
- PyList_Append(list, Py_BuildValue("ssss", dev->name, dev->vendor,
+ PyList_Append(list, Py_BuildValue("ssss", dev->name, dev->vendor,
dev->model, dev->type));
}
-
+
return list;
}
@@ -1191,8 +1237,10 @@ PySane_open(PyObject *self, PyObject *args)
rv = newSaneDevObject();
if ( rv == NULL )
return NULL;
+ Py_BEGIN_ALLOW_THREADS
st = sane_open(name, &(rv->h));
- if (st)
+ Py_END_ALLOW_THREADS
+ if (st)
{
Py_DECREF(rv);
return PySane_Error(st);
@@ -1205,7 +1253,7 @@ PySane_OPTION_IS_ACTIVE(PyObject *self, PyObject *args)
{
SANE_Int cap;
long lg;
-
+
if (!PyArg_ParseTuple(args, "l", &lg))
return NULL;
cap=lg;
@@ -1217,7 +1265,7 @@ PySane_OPTION_IS_SETTABLE(PyObject *self, PyObject *args)
{
SANE_Int cap;
long lg;
-
+
if (!PyArg_ParseTuple(args, "l", &lg))
return NULL;
cap=lg;
@@ -1248,17 +1296,40 @@ insint(PyObject *d, char *name, int value)
Py_DECREF(v);
}
-void
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef PySane_moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_sane",
+ NULL,
+ 0,
+ PySane_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit__sane(void)
+{
+ /* Create the module and add the functions */
+ PyObject *m = PyModule_Create(&PySane_moduledef);
+ if(!m)
+ return NULL;
+#else /* if PY_MAJOR_VERSION < 3 */
+
+PyMODINIT_FUNC
init_sane(void)
{
- PyObject *m, *d;
-
- /* Create the module and add the functions */
- m = Py_InitModule("_sane", PySane_methods);
+ /* Create the module and add the functions */
+ PyObject *m = Py_InitModule("_sane", PySane_methods);
+ if(!m)
+ return;
+#endif
/* Add some symbolic constants to the module */
- d = PyModule_GetDict(m);
- ErrorObject = PyString_FromString("_sane.error");
+ PyObject *d = PyModule_GetDict(m);
+ ErrorObject = PyErr_NewException("_sane.error", NULL, NULL);
PyDict_SetItemString(d, "error", ErrorObject);
insint(d, "INFO_INEXACT", SANE_INFO_INEXACT);
@@ -1306,7 +1377,7 @@ init_sane(void)
insint(d, "INFO_INEXACT", SANE_INFO_INEXACT);
insint(d, "INFO_RELOAD_OPTIONS", SANE_INFO_RELOAD_OPTIONS);
insint(d, "INFO_RELOAD_PARAMS", SANE_INFO_RELOAD_PARAMS);
-
+
/* Check for errors */
if (PyErr_Occurred())
Py_FatalError("can't initialize module _sane");
@@ -1316,10 +1387,13 @@ init_sane(void)
if (PyErr_Occurred())
PyErr_Clear();
else
- /* this global variable is declared just in front of the
+ /* this global variable is declared just in front of the
arr_snap() function and should be set to 1 after
successfully importing the numarray module. */
NUMARRAY_IMPORTED = 1;
#endif /* WITH_NUMARRAY */
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
}
diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py
index 0104af2d5..57fcc4407 100644
--- a/Sane/demo_numarray.py
+++ b/Sane/demo_numarray.py
@@ -4,6 +4,8 @@
# Shows how to scan a 16 bit grayscale image into a numarray object
#
+from __future__ import print_function
+
# Get the path set up to find PIL modules if not installed yet:
import sys ; sys.path.append('../PIL')
@@ -14,17 +16,17 @@ import Image
def toImage(arr):
if arr.type().bytes == 1:
# need to swap coordinates btw array and image (with [::-1])
- im = Image.fromstring('L', arr.shape[::-1], arr.tostring())
+ im = Image.frombytes('L', arr.shape[::-1], arr.tostring())
else:
arr_c = arr - arr.min()
arr_c *= (255./arr_c.max())
arr = arr_c.astype(UInt8)
# need to swap coordinates btw array and image (with [::-1])
- im = Image.fromstring('L', arr.shape[::-1], arr.tostring())
+ im = Image.frombytes('L', arr.shape[::-1], arr.tostring())
return im
-print 'SANE version:', sane.init()
-print 'Available devices=', sane.get_devices()
+print('SANE version:', sane.init())
+print('Available devices=', sane.get_devices())
s = sane.open(sane.get_devices()[0][0])
@@ -32,7 +34,7 @@ s = sane.open(sane.get_devices()[0][0])
s.mode = 'gray'
s.br_x=320. ; s.br_y=240.
-print 'Device parameters:', s.get_parameters()
+print('Device parameters:', s.get_parameters())
s.depth=16
arr16 = s.arr_scan()
diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py
index 016361f8a..490f33158 100644
--- a/Sane/demo_pil.py
+++ b/Sane/demo_pil.py
@@ -4,19 +4,21 @@
# Shows how to scan a color image into a PIL rgb-image
#
+from __future__ import print_function
+
# Get the path set up to find PIL modules if not installed yet:
import sys ; sys.path.append('../PIL')
import sane
-print 'SANE version:', sane.init()
-print 'Available devices=', sane.get_devices()
+print('SANE version:', sane.init())
+print('Available devices=', sane.get_devices())
s = sane.open(sane.get_devices()[0][0])
s.mode = 'color'
s.br_x=320. ; s.br_y=240.
-print 'Device parameters:', s.get_parameters()
+print('Device parameters:', s.get_parameters())
# Initiate the scan
s.start()
diff --git a/Sane/sane.py b/Sane/sane.py
index 27be5a259..331776f95 100644
--- a/Sane/sane.py
+++ b/Sane/sane.py
@@ -46,7 +46,6 @@ class Option:
"""
def __init__(self, args, scanDev):
- import string
self.scanDev = scanDev # needed to get current value of this option
self.index, self.name = args[0], args[1]
self.title, self.desc = args[2], args[3]
@@ -56,8 +55,8 @@ class Option:
def f(x):
if x=='-': return '_'
else: return x
- if type(self.name)!=type(''): self.py_name=str(self.name)
- else: self.py_name=string.join(map(f, self.name), '')
+ if not isinstance(self.name, str): self.py_name=str(self.name)
+ else: self.py_name=''.join(map(f, self.name))
def is_active(self):
return _sane.OPTION_IS_ACTIVE(self.cap)
@@ -86,7 +85,7 @@ active: %s
settable: %s\n""" % (self.py_name, curValue,
self.index, self.title, self.desc,
TYPE_STR[self.type], UNIT_STR[self.unit],
- `self.constraint`, active, settable)
+ repr(self.constraint), active, settable)
return s
@@ -106,7 +105,7 @@ class _SaneIterator:
def next(self):
try:
self.device.start()
- except error, v:
+ except error as v:
if v == 'Document feeder out of documents':
raise StopIteration
else:
@@ -166,16 +165,16 @@ class SaneDev:
def __setattr__(self, key, value):
dev=self.__dict__['dev']
optdict=self.__dict__['opt']
- if not optdict.has_key(key):
+ if key not in optdict:
self.__dict__[key]=value ; return
opt=optdict[key]
if opt.type==TYPE_GROUP:
- raise AttributeError, "Groups can't be set: "+key
+ raise AttributeError("Groups can't be set: "+key)
if not _sane.OPTION_IS_ACTIVE(opt.cap):
- raise AttributeError, 'Inactive option: '+key
+ raise AttributeError('Inactive option: '+key)
if not _sane.OPTION_IS_SETTABLE(opt.cap):
- raise AttributeError, "Option can't be set by software: "+key
- if type(value) == int and opt.type == TYPE_FIXED:
+ raise AttributeError("Option can't be set by software: "+key)
+ if isinstance(value, int) and opt.type == TYPE_FIXED:
# avoid annoying errors of backend if int is given instead float:
value = float(value)
self.last_opt = dev.set_option(opt.index, value)
@@ -187,18 +186,18 @@ class SaneDev:
dev=self.__dict__['dev']
optdict=self.__dict__['opt']
if key=='optlist':
- return self.opt.keys()
+ return list(self.opt.keys())
if key=='area':
return (self.tl_x, self.tl_y),(self.br_x, self.br_y)
- if not optdict.has_key(key):
- raise AttributeError, 'No such attribute: '+key
+ if key not in optdict:
+ raise AttributeError('No such attribute: '+key)
opt=optdict[key]
if opt.type==TYPE_BUTTON:
- raise AttributeError, "Buttons don't have values: "+key
+ raise AttributeError("Buttons don't have values: "+key)
if opt.type==TYPE_GROUP:
- raise AttributeError, "Groups don't have values: "+key
+ raise AttributeError("Groups don't have values: "+key)
if not _sane.OPTION_IS_ACTIVE(opt.cap):
- raise AttributeError, 'Inactive option: '+key
+ raise AttributeError('Inactive option: '+key)
value = dev.get_option(opt.index)
return value
diff --git a/Sane/sanedoc.txt b/Sane/sanedoc.txt
index db86938e3..f23000122 100644
--- a/Sane/sanedoc.txt
+++ b/Sane/sanedoc.txt
@@ -9,13 +9,13 @@ understanding.
This module has been originally developed by A.M. Kuchling (amk1@erols.com),
now development has been taken over by Ralph Heinkel (rheinkel-at-email.de).
-If you write to me please make sure to have the word 'SANE' or 'sane' in
+If you write to me please make sure to have the word 'SANE' or 'sane' in
the subject of your mail, otherwise it might be classified as spam in the
future.
The module exports two object types, a bunch of constants, and two
-functions.
+functions.
get_devices()
Return a list of 4-tuples containing the available scanning
@@ -73,7 +73,7 @@ get_parameters()
start()
Start a scan. This function must be called before the
_snap()_ method can be used.
-
+
cancel()
Cancel a scan already in progress.
@@ -81,7 +81,7 @@ snap(no_cancel=0)
Snap a single frame of data, returning a PIL Image object
containing the data. If no_cancel is false, the Sane library function
sane_cancel is called after the scan. This is reasonable in most cases,
- but may cause backends for duplex ADF scanners to drop the backside image,
+ but may cause backends for duplex ADF scanners to drop the backside image,
when snap() is called for the front side image. If no_cancel is true,
cancel() should be called manually, after all scans are finished.
@@ -90,13 +90,13 @@ scan()
Returns a PIL image
multi_scan()
- This method returns an iterator. It is intended to be used for
- scanning with an automatic document feeder. The next() method of the
+ This method returns an iterator. It is intended to be used for
+ scanning with an automatic document feeder. The next() method of the
iterator tries to start a scan. If this is successful, it returns a
- PIL Image object, like scan(); if the document feeder runs out of
+ PIL Image object, like scan(); if the document feeder runs out of
paper, it raises StopIteration, thereby signaling that the sequence
is ran out of items.
-
+
arr_snap(multipleOf=1)
same as snap, but the result is a NumArray object. (Not that
num_array must be installed already at compilation time, otherwise
@@ -123,14 +123,14 @@ Attributes:
SaneDev objects have a few fixed attributes which are always
available, and a larger collection of attributes which vary depending
on the device. An Epson 1660 photo scanner has attributes like
-'mode', 'depth', etc.
+'mode', 'depth', etc.
Another (pseudo scanner), the _pnm:0_ device, takes a PNM file and
simulates a scanner using the image data; a SaneDev object
representing the _pnm:0_ device therefore has a _filename_ attribute
which can be changed to specify the filename, _contrast_ and
_brightness_ attributes to modify the returned image, and so forth.
-The values of the scanner options may be an integer, floating-point
+The values of the scanner options may be an integer, floating-point
value, or string, depending on the nature of the option.
sane_signature
@@ -183,13 +183,13 @@ In order to change 'mode' to 'gray', just type:
>>> s.mode = 'gray'
-With the attributes and methods of sane-option objects it is possible
+With the attributes and methods of sane-option objects it is possible
to access individual option values:
-is_active()
+is_active()
Returns true if the option is active.
-is_settable()
+is_settable()
Returns true if the option can be set under software control.
@@ -216,21 +216,21 @@ index
An integer giving the option's index in the option list.
name
- A short name for the option, as it comes from the sane-backend.
+ A short name for the option, as it comes from the sane-backend.
-py_name
+py_name
The option's name, as a legal Python identifier. The name
attribute may contain the '-' character, so it will be converted to
'_' for the py_name attribute.
size
- For a string-valued option, this is the maximum length allowed.
+ For a string-valued option, this is the maximum length allowed.
title
A single-line string that can be used as a title string.
-
+
type
- A constant giving the type of this option: will be one of the following
+ A constant giving the type of this option: will be one of the following
constants found in the SANE module:
TYPE_BOOL
TYPE_INT
@@ -274,7 +274,7 @@ Device parameters: ('L', 1, (424, 585), 1, 53)
## In order to obtain a 16-bit grayscale image at 100DPI in a numarray object
## with bottom-right coordinates set to (160, 120) [in millimeter] :
>>> s.mode = 'gray'
->>> s.br_x=160. ; s.br_y=120.
+>>> s.br_x=160. ; s.br_y=120.
>>> s.resolution = 100
>>> s.depth=16
>>> s.start()
diff --git a/Scripts/README b/Scripts/README
index a09b0621a..befa7e7be 100644
--- a/Scripts/README
+++ b/Scripts/README
@@ -14,7 +14,7 @@ pildriver.py (by Eric S. Raymond)
A class implementing an image-processing calculator for scripts.
Parses lists of commnds (or, called interactively, command-line
-arguments) into image loads, transformations, and saves.
+arguments) into image loads, transformations, and saves.
--------------------------------------------------------------------
viewer.py
diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py
index 957b51c8d..fe250c9f8 100644
--- a/Scripts/enhancer.py
+++ b/Scripts/enhancer.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -6,7 +7,11 @@
# drag the slider to modify the image.
#
-from Tkinter import *
+try:
+ from tkinter import *
+except ImportError:
+ from Tkinter import *
+
from PIL import Image, ImageTk, ImageEnhance
import sys
diff --git a/Scripts/explode.py b/Scripts/explode.py
index a336f0699..90084a464 100644
--- a/Scripts/explode.py
+++ b/Scripts/explode.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -5,8 +6,10 @@
# split an animation into a number of frame files
#
+from __future__ import print_function
+
from PIL import Image
-import os, string, sys
+import os, sys
class Interval:
@@ -18,23 +21,23 @@ class Interval:
self.hilo = []
- for s in string.split(interval, ","):
- if not string.strip(s):
+ for s in interval.split(","):
+ if not s.strip():
continue
try:
- v = string.atoi(s)
+ v = int(s)
if v < 0:
lo, hi = 0, -v
else:
lo = hi = v
except ValueError:
- i = string.find(s, "-")
- lo, hi = string.atoi(s[:i]), string.atoi(s[i+1:])
+ i = s.find("-")
+ lo, hi = int(s[:i]), int(s[i+1:])
self.hilo.append((hi, lo))
if not self.hilo:
- self.hilo = [(sys.maxint, 0)]
+ self.hilo = [(sys.maxsize, 0)]
def __getitem__(self, index):
@@ -53,23 +56,23 @@ if sys.argv[1:2] == ["-h"]:
del sys.argv[1]
if not sys.argv[2:]:
- print
- print "Syntax: python explode.py infile template [range]"
- print
- print "The template argument is used to construct the names of the"
- print "individual frame files. The frames are numbered file001.ext,"
- print "file002.ext, etc. You can insert %d to control the placement"
- print "and syntax of the frame number."
- print
- print "The optional range argument specifies which frames to extract."
- print "You can give one or more ranges like 1-10, 5, -15 etc. If"
- print "omitted, all frames are extracted."
+ print()
+ print("Syntax: python explode.py infile template [range]")
+ print()
+ print("The template argument is used to construct the names of the")
+ print("individual frame files. The frames are numbered file001.ext,")
+ print("file002.ext, etc. You can insert %d to control the placement")
+ print("and syntax of the frame number.")
+ print()
+ print("The optional range argument specifies which frames to extract.")
+ print("You can give one or more ranges like 1-10, 5, -15 etc. If")
+ print("omitted, all frames are extracted.")
sys.exit(1)
infile = sys.argv[1]
outfile = sys.argv[2]
-frames = Interval(string.join(sys.argv[3:], ","))
+frames = Interval(",".join(sys.argv[3:]))
try:
# check if outfile contains a placeholder
@@ -87,11 +90,11 @@ if html:
html = open(file+".html", "w")
html.write("\n\n")
-while 1:
+while True:
if frames[ix]:
im.save(outfile % ix)
- print outfile % ix
+ print(outfile % ix)
if html:
html.write("
\n" % outfile % ix)
diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py
index 95524eacc..9964f77b1 100644
--- a/Scripts/gifmaker.py
+++ b/Scripts/gifmaker.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -39,8 +40,9 @@
# write data directly to a socket. Or something...
#
+from __future__ import print_function
+
from PIL import Image, ImageChops
-import string
from PIL.GifImagePlugin import getheader, getdata
@@ -128,8 +130,8 @@ if __name__ == "__main__":
import sys
if len(sys.argv) < 3:
- print "GIFMAKER -- create GIF animations"
- print "Usage: gifmaker infile outfile"
+ print("GIFMAKER -- create GIF animations")
+ print("Usage: gifmaker infile outfile")
sys.exit(1)
compress(sys.argv[1], sys.argv[2])
diff --git a/Scripts/painter.py b/Scripts/painter.py
index efe307386..80c4db6a0 100644
--- a/Scripts/painter.py
+++ b/Scripts/painter.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -8,7 +9,11 @@
# the image into a set of tiles.
#
-from Tkinter import *
+try:
+ from tkinter import *
+except ImportError:
+ from Tkinter import *
+
from PIL import Image, ImageTk
import sys
diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py
index 1c688f7c9..934167351 100644
--- a/Scripts/pilconvert.py
+++ b/Scripts/pilconvert.py
@@ -1,4 +1,4 @@
-#! /usr/local/bin/python
+#!/usr/bin/env python
#
# The Python Imaging Library.
# $Id$
@@ -13,27 +13,29 @@
# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter)
#
+from __future__ import print_function
+
import site
import getopt, string, sys
from PIL import Image
def usage():
- print "PIL Convert 0.5/1998-12-30 -- convert image files"
- print "Usage: pilconvert [option] infile outfile"
- print
- print "Options:"
- print
- print " -c convert to format (default is given by extension)"
- print
- print " -g convert to greyscale"
- print " -p convert to palette image (using standard palette)"
- print " -r convert to rgb"
- print
- print " -o optimize output (trade speed for size)"
- print " -q set compression quality (0-100, JPEG only)"
- print
- print " -f list supported file formats"
+ print("PIL Convert 0.5/1998-12-30 -- convert image files")
+ print("Usage: pilconvert [option] infile outfile")
+ print()
+ print("Options:")
+ print()
+ print(" -c convert to format (default is given by extension)")
+ print()
+ print(" -g convert to greyscale")
+ print(" -p convert to palette image (using standard palette)")
+ print(" -r convert to rgb")
+ print()
+ print(" -o optimize output (trade speed for size)")
+ print(" -q set compression quality (0-100, JPEG only)")
+ print()
+ print(" -f list supported file formats")
sys.exit(1)
if len(sys.argv) == 1:
@@ -41,8 +43,8 @@ if len(sys.argv) == 1:
try:
opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r")
-except getopt.error, v:
- print v
+except getopt.error as v:
+ print(v)
sys.exit(1)
format = None
@@ -54,14 +56,13 @@ for o, a in opt:
if o == "-f":
Image.init()
- id = Image.ID[:]
- id.sort()
- print "Supported formats (* indicates output format):"
+ id = sorted(Image.ID)
+ print("Supported formats (* indicates output format):")
for i in id:
- if Image.SAVE.has_key(i):
- print i+"*",
+ if i in Image.SAVE:
+ print(i+"*", end=' ')
else:
- print i,
+ print(i, end=' ')
sys.exit(1)
elif o == "-c":
@@ -88,9 +89,9 @@ try:
im.draft(convert, im.size)
im = im.convert(convert)
if format:
- apply(im.save, (argv[1], format), options)
+ im.save(argv[1], format, **options)
else:
- apply(im.save, (argv[1],), options)
+ im.save(argv[1], **options)
except:
- print "cannot convert image",
- print "(%s:%s)" % (sys.exc_type, sys.exc_value)
+ print("cannot convert image", end=' ')
+ print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]))
diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py
index 5dd575a3f..98708c897 100644
--- a/Scripts/pildriver.py
+++ b/Scripts/pildriver.py
@@ -48,8 +48,9 @@ of its upper-left-hand corner and displays the cropped portion.
# 3. Add support for composing and decomposing multiple-image files.
#
+from __future__ import print_function
+
from PIL import Image
-import string
class PILDriver:
@@ -60,7 +61,7 @@ class PILDriver:
Set verbosity flag from top of stack.
"""
- self.verbose = self.do_pop()
+ self.verbose = int(self.do_pop())
# The evaluation stack (internal only)
@@ -205,8 +206,8 @@ class PILDriver:
Process the top image with the given filter.
"""
- import ImageFilter
- filter = eval("ImageFilter." + string.upper(self.do_pop()))
+ from PIL import ImageFilter
+ filter = eval("ImageFilter." + self.do_pop().upper())
image = self.do_pop()
self.push(image.filter(filter))
@@ -314,7 +315,7 @@ class PILDriver:
Transpose the top image.
"""
- transpose = string.upper(self.do_pop())
+ transpose = self.do_pop().upper()
image = self.do_pop()
self.push(image.transpose(transpose))
@@ -325,21 +326,21 @@ class PILDriver:
Push the format of the top image onto the stack.
"""
- self.push(self.pop().format)
+ self.push(self.do_pop().format)
def do_mode(self):
"""usage: mode
Push the mode of the top image onto the stack.
"""
- self.push(self.pop().mode)
+ self.push(self.do_pop().mode)
def do_size(self):
"""usage: size
Push the image size on the stack as (y, x).
"""
- size = self.pop().size
+ size = self.do_pop().size
self.push(size[0])
self.push(size[1])
@@ -350,7 +351,7 @@ class PILDriver:
Invert the top image.
"""
- import ImageChops
+ from PIL import ImageChops
self.push(ImageChops.invert(self.do_pop()))
def do_lighter(self):
@@ -358,7 +359,7 @@ class PILDriver:
Pop the two top images, push an image of the lighter pixels of both.
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
self.push(ImageChops.lighter(image1, image2))
@@ -368,7 +369,7 @@ class PILDriver:
Pop the two top images, push an image of the darker pixels of both.
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
self.push(ImageChops.darker(image1, image2))
@@ -378,7 +379,7 @@ class PILDriver:
Pop the two top images, push the difference image
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
self.push(ImageChops.difference(image1, image2))
@@ -388,7 +389,7 @@ class PILDriver:
Pop the two top images, push the multiplication image.
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
self.push(ImageChops.multiply(image1, image2))
@@ -398,7 +399,7 @@ class PILDriver:
Pop the two top images, superimpose their inverted versions.
"""
- import ImageChops
+ from PIL import ImageChops
image2 = self.do_pop()
image1 = self.do_pop()
self.push(ImageChops.screen(image1, image2))
@@ -408,7 +409,7 @@ class PILDriver:
Pop the two top images, produce the scaled sum with offset.
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
scale = float(self.do_pop())
@@ -420,7 +421,7 @@ class PILDriver:
Pop the two top images, produce the scaled difference with offset.
"""
- import ImageChops
+ from PIL import ImageChops
image1 = self.do_pop()
image2 = self.do_pop()
scale = float(self.do_pop())
@@ -434,7 +435,7 @@ class PILDriver:
Enhance color in the top image.
"""
- import ImageEnhance
+ from PIL import ImageEnhance
factor = float(self.do_pop())
image = self.do_pop()
enhancer = ImageEnhance.Color(image)
@@ -445,10 +446,10 @@ class PILDriver:
Enhance contrast in the top image.
"""
- import ImageEnhance
+ from PIL import ImageEnhance
factor = float(self.do_pop())
image = self.do_pop()
- enhancer = ImageEnhance.Color(image)
+ enhancer = ImageEnhance.Contrast(image)
self.push(enhancer.enhance(factor))
def do_brightness(self):
@@ -456,10 +457,10 @@ class PILDriver:
Enhance brightness in the top image.
"""
- import ImageEnhance
+ from PIL import ImageEnhance
factor = float(self.do_pop())
image = self.do_pop()
- enhancer = ImageEnhance.Color(image)
+ enhancer = ImageEnhance.Brightness(image)
self.push(enhancer.enhance(factor))
def do_sharpness(self):
@@ -467,10 +468,10 @@ class PILDriver:
Enhance sharpness in the top image.
"""
- import ImageEnhance
+ from PIL import ImageEnhance
factor = float(self.do_pop())
image = self.do_pop()
- enhancer = ImageEnhance.Color(image)
+ enhancer = ImageEnhance.Sharpness(image)
self.push(enhancer.enhance(factor))
# The interpreter loop
@@ -482,9 +483,9 @@ class PILDriver:
self.push(list[0])
list = list[1:]
if self.verbose:
- print "Stack: " + `self.stack`
+ print("Stack: " + repr(self.stack))
top = self.top()
- if type(top) != type(""):
+ if not isinstance(top, str):
continue;
funcname = "do_" + top
if not hasattr(self, funcname):
@@ -508,15 +509,18 @@ if __name__ == '__main__':
if len(sys.argv[1:]) > 0:
driver.execute(sys.argv[1:])
else:
- print "PILDriver says hello."
- while 1:
+ print("PILDriver says hello.")
+ while True:
try:
- line = raw_input('pildriver> ');
+ if sys.version_info[0] >= 3:
+ line = input('pildriver> ');
+ else:
+ line = raw_input('pildriver> ');
except EOFError:
- print "\nPILDriver says goodbye."
+ print("\nPILDriver says goodbye.")
break
- driver.execute(string.split(line))
- print driver.stack
+ driver.execute(line.split())
+ print(driver.stack)
# The following sets edit modes for GNU EMACS
# Local Variables:
diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py
index 695725796..48514e88b 100644
--- a/Scripts/pilfile.py
+++ b/Scripts/pilfile.py
@@ -1,4 +1,4 @@
-#! /usr/local/bin/python
+#!/usr/bin/env python
#
# The Python Imaging Library.
# $Id$
@@ -17,25 +17,27 @@
# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks
#
+from __future__ import print_function
+
import site
import getopt, glob, sys
from PIL import Image
if len(sys.argv) == 1:
- print "PIL File 0.4/2003-09-30 -- identify image files"
- print "Usage: pilfile [option] files..."
- print "Options:"
- print " -f list supported file formats"
- print " -i show associated info and tile data"
- print " -v verify file headers"
- print " -q quiet, don't warn for unidentified/missing/broken files"
+ print("PIL File 0.4/2003-09-30 -- identify image files")
+ print("Usage: pilfile [option] files...")
+ print("Options:")
+ print(" -f list supported file formats")
+ print(" -i show associated info and tile data")
+ print(" -v verify file headers")
+ print(" -q quiet, don't warn for unidentified/missing/broken files")
sys.exit(1)
try:
opt, args = getopt.getopt(sys.argv[1:], "fqivD")
-except getopt.error, v:
- print v
+except getopt.error as v:
+ print(v)
sys.exit(1)
verbose = quiet = verify = 0
@@ -43,11 +45,10 @@ verbose = quiet = verify = 0
for o, a in opt:
if o == "-f":
Image.init()
- id = Image.ID[:]
- id.sort()
- print "Supported formats:"
+ id = sorted(Image.ID)
+ print("Supported formats:")
for i in id:
- print i,
+ print(i, end=' ')
sys.exit(1)
elif o == "-i":
verbose = 1
@@ -73,22 +74,22 @@ def globfix(files):
for file in globfix(args):
try:
im = Image.open(file)
- print "%s:" % file, im.format, "%dx%d" % im.size, im.mode,
+ print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ')
if verbose:
- print im.info, im.tile,
- print
+ print(im.info, im.tile, end=' ')
+ print()
if verify:
try:
im.verify()
except:
if not quiet:
- print "failed to verify image",
- print "(%s:%s)" % (sys.exc_type, sys.exc_value)
- except IOError, v:
+ print("failed to verify image", end=' ')
+ print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]))
+ except IOError as v:
if not quiet:
- print file, "failed:", v
+ print(file, "failed:", v)
except:
import traceback
if not quiet:
- print file, "failed:", "unexpected error"
+ print(file, "failed:", "unexpected error")
traceback.print_exc(file=sys.stdout)
diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py
index df08d4c08..ec25e7a71 100644
--- a/Scripts/pilfont.py
+++ b/Scripts/pilfont.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -9,22 +10,23 @@
# 2002-03-10 fl use "from PIL import"
#
+from __future__ import print_function
+
VERSION = "0.4"
-import site
-import glob, os, sys
+import glob, sys
# drivers
from PIL import BdfFontFile
from PIL import PcfFontFile
if len(sys.argv) <= 1:
- print "PILFONT", VERSION, "-- PIL font compiler."
- print
- print "Usage: pilfont fontfiles..."
- print
- print "Convert given font files to the PIL raster font format."
- print "This version of pilfont supports X BDF and PCF fonts."
+ print("PILFONT", VERSION, "-- PIL font compiler.")
+ print()
+ print("Usage: pilfont fontfiles...")
+ print()
+ print("Convert given font files to the PIL raster font format.")
+ print("This version of pilfont supports X BDF and PCF fonts.")
sys.exit(1)
files = []
@@ -33,7 +35,7 @@ for f in sys.argv[1:]:
for f in files:
- print f + "...",
+ print(f + "...", end=' ')
try:
@@ -48,7 +50,7 @@ for f in files:
p.save(f)
except (SyntaxError, IOError):
- print "failed"
+ print("failed")
else:
- print "OK"
+ print("OK")
diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py
index a98b39f7d..be42e0a75 100644
--- a/Scripts/pilprint.py
+++ b/Scripts/pilprint.py
@@ -1,4 +1,4 @@
-#! /usr/local/bin/python
+#!/usr/bin/env python
#
# The Python Imaging Library.
# $Id$
@@ -11,6 +11,8 @@
# 0.3 2003-05-06 fl Fixed a typo or two.
#
+from __future__ import print_function
+
VERSION = "pilprint 0.3/2003-05-05"
from PIL import Image
@@ -29,18 +31,18 @@ def description(file, image):
import getopt, os, sys
if len(sys.argv) == 1:
- print "PIL Print 0.2a1/96-10-04 -- print image files"
- print "Usage: pilprint files..."
- print "Options:"
- print " -c colour printer (default is monochrome)"
- print " -p print via lpr (default is stdout)"
- print " -P same as -p but use given printer"
+ print("PIL Print 0.2a1/96-10-04 -- print image files")
+ print("Usage: pilprint files...")
+ print("Options:")
+ print(" -c colour printer (default is monochrome)")
+ print(" -p print via lpr (default is stdout)")
+ print(" -P same as -p but use given printer")
sys.exit(1)
try:
opt, argv = getopt.getopt(sys.argv[1:], "cdpP:")
-except getopt.error, v:
- print v
+except getopt.error as v:
+ print(v)
sys.exit(1)
printer = None # print to stdout
@@ -50,7 +52,7 @@ for o, a in opt:
if o == "-d":
# debug: show available drivers
Image.init()
- print Image.ID
+ print(Image.ID)
sys.exit(1)
elif o == "-c":
# colour printer
@@ -89,5 +91,5 @@ for file in argv:
ps.end_document()
except:
- print "cannot print image",
- print "(%s:%s)" % (sys.exc_type, sys.exc_value)
+ print("cannot print image", end=' ')
+ print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1]))
diff --git a/Scripts/player.py b/Scripts/player.py
index 9ca4500bf..84b636668 100644
--- a/Scripts/player.py
+++ b/Scripts/player.py
@@ -1,9 +1,16 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
#
-from Tkinter import *
+from __future__ import print_function
+
+try:
+ from tkinter import *
+except ImportError:
+ from Tkinter import *
+
from PIL import Image, ImageTk
import sys
@@ -36,7 +43,7 @@ class AppletDisplay:
class UI(Label):
def __init__(self, master, im):
- if type(im) == type([]):
+ if isinstance(im, list):
# list of images
self.im = im[1:]
im = self.im[0]
@@ -65,7 +72,7 @@ class UI(Label):
def next(self):
- if type(self.im) == type([]):
+ if isinstance(self.im, list):
try:
im = self.im[0]
@@ -98,7 +105,7 @@ class UI(Label):
if __name__ == "__main__":
if not sys.argv[1:]:
- print "Syntax: python player.py imagefile(s)"
+ print("Syntax: python player.py imagefile(s)")
sys.exit(1)
filename = sys.argv[1]
@@ -108,7 +115,7 @@ if __name__ == "__main__":
if len(sys.argv) > 2:
# list of images
- print "loading..."
+ print("loading...")
im = []
for filename in sys.argv[1:]:
im.append(Image.open(filename))
diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py
index eb5109872..29d4592d9 100644
--- a/Scripts/thresholder.py
+++ b/Scripts/thresholder.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
@@ -6,7 +7,11 @@
# as a dynamically updated overlay
#
-from Tkinter import *
+try:
+ from tkinter import *
+except ImportError:
+ from Tkinter import *
+
from PIL import Image, ImageTk
import sys
diff --git a/Scripts/viewer.py b/Scripts/viewer.py
index 6e4dc8b67..86b2526cd 100644
--- a/Scripts/viewer.py
+++ b/Scripts/viewer.py
@@ -1,9 +1,16 @@
+#!/usr/bin/env python
#
# The Python Imaging Library
# $Id$
#
-from Tkinter import *
+from __future__ import print_function
+
+try:
+ from tkinter import *
+except ImportError:
+ from Tkinter import *
+
from PIL import Image, ImageTk
#
@@ -31,7 +38,7 @@ if __name__ == "__main__":
import sys
if not sys.argv[1:]:
- print "Syntax: python viewer.py imagefile"
+ print("Syntax: python viewer.py imagefile")
sys.exit(1)
filename = sys.argv[1]
diff --git a/Tests/README.txt b/Tests/README.txt
new file mode 100644
index 000000000..169bc4da5
--- /dev/null
+++ b/Tests/README.txt
@@ -0,0 +1,14 @@
+Minimalistic PIL test framework.
+
+Test scripts are named "test_xxx" and are supposed to output "ok". That's it. To run the tests::
+
+ python setup.py develop
+
+Run the tests from the root of the Pillow source distribution:
+
+ python selftest.py
+ python Tests/run.py --installed
+
+To run an individual test:
+
+ python Tests/test_image.py
diff --git a/Tests/bench_get.py b/Tests/bench_get.py
new file mode 100644
index 000000000..eca491600
--- /dev/null
+++ b/Tests/bench_get.py
@@ -0,0 +1,20 @@
+import sys
+sys.path.insert(0, ".")
+
+import tester
+import timeit
+
+def bench(mode):
+ im = tester.lena(mode)
+ get = im.im.getpixel
+ xy = 50, 50 # position shouldn't really matter
+ t0 = timeit.default_timer()
+ for i in range(1000000):
+ get(xy)
+ print(mode, timeit.default_timer() - t0, "us")
+
+bench("L")
+bench("I")
+bench("I;16")
+bench("F")
+bench("RGB")
diff --git a/Tests/cms_test.py b/Tests/cms_test.py
new file mode 100644
index 000000000..634e3f717
--- /dev/null
+++ b/Tests/cms_test.py
@@ -0,0 +1,222 @@
+# PyCMSTests.py
+# Examples of how to use pyCMS, as well as tests to verify it works properly
+# By Kevin Cazabon (kevin@cazabon.com)
+
+# Imports
+import os
+from PIL import Image
+from PIL import ImageCms
+
+# import PyCMSError separately so we can catch it
+PyCMSError = ImageCms.PyCMSError
+
+#######################################################################
+# Configuration:
+#######################################################################
+# set this to the image you want to test with
+IMAGE = "c:\\temp\\test.tif"
+
+# set this to where you want to save the output images
+OUTPUTDIR = "c:\\temp\\"
+
+# set these to two different ICC profiles, one for input, one for output
+# set the corresponding mode to the proper PIL mode for that profile
+INPUT_PROFILE = "c:\\temp\\profiles\\sRGB.icm"
+INMODE = "RGB"
+
+OUTPUT_PROFILE = "c:\\temp\\profiles\\genericRGB.icm"
+OUTMODE = "RGB"
+
+PROOF_PROFILE = "c:\\temp\\profiles\\monitor.icm"
+
+# set to True to show() images, False to save them into OUTPUT_DIRECTORY
+SHOW = False
+
+# Tests you can enable/disable
+TEST_error_catching = True
+TEST_profileToProfile = True
+TEST_profileToProfile_inPlace = True
+TEST_buildTransform = True
+TEST_buildTransformFromOpenProfiles = True
+TEST_buildProofTransform = True
+TEST_getProfileInfo = True
+TEST_misc = False
+
+#######################################################################
+# helper functions
+#######################################################################
+def outputImage(im, funcName = None):
+ # save or display the image, depending on value of SHOW_IMAGES
+ if SHOW == True:
+ im.show()
+ else:
+ im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName))
+
+
+#######################################################################
+# The tests themselves
+#######################################################################
+
+if TEST_error_catching == True:
+ im = Image.open(IMAGE)
+ try:
+ #neither of these proifles exists (unless you make them), so we should
+ # get an error
+ imOut = ImageCms.profileToProfile(im, "missingProfile.icm", "cmyk.icm")
+
+ except PyCMSError as reason:
+ print("We caught a PyCMSError: %s\n\n" %reason)
+
+ print("error catching test completed successfully (if you see the message \
+ above that we caught the error).")
+
+if TEST_profileToProfile == True:
+ # open the image file using the standard PIL function Image.open()
+ im = Image.open(IMAGE)
+
+ # send the image, input/output profiles, and rendering intent to
+ # ImageCms.profileToProfile()
+ imOut = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \
+ outputMode = OUTMODE)
+
+ # now that the image is converted, save or display it
+ outputImage(imOut, "profileToProfile")
+
+ print("profileToProfile test completed successfully.")
+
+if TEST_profileToProfile_inPlace == True:
+ # we'll do the same test as profileToProfile, but modify im in place
+ # instead of getting a new image returned to us
+ im = Image.open(IMAGE)
+
+ # send the image to ImageCms.profileToProfile(), specifying inPlace = True
+ result = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \
+ outputMode = OUTMODE, inPlace = True)
+
+ # now that the image is converted, save or display it
+ if result == None:
+ # this is the normal result when modifying in-place
+ outputImage(im, "profileToProfile_inPlace")
+ else:
+ # something failed...
+ print("profileToProfile in-place failed: %s" %result)
+
+ print("profileToProfile in-place test completed successfully.")
+
+if TEST_buildTransform == True:
+ # make a transform using the input and output profile path strings
+ transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \
+ OUTMODE)
+
+ # now, use the trnsform to convert a couple images
+ im = Image.open(IMAGE)
+
+ # transform im normally
+ im2 = ImageCms.applyTransform(im, transform)
+ outputImage(im2, "buildTransform")
+
+ # then transform it again using the same transform, this time in-place.
+ result = ImageCms.applyTransform(im, transform, inPlace = True)
+ outputImage(im, "buildTransform_inPlace")
+
+ print("buildTransform test completed successfully.")
+
+ # and, to clean up a bit, delete the transform
+ # this should call the C destructor for the transform structure.
+ # Python should also do this automatically when it goes out of scope.
+ del(transform)
+
+if TEST_buildTransformFromOpenProfiles == True:
+ # we'll actually test a couple profile open/creation functions here too
+
+ # first, get a handle to an input profile, in this case we'll create
+ # an sRGB profile on the fly:
+ inputProfile = ImageCms.createProfile("sRGB")
+
+ # then, get a handle to the output profile
+ outputProfile = ImageCms.getOpenProfile(OUTPUT_PROFILE)
+
+ # make a transform from these
+ transform = ImageCms.buildTransformFromOpenProfiles(inputProfile, \
+ outputProfile, INMODE, OUTMODE)
+
+ # now, use the trnsform to convert a couple images
+ im = Image.open(IMAGE)
+
+ # transform im normally
+ im2 = ImageCms.applyTransform(im, transform)
+ outputImage(im2, "buildTransformFromOpenProfiles")
+
+ # then do it again using the same transform, this time in-place.
+ result = ImageCms.applyTransform(im, transform, inPlace = True)
+ outputImage(im, "buildTransformFromOpenProfiles_inPlace")
+
+ print("buildTransformFromOpenProfiles test completed successfully.")
+
+ # and, to clean up a bit, delete the transform
+ # this should call the C destructor for the each item.
+ # Python should also do this automatically when it goes out of scope.
+ del(inputProfile)
+ del(outputProfile)
+ del(transform)
+
+if TEST_buildProofTransform == True:
+ # make a transform using the input and output and proof profile path
+ # strings
+ # images converted with this transform will simulate the appearance
+ # of the output device while actually being displayed/proofed on the
+ # proof device. This usually means a monitor, but can also mean
+ # other proof-printers like dye-sub, etc.
+ transform = ImageCms.buildProofTransform(INPUT_PROFILE, OUTPUT_PROFILE, \
+ PROOF_PROFILE, INMODE, OUTMODE)
+
+ # now, use the trnsform to convert a couple images
+ im = Image.open(IMAGE)
+
+ # transform im normally
+ im2 = ImageCms.applyTransform(im, transform)
+ outputImage(im2, "buildProofTransform")
+
+ # then transform it again using the same transform, this time in-place.
+ result = ImageCms.applyTransform(im, transform, inPlace = True)
+ outputImage(im, "buildProofTransform_inPlace")
+
+ print("buildProofTransform test completed successfully.")
+
+ # and, to clean up a bit, delete the transform
+ # this should call the C destructor for the transform structure.
+ # Python should also do this automatically when it goes out of scope.
+ del(transform)
+
+if TEST_getProfileInfo == True:
+ # get a profile handle
+ profile = ImageCms.getOpenProfile(INPUT_PROFILE)
+
+ # lets print some info about our input profile:
+ print("Profile name (retrieved from profile string path name): %s" %ImageCms.getProfileName(INPUT_PROFILE))
+
+ # or, you could do the same thing using a profile handle as the arg
+ print("Profile name (retrieved from profile handle): %s" %ImageCms.getProfileName(profile))
+
+ # now lets get the embedded "info" tag contents
+ # once again, you can use a path to a profile, or a profile handle
+ print("Profile info (retrieved from profile handle): %s" %ImageCms.getProfileInfo(profile))
+
+ # and what's the default intent of this profile?
+ print("The default intent is (this will be an integer): %d" %(ImageCms.getDefaultIntent(profile)))
+
+ # Hmmmm... but does this profile support INTENT_ABSOLUTE_COLORIMETRIC?
+ print("Does it support INTENT_ABSOLUTE_COLORIMETRIC?: (1 is yes, -1 is no): %s" \
+ %ImageCms.isIntentSupported(profile, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, \
+ ImageCms.DIRECTION_INPUT))
+
+ print("getProfileInfo test completed successfully.")
+
+if TEST_misc == True:
+ # test the versions, about, and copyright functions
+ print("Versions: %s" %str(ImageCms.versions()))
+ print("About:\n\n%s" %ImageCms.about())
+ print("Copyright:\n\n%s" %ImageCms.copyright())
+
+ print("misc test completed successfully.")
+
diff --git a/Tests/crash_ttf_memory_error.py b/Tests/crash_ttf_memory_error.py
new file mode 100644
index 000000000..881fc03a6
--- /dev/null
+++ b/Tests/crash_ttf_memory_error.py
@@ -0,0 +1,14 @@
+from PIL import Image, ImageFont, ImageDraw
+
+font = "../pil-archive/memory-error-2.ttf"
+
+s = "Test Text"
+f = ImageFont.truetype(font, 64, index=0, encoding="unicode")
+w, h = f.getsize(s)
+i = Image.new("RGB", (500, h), "white")
+d = ImageDraw.Draw(i)
+
+# this line causes a MemoryError
+d.text((0,0), s, font=f, fill=0)
+
+i.show()
diff --git a/Tests/fonts/FreeMono.ttf b/Tests/fonts/FreeMono.ttf
new file mode 100644
index 000000000..f88bcef9c
Binary files /dev/null and b/Tests/fonts/FreeMono.ttf differ
diff --git a/Tests/fonts/helvO18.pcf b/Tests/fonts/helvO18.pcf
new file mode 100644
index 000000000..f5e68ae9c
Binary files /dev/null and b/Tests/fonts/helvO18.pcf differ
diff --git a/Tests/icc/CMY.icm b/Tests/icc/CMY.icm
new file mode 100644
index 000000000..acc71ddd9
Binary files /dev/null and b/Tests/icc/CMY.icm differ
diff --git a/Tests/icc/YCC709.icm b/Tests/icc/YCC709.icm
new file mode 100644
index 000000000..d9f5a5a5d
Binary files /dev/null and b/Tests/icc/YCC709.icm differ
diff --git a/Tests/icc/sRGB.icm b/Tests/icc/sRGB.icm
new file mode 100644
index 000000000..7f9d18d09
Binary files /dev/null and b/Tests/icc/sRGB.icm differ
diff --git a/Tests/images/12bit.cropped.tif b/Tests/images/12bit.cropped.tif
new file mode 100644
index 000000000..85af58328
Binary files /dev/null and b/Tests/images/12bit.cropped.tif differ
diff --git a/Tests/images/12in16bit.tif b/Tests/images/12in16bit.tif
new file mode 100644
index 000000000..02e1bfe6a
Binary files /dev/null and b/Tests/images/12in16bit.tif differ
diff --git a/Tests/images/16bit.MM.cropped.tif b/Tests/images/16bit.MM.cropped.tif
new file mode 100644
index 000000000..c4c24e2eb
Binary files /dev/null and b/Tests/images/16bit.MM.cropped.tif differ
diff --git a/Tests/images/16bit.MM.deflate.tif b/Tests/images/16bit.MM.deflate.tif
new file mode 100644
index 000000000..90a62e94f
Binary files /dev/null and b/Tests/images/16bit.MM.deflate.tif differ
diff --git a/Tests/images/16bit.cropped.tif b/Tests/images/16bit.cropped.tif
new file mode 100644
index 000000000..eb8b1dc21
Binary files /dev/null and b/Tests/images/16bit.cropped.tif differ
diff --git a/Tests/images/16bit.deflate.tif b/Tests/images/16bit.deflate.tif
new file mode 100644
index 000000000..4f9f4ec06
Binary files /dev/null and b/Tests/images/16bit.deflate.tif differ
diff --git a/Tests/images/broken.png b/Tests/images/broken.png
new file mode 100644
index 000000000..7def38564
--- /dev/null
+++ b/Tests/images/broken.png
@@ -0,0 +1,5 @@
+PNG
+
+/}P̒>0jȂ&✉\@TLU
+x/
+Z`w;9c
\ No newline at end of file
diff --git a/Tests/images/caption_6_33_22.png b/Tests/images/caption_6_33_22.png
new file mode 100644
index 000000000..2aa0a50bf
Binary files /dev/null and b/Tests/images/caption_6_33_22.png differ
diff --git a/Tests/images/create_eps.gnuplot b/Tests/images/create_eps.gnuplot
new file mode 100644
index 000000000..4d7e29877
--- /dev/null
+++ b/Tests/images/create_eps.gnuplot
@@ -0,0 +1,30 @@
+#!/usr/bin/gnuplot
+
+#This is the script that was used to create our sample EPS files
+#We used the following version of the gnuplot program
+#G N U P L O T
+#Version 4.6 patchlevel 3 last modified 2013-04-12
+#Build System: Darwin x86_64
+
+#This file will generate the non_zero_bb.eps variant, in order to get the
+#zero_bb.eps variant you will need to edit line6 in the result file to
+#be "%%BoundingBox: 0 0 460 352" instead of "%%BoundingBox: 50 50 410 302"
+
+set t postscript eps color
+set o "sample.eps"
+set dummy u,v
+set key bmargin center horizontal Right noreverse enhanced autotitles nobox
+set parametric
+set view 50, 30, 1, 1
+set isosamples 10, 10
+set hidden3d back offset 1 trianglepattern 3 undefined 1 altdiagonal bentover
+set ticslevel 0
+set title "Interlocking Tori"
+
+set style line 1 lt 1 lw 1 pt 3 lc rgb "red"
+set style line 2 lt 1 lw 1 pt 3 lc rgb "blue"
+
+set urange [ -3.14159 : 3.14159 ] noreverse nowriteback
+set vrange [ -3.14159 : 3.14159 ] noreverse nowriteback
+splot cos(u)+.5*cos(u)*cos(v),sin(u)+.5*sin(u)*cos(v),.5*sin(v) ls 1,\
+ 1+cos(u)+.5*cos(u)*cos(v),.5*sin(v),sin(u)+.5*sin(u)*cos(v) ls 2
diff --git a/Tests/images/flower.jpg b/Tests/images/flower.jpg
new file mode 100644
index 000000000..933719d1c
Binary files /dev/null and b/Tests/images/flower.jpg differ
diff --git a/Tests/images/flower2.jpg b/Tests/images/flower2.jpg
new file mode 100644
index 000000000..e94b2f065
Binary files /dev/null and b/Tests/images/flower2.jpg differ
diff --git a/Tests/images/g4-fillorder-test.png b/Tests/images/g4-fillorder-test.png
new file mode 100644
index 000000000..86b009cfd
Binary files /dev/null and b/Tests/images/g4-fillorder-test.png differ
diff --git a/Tests/images/g4-fillorder-test.tif b/Tests/images/g4-fillorder-test.tif
new file mode 100644
index 000000000..22537b308
Binary files /dev/null and b/Tests/images/g4-fillorder-test.tif differ
diff --git a/Tests/images/l_trns.png b/Tests/images/l_trns.png
new file mode 100644
index 000000000..a26a82076
Binary files /dev/null and b/Tests/images/l_trns.png 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/images/lena.tif b/Tests/images/lena.tif
new file mode 100644
index 000000000..fead980d4
Binary files /dev/null and b/Tests/images/lena.tif differ
diff --git a/Tests/images/lena_bw.png b/Tests/images/lena_bw.png
new file mode 100644
index 000000000..f9b64c185
Binary files /dev/null and b/Tests/images/lena_bw.png differ
diff --git a/Tests/images/lena_bw_500.png b/Tests/images/lena_bw_500.png
new file mode 100644
index 000000000..1e1d0bd7d
Binary files /dev/null and b/Tests/images/lena_bw_500.png differ
diff --git a/Tests/images/lena_g4.tif b/Tests/images/lena_g4.tif
new file mode 100644
index 000000000..7ebe72fab
Binary files /dev/null and b/Tests/images/lena_g4.tif differ
diff --git a/Tests/images/lena_g4_500.tif b/Tests/images/lena_g4_500.tif
new file mode 100644
index 000000000..80f5e70f1
Binary files /dev/null and b/Tests/images/lena_g4_500.tif differ
diff --git a/Tests/images/lena_webp_bits.ppm b/Tests/images/lena_webp_bits.ppm
new file mode 100644
index 000000000..62fd9803d
Binary files /dev/null and b/Tests/images/lena_webp_bits.ppm differ
diff --git a/Tests/images/lena_webp_write.ppm b/Tests/images/lena_webp_write.ppm
new file mode 100644
index 000000000..4fa197999
Binary files /dev/null and b/Tests/images/lena_webp_write.ppm differ
diff --git a/Tests/images/multiline_text.png b/Tests/images/multiline_text.png
new file mode 100644
index 000000000..ff1308c5e
Binary files /dev/null and b/Tests/images/multiline_text.png differ
diff --git a/Tests/images/non_zero_bb.eps b/Tests/images/non_zero_bb.eps
new file mode 100644
index 000000000..750a44b38
Binary files /dev/null and b/Tests/images/non_zero_bb.eps differ
diff --git a/Tests/images/non_zero_bb.png b/Tests/images/non_zero_bb.png
new file mode 100644
index 000000000..156c9a091
Binary files /dev/null and b/Tests/images/non_zero_bb.png differ
diff --git a/Tests/images/non_zero_bb_scale2.png b/Tests/images/non_zero_bb_scale2.png
new file mode 100644
index 000000000..2600580b3
Binary files /dev/null and b/Tests/images/non_zero_bb_scale2.png differ
diff --git a/Tests/images/p_trns_single.png b/Tests/images/p_trns_single.png
new file mode 100644
index 000000000..8f37bd452
Binary files /dev/null and b/Tests/images/p_trns_single.png differ
diff --git a/Tests/images/pil123p.png b/Tests/images/pil123p.png
new file mode 100644
index 000000000..03960d493
Binary files /dev/null and b/Tests/images/pil123p.png differ
diff --git a/Tests/images/pil123rgba.png b/Tests/images/pil123rgba.png
new file mode 100644
index 000000000..c3d7b7d1b
Binary files /dev/null and b/Tests/images/pil123rgba.png differ
diff --git a/Tests/images/pil136.tiff b/Tests/images/pil136.tiff
new file mode 100644
index 000000000..07d339e7c
Binary files /dev/null and b/Tests/images/pil136.tiff differ
diff --git a/Tests/images/pil168.tif b/Tests/images/pil168.tif
new file mode 100644
index 000000000..621f03a2f
Binary files /dev/null and b/Tests/images/pil168.tif differ
diff --git a/Tests/images/pil184.pcx b/Tests/images/pil184.pcx
new file mode 100644
index 000000000..bbd68be5c
Binary files /dev/null and b/Tests/images/pil184.pcx differ
diff --git a/Tests/images/pil_sample_cmyk.jpg b/Tests/images/pil_sample_cmyk.jpg
new file mode 100644
index 000000000..0c11e70f2
Binary files /dev/null and b/Tests/images/pil_sample_cmyk.jpg differ
diff --git a/Tests/images/pil_sample_rgb.jpg b/Tests/images/pil_sample_rgb.jpg
new file mode 100644
index 000000000..956a86bae
Binary files /dev/null and b/Tests/images/pil_sample_rgb.jpg differ
diff --git a/Tests/images/pngtest_bad.png.bin b/Tests/images/pngtest_bad.png.bin
new file mode 100644
index 000000000..07a74a104
Binary files /dev/null and b/Tests/images/pngtest_bad.png.bin differ
diff --git a/Tests/images/pport_g4.tif b/Tests/images/pport_g4.tif
new file mode 100644
index 000000000..8268d398d
Binary files /dev/null and b/Tests/images/pport_g4.tif differ
diff --git a/Tests/images/rgb.jpg b/Tests/images/rgb.jpg
new file mode 100644
index 000000000..fa9953844
Binary files /dev/null and b/Tests/images/rgb.jpg differ
diff --git a/Tests/images/rgb_trns.png b/Tests/images/rgb_trns.png
new file mode 100644
index 000000000..dee80acdc
Binary files /dev/null and b/Tests/images/rgb_trns.png differ
diff --git a/Tests/images/tiff_adobe_deflate.tif b/Tests/images/tiff_adobe_deflate.tif
new file mode 100644
index 000000000..7c0d9c53d
Binary files /dev/null and b/Tests/images/tiff_adobe_deflate.tif differ
diff --git a/Tests/images/zero_bb.eps b/Tests/images/zero_bb.eps
new file mode 100644
index 000000000..e931bf833
Binary files /dev/null and b/Tests/images/zero_bb.eps differ
diff --git a/Tests/images/zero_bb.png b/Tests/images/zero_bb.png
new file mode 100644
index 000000000..7d02a5814
Binary files /dev/null and b/Tests/images/zero_bb.png differ
diff --git a/Tests/images/zero_bb_scale2.png b/Tests/images/zero_bb_scale2.png
new file mode 100644
index 000000000..81c9d056d
Binary files /dev/null and b/Tests/images/zero_bb_scale2.png differ
diff --git a/Tests/import_all.py b/Tests/import_all.py
new file mode 100644
index 000000000..118bf69a7
--- /dev/null
+++ b/Tests/import_all.py
@@ -0,0 +1,13 @@
+import sys
+sys.path.insert(0, ".")
+
+import glob, os
+import traceback
+
+for file in glob.glob("PIL/*.py"):
+ module = os.path.basename(file)[:-3]
+ try:
+ exec("from PIL import " + module)
+ except (ImportError, SyntaxError):
+ print("===", "failed to import", module)
+ traceback.print_exc()
diff --git a/Tests/large_memory_numpy_test.py b/Tests/large_memory_numpy_test.py
new file mode 100644
index 000000000..eb9b8aa01
--- /dev/null
+++ b/Tests/large_memory_numpy_test.py
@@ -0,0 +1,37 @@
+from tester import *
+
+# This test is not run automatically.
+#
+# It requires > 2gb memory for the >2 gigapixel image generated in the
+# second test. Running this automatically would amount to a denial of
+# service on our testing infrastructure. I expect this test to fail
+# on any 32 bit machine, as well as any smallish things (like
+# raspberrypis).
+
+from PIL import Image
+try:
+ import numpy as np
+except:
+ skip()
+
+ydim = 32769
+xdim = 48000
+f = tempfile('temp.png')
+
+def _write_png(xdim,ydim):
+ dtype = np.uint8
+ a = np.zeros((xdim, ydim), dtype=dtype)
+ im = Image.fromarray(a, 'L')
+ im.save(f)
+ success()
+
+def test_large():
+ """ succeeded prepatch"""
+ _write_png(xdim,ydim)
+def test_2gpx():
+ """failed prepatch"""
+ _write_png(xdim,xdim)
+
+
+
+
diff --git a/Tests/large_memory_test.py b/Tests/large_memory_test.py
new file mode 100644
index 000000000..148841ec2
--- /dev/null
+++ b/Tests/large_memory_test.py
@@ -0,0 +1,27 @@
+from tester import *
+
+# This test is not run automatically.
+#
+# It requires > 2gb memory for the >2 gigapixel image generated in the
+# second test. Running this automatically would amount to a denial of
+# service on our testing infrastructure. I expect this test to fail
+# on any 32 bit machine, as well as any smallish things (like
+# raspberrypis). It does succeed on a 3gb Ubuntu 12.04x64 VM on python
+# 2.7 an 3.2
+
+from PIL import Image
+ydim = 32769
+xdim = 48000
+f = tempfile('temp.png')
+
+def _write_png(xdim,ydim):
+ im = Image.new('L',(xdim,ydim),(0))
+ im.save(f)
+ success()
+
+def test_large():
+ """ succeeded prepatch"""
+ _write_png(xdim,ydim)
+def test_2gpx():
+ """failed prepatch"""
+ _write_png(xdim,xdim)
diff --git a/Tests/make_hash.py b/Tests/make_hash.py
new file mode 100644
index 000000000..71e208cff
--- /dev/null
+++ b/Tests/make_hash.py
@@ -0,0 +1,57 @@
+# brute-force search for access descriptor hash table
+
+import random
+
+modes = [
+ "1",
+ "L", "LA",
+ "I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B",
+ "F",
+ "P", "PA",
+ "RGB", "RGBA", "RGBa", "RGBX",
+ "CMYK",
+ "YCbCr",
+ ]
+
+def hash(s, i):
+ # djb2 hash: multiply by 33 and xor character
+ for c in s:
+ i = (((i<<5) + i) ^ ord(c)) & 0xffffffff
+ return i
+
+def check(size, i0):
+ h = [None] * size
+ for m in modes:
+ i = hash(m, i0)
+ i = i % size
+ if h[i]:
+ return 0
+ h[i] = m
+ return h
+
+min_start = 0
+
+# 1) find the smallest table size with no collisions
+for min_size in range(len(modes), 16384):
+ if check(min_size, 0):
+ print(len(modes), "modes fit in", min_size, "slots")
+ break
+
+# 2) see if we can do better with a different initial value
+for i0 in range(65556):
+ for size in range(1, min_size):
+ if check(size, i0):
+ if size < min_size:
+ print(len(modes), "modes fit in", size, "slots with start", i0)
+ min_size = size
+ min_start = i0
+
+print()
+
+# print check(min_size, min_start)
+
+print("#define ACCESS_TABLE_SIZE", min_size)
+print("#define ACCESS_TABLE_HASH", min_start)
+
+# for m in modes:
+# print m, "=>", hash(m, min_start) % min_size
diff --git a/Tests/run.py b/Tests/run.py
new file mode 100644
index 000000000..02b633c90
--- /dev/null
+++ b/Tests/run.py
@@ -0,0 +1,96 @@
+from __future__ import print_function
+
+# minimal test runner
+
+import glob, os, os.path, sys, tempfile
+
+try:
+ root = os.path.dirname(__file__)
+except NameError:
+ root = os.path.dirname(sys.argv[0])
+
+if not os.path.isfile("PIL/Image.py"):
+ print("***", "please run this script from the PIL development directory as")
+ print("***", "$ python Tests/run.py")
+ sys.exit(1)
+
+print("-"*68)
+
+python_options = []
+tester_options = []
+
+if "--installed" not in sys.argv:
+ os.environ["PYTHONPATH"] = "."
+
+if "--coverage" in sys.argv:
+ tester_options.append("--coverage")
+
+if "--log" in sys.argv:
+ tester_options.append("--log")
+
+files = glob.glob(os.path.join(root, "test_*.py"))
+files.sort()
+
+success = failure = 0
+include = [x for x in sys.argv[1:] if x[:2] != "--"]
+skipped = []
+
+python_options = " ".join(python_options)
+tester_options = " ".join(tester_options)
+
+for file in files:
+ test, ext = os.path.splitext(os.path.basename(file))
+ if include and test not in include:
+ continue
+ print("running", test, "...")
+ # 2>&1 works on unix and on modern windowses. we might care about
+ # very old Python versions, but not ancient microsoft products :-)
+ out = os.popen("%s %s -u %s %s 2>&1" % (
+ sys.executable, python_options, file, tester_options
+ ))
+ result = out.read().strip()
+ if result == "ok":
+ result = None
+ elif result == "skip":
+ print("---", "skipped") # FIXME: driver should include a reason
+ skipped.append(test)
+ continue
+ elif not result:
+ result = "(no output)"
+ status = out.close()
+ if status or result:
+ if status:
+ print("=== error", status)
+ if result:
+ if result[-3:] == "\nok":
+ # if there's an ok at the end, it's not really ok
+ result = result[:-3]
+ print(result)
+ failure = failure + 1
+ else:
+ success = success + 1
+
+print("-"*68)
+
+temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests')
+tempfiles = glob.glob(os.path.join(temp_root, "temp_*"))
+if tempfiles:
+ print("===", "remaining temporary files")
+ for file in tempfiles:
+ print(file)
+ print("-"*68)
+
+def tests(n):
+ if n == 1:
+ return "1 test"
+ else:
+ return "%d tests" % n
+
+if skipped:
+ print("---", tests(len(skipped)), "skipped.")
+ print(skipped)
+if failure:
+ print("***", tests(failure), "of", (success + failure), "failed.")
+ sys.exit(1)
+else:
+ print(tests(success), "passed.")
diff --git a/Tests/show_icc.py b/Tests/show_icc.py
new file mode 100644
index 000000000..e062747e7
--- /dev/null
+++ b/Tests/show_icc.py
@@ -0,0 +1,28 @@
+import sys
+sys.path.insert(0, ".")
+
+from PIL import Image
+from PIL import ImageCms
+
+try:
+ filename = sys.argv[1]
+except IndexError:
+ filename = "../pil-archive/cmyk.jpg"
+
+i = Image.open(filename)
+
+print(i.format)
+print(i.mode)
+print(i.size)
+print(i.tile)
+
+p = ImageCms.getMemoryProfile(i.info["icc_profile"])
+
+print(repr(p.product_name))
+print(repr(p.product_info))
+
+o = ImageCms.createProfile("sRGB")
+t = ImageCms.buildTransformFromOpenProfiles(p, o, i.mode, "RGB")
+i = ImageCms.applyTransform(i, t)
+
+i.show()
diff --git a/Tests/show_mcidas.py b/Tests/show_mcidas.py
new file mode 100644
index 000000000..db193b82a
--- /dev/null
+++ b/Tests/show_mcidas.py
@@ -0,0 +1,26 @@
+import sys
+sys.path.insert(0, ".")
+
+from PIL import Image
+from PIL import ImageMath
+
+try:
+ filename = sys.argv[1]
+except IndexError:
+ filename = "../pil-archive/goes12.2005.140.190925.BAND_01.mcidas"
+ # filename = "../pil-archive/goes12.2005.140.190925.BAND_01.im"
+
+im = Image.open(filename)
+
+print(im.format)
+print(im.mode)
+print(im.size)
+print(im.tile)
+
+lo, hi = im.getextrema()
+
+print("map", lo, hi, "->", end=' ')
+im = ImageMath.eval("convert(im*255/hi, 'L')", im=im, hi=hi)
+print(im.getextrema())
+
+im.show()
diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py
new file mode 100644
index 000000000..a30786458
--- /dev/null
+++ b/Tests/test_000_sanity.py
@@ -0,0 +1,24 @@
+from __future__ import print_function
+from tester import *
+
+import PIL
+import PIL.Image
+
+# Make sure we have the binary extension
+im = PIL.Image.core.new("L", (100, 100))
+
+assert PIL.Image.VERSION[:3] == '1.1'
+
+# Create an image and do stuff with it.
+im = PIL.Image.new("1", (100, 100))
+assert (im.mode, im.size) == ('1', (100, 100))
+assert len(im.tobytes()) == 1300
+
+# Create images in all remaining major modes.
+im = PIL.Image.new("L", (100, 100))
+im = PIL.Image.new("P", (100, 100))
+im = PIL.Image.new("RGB", (100, 100))
+im = PIL.Image.new("I", (100, 100))
+im = PIL.Image.new("F", (100, 100))
+
+print("ok")
diff --git a/Tests/test_001_archive.py b/Tests/test_001_archive.py
new file mode 100644
index 000000000..a914a6c4c
--- /dev/null
+++ b/Tests/test_001_archive.py
@@ -0,0 +1,23 @@
+import PIL
+import PIL.Image
+
+import glob, os
+
+for file in glob.glob("../pil-archive/*"):
+ f, e = os.path.splitext(file)
+ if e in [".txt", ".ttf", ".otf", ".zip"]:
+ continue
+ try:
+ im = PIL.Image.open(file)
+ im.load()
+ except IOError as v:
+ print("-", "failed to open", file, "-", v)
+ else:
+ print("+", file, im.mode, im.size, im.format)
+ if e == ".exif":
+ try:
+ info = im._getexif()
+ except KeyError as v:
+ print("-", "failed to get exif info from", file, "-", v)
+
+print("ok")
diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py
new file mode 100644
index 000000000..e0584641c
--- /dev/null
+++ b/Tests/test_file_bmp.py
@@ -0,0 +1,27 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ file = tempfile("temp.bmp")
+
+ lena().save(file)
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "BMP")
+
+ lena("1").save(file)
+ im = Image.open(file)
+
+ lena("L").save(file)
+ im = Image.open(file)
+
+ lena("P").save(file)
+ im = Image.open(file)
+
+ lena("RGB").save(file)
+ im = Image.open(file)
diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py
new file mode 100644
index 000000000..e9d051b11
--- /dev/null
+++ b/Tests/test_file_eps.py
@@ -0,0 +1,108 @@
+from tester import *
+
+from PIL import Image, EpsImagePlugin
+import sys
+import io
+
+if EpsImagePlugin.gs_windows_binary == False:
+ # already checked. Not there.
+ skip()
+
+if not sys.platform.startswith('win'):
+ import subprocess
+ try:
+ gs = subprocess.Popen(['gs','--version'], stdout=subprocess.PIPE)
+ gs.stdout.read()
+ except OSError:
+ # no ghostscript
+ skip()
+
+#Our two EPS test files (they are identical except for their bounding boxes)
+file1 = "Tests/images/zero_bb.eps"
+file2 = "Tests/images/non_zero_bb.eps"
+
+#Due to palletization, we'll need to convert these to RGB after load
+file1_compare = "Tests/images/zero_bb.png"
+file1_compare_scale2 = "Tests/images/zero_bb_scale2.png"
+
+file2_compare = "Tests/images/non_zero_bb.png"
+file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png"
+
+def test_sanity():
+ #Regular scale
+ image1 = Image.open(file1)
+ image1.load()
+ assert_equal(image1.mode, "RGB")
+ assert_equal(image1.size, (460, 352))
+ assert_equal(image1.format, "EPS")
+
+ image2 = Image.open(file2)
+ image2.load()
+ assert_equal(image2.mode, "RGB")
+ assert_equal(image2.size, (360, 252))
+ assert_equal(image2.format, "EPS")
+
+ #Double scale
+ image1_scale2 = Image.open(file1)
+ image1_scale2.load(scale=2)
+ assert_equal(image1_scale2.mode, "RGB")
+ assert_equal(image1_scale2.size, (920, 704))
+ assert_equal(image1_scale2.format, "EPS")
+
+ image2_scale2 = Image.open(file2)
+ image2_scale2.load(scale=2)
+ assert_equal(image2_scale2.mode, "RGB")
+ assert_equal(image2_scale2.size, (720, 504))
+ assert_equal(image2_scale2.format, "EPS")
+
+def test_file_object():
+ #issue 479
+ image1 = Image.open(file1)
+ with open(tempfile('temp_file.eps'), 'wb') as fh:
+ image1.save(fh, 'EPS')
+
+def test_iobase_object():
+ #issue 479
+ image1 = Image.open(file1)
+ with io.open(tempfile('temp_iobase.eps'), 'wb') as fh:
+ image1.save(fh, 'EPS')
+
+def test_render_scale1():
+ #We need png support for these render test
+ codecs = dir(Image.core)
+ if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
+ skip("zip/deflate support not available")
+
+ #Zero bounding box
+ image1_scale1 = Image.open(file1)
+ image1_scale1.load()
+ image1_scale1_compare = Image.open(file1_compare).convert("RGB")
+ image1_scale1_compare.load()
+ assert_image_similar(image1_scale1, image1_scale1_compare, 5)
+
+ #Non-Zero bounding box
+ image2_scale1 = Image.open(file2)
+ image2_scale1.load()
+ image2_scale1_compare = Image.open(file2_compare).convert("RGB")
+ image2_scale1_compare.load()
+ assert_image_similar(image2_scale1, image2_scale1_compare, 10)
+
+def test_render_scale2():
+ #We need png support for these render test
+ codecs = dir(Image.core)
+ if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
+ skip("zip/deflate support not available")
+
+ #Zero bounding box
+ image1_scale2 = Image.open(file1)
+ image1_scale2.load(scale=2)
+ image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
+ image1_scale2_compare.load()
+ assert_image_similar(image1_scale2, image1_scale2_compare, 5)
+
+ #Non-Zero bounding box
+ image2_scale2 = Image.open(file2)
+ image2_scale2.load(scale=2)
+ image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
+ image2_scale2_compare.load()
+ assert_image_similar(image2_scale2, image2_scale2_compare, 10)
diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py
new file mode 100644
index 000000000..4e06a732e
--- /dev/null
+++ b/Tests/test_file_fli.py
@@ -0,0 +1,14 @@
+from tester import *
+
+from PIL import Image
+
+# sample ppm stream
+file = "Images/lena.fli"
+data = open(file, "rb").read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "P")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "FLI")
diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py
new file mode 100644
index 000000000..3a6478e2a
--- /dev/null
+++ b/Tests/test_file_gif.py
@@ -0,0 +1,48 @@
+from tester import *
+
+from PIL import Image
+
+codecs = dir(Image.core)
+
+if "gif_encoder" not in codecs or "gif_decoder" not in codecs:
+ skip("gif support not available") # can this happen?
+
+# sample gif stream
+file = "Images/lena.gif"
+with open(file, "rb") as f:
+ data = f.read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "P")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "GIF")
+
+def test_optimize():
+ def test(optimize):
+ im = Image.new("L", (1, 1), 0)
+ file = BytesIO()
+ im.save(file, "GIF", optimize=optimize)
+ return len(file.getvalue())
+ assert_equal(test(0), 800)
+ assert_equal(test(1), 38)
+
+def test_roundtrip():
+ out = tempfile('temp.gif')
+ im = lena()
+ im.save(out)
+ reread = Image.open(out)
+
+ assert_image_similar(reread.convert('RGB'), im, 50)
+
+def test_roundtrip2():
+ #see https://github.com/python-imaging/Pillow/issues/403
+ out = 'temp.gif'#tempfile('temp.gif')
+ im = Image.open('Images/lena.gif')
+ im2 = im.copy()
+ im2.save(out)
+ reread = Image.open(out)
+
+ assert_image_similar(reread.convert('RGB'), lena(), 50)
+
diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py
new file mode 100644
index 000000000..e0db34acc
--- /dev/null
+++ b/Tests/test_file_ico.py
@@ -0,0 +1,14 @@
+from tester import *
+
+from PIL import Image
+
+# sample ppm stream
+file = "Images/lena.ico"
+data = open(file, "rb").read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGBA")
+ assert_equal(im.size, (16, 16))
+ assert_equal(im.format, "ICO")
diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py
new file mode 100644
index 000000000..de1c3f0e1
--- /dev/null
+++ b/Tests/test_file_jpeg.py
@@ -0,0 +1,193 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageFile
+
+codecs = dir(Image.core)
+
+if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs:
+ skip("jpeg support not available")
+
+# sample jpeg stream
+file = "Images/lena.jpg"
+data = open(file, "rb").read()
+
+def roundtrip(im, **options):
+ out = BytesIO()
+ im.save(out, "JPEG", **options)
+ bytes = out.tell()
+ out.seek(0)
+ im = Image.open(out)
+ im.bytes = bytes # for testing only
+ return im
+
+# --------------------------------------------------------------------
+
+def test_sanity():
+
+ # internal version number
+ assert_match(Image.core.jpeglib_version, "\d+\.\d+$")
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "JPEG")
+
+# --------------------------------------------------------------------
+
+def test_app():
+ # Test APP/COM reader (@PIL135)
+ im = Image.open(file)
+ assert_equal(im.applist[0],
+ ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00"))
+ assert_equal(im.applist[1], ("COM", b"Python Imaging Library"))
+ assert_equal(len(im.applist), 2)
+
+def test_cmyk():
+ # Test CMYK handling. Thanks to Tim and Charlie for test data,
+ # Michael for getting me to look one more time.
+ file = "Tests/images/pil_sample_cmyk.jpg"
+ im = Image.open(file)
+ # the source image has red pixels in the upper left corner.
+ c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
+ assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0)
+ # the opposite corner is black
+ c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))]
+ assert_true(k > 0.9)
+ # roundtrip, and check again
+ im = roundtrip(im)
+ c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))]
+ assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0)
+ c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))]
+ assert_true(k > 0.9)
+
+def test_dpi():
+ def test(xdpi, ydpi=None):
+ im = Image.open(file)
+ im = roundtrip(im, dpi=(xdpi, ydpi or xdpi))
+ return im.info.get("dpi")
+ assert_equal(test(72), (72, 72))
+ assert_equal(test(300), (300, 300))
+ assert_equal(test(100, 200), (100, 200))
+ assert_equal(test(0), None) # square pixels
+
+def test_icc():
+ # Test ICC support
+ im1 = Image.open("Tests/images/rgb.jpg")
+ icc_profile = im1.info["icc_profile"]
+ assert_equal(len(icc_profile), 3144)
+ # Roundtrip via physical file.
+ file = tempfile("temp.jpg")
+ im1.save(file, icc_profile=icc_profile)
+ im2 = Image.open(file)
+ assert_equal(im2.info.get("icc_profile"), icc_profile)
+ # Roundtrip via memory buffer.
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), icc_profile=icc_profile)
+ assert_image_equal(im1, im2)
+ assert_false(im1.info.get("icc_profile"))
+ assert_true(im2.info.get("icc_profile"))
+
+def test_icc_big():
+ # Make sure that the "extra" support handles large blocks
+ def test(n):
+ # The ICC APP marker can store 65519 bytes per marker, so
+ # using a 4-byte test code should allow us to detect out of
+ # order issues.
+ icc_profile = (b"Test"*int(n/4+1))[:n]
+ assert len(icc_profile) == n # sanity
+ im1 = roundtrip(lena(), icc_profile=icc_profile)
+ assert_equal(im1.info.get("icc_profile"), icc_profile or None)
+ test(0); test(1)
+ test(3); test(4); test(5)
+ test(65533-14) # full JPEG marker block
+ test(65533-14+1) # full block plus one byte
+ test(ImageFile.MAXBLOCK) # full buffer block
+ test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte
+ test(ImageFile.MAXBLOCK*4+3) # large block
+
+def test_optimize():
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), optimize=1)
+ assert_image_equal(im1, im2)
+ assert_true(im1.bytes >= im2.bytes)
+
+def test_optimize_large_buffer():
+ #https://github.com/python-imaging/Pillow/issues/148
+ f = tempfile('temp.jpg')
+ # this requires ~ 1.5x Image.MAXBLOCK
+ im = Image.new("RGB", (4096,4096), 0xff3333)
+ im.save(f, format="JPEG", optimize=True)
+
+def test_progressive():
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), progressive=True)
+ assert_image_equal(im1, im2)
+ assert_true(im1.bytes >= im2.bytes)
+
+def test_progressive_large_buffer():
+ f = tempfile('temp.jpg')
+ # this requires ~ 1.5x Image.MAXBLOCK
+ im = Image.new("RGB", (4096,4096), 0xff3333)
+ im.save(f, format="JPEG", progressive=True)
+
+def test_large_exif():
+ #https://github.com/python-imaging/Pillow/issues/148
+ f = tempfile('temp.jpg')
+ im = lena()
+ im.save(f,'JPEG', quality=90, exif=b"1"*65532)
+
+def test_progressive():
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), progressive=1)
+ im3 = roundtrip(lena(), progression=1) # compatibility
+ assert_image_equal(im1, im2)
+ assert_image_equal(im1, im3)
+ assert_false(im1.info.get("progressive"))
+ assert_false(im1.info.get("progression"))
+ assert_true(im2.info.get("progressive"))
+ assert_true(im2.info.get("progression"))
+ assert_true(im3.info.get("progressive"))
+ assert_true(im3.info.get("progression"))
+
+def test_quality():
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), quality=50)
+ assert_image(im1, im2.mode, im2.size)
+ assert_true(im1.bytes >= im2.bytes)
+
+def test_smooth():
+ im1 = roundtrip(lena())
+ im2 = roundtrip(lena(), smooth=100)
+ assert_image(im1, im2.mode, im2.size)
+
+def test_subsampling():
+ def getsampling(im):
+ layer = im.layer
+ return layer[0][1:3] + layer[1][1:3] + layer[2][1:3]
+ # experimental API
+ im = roundtrip(lena(), subsampling=-1) # default
+ assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling=0) # 4:4:4
+ assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling=1) # 4:2:2
+ assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling=2) # 4:1:1
+ assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling=3) # default (undefined)
+ assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
+
+ im = roundtrip(lena(), subsampling="4:4:4")
+ assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling="4:2:2")
+ assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1))
+ im = roundtrip(lena(), subsampling="4:1:1")
+ assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1))
+
+ assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1"))
+
+def test_exif():
+ im = Image.open("Tests/images/pil_sample_rgb.jpg")
+ info = im._getexif()
+ assert_equal(info[305], 'Adobe Photoshop CS Macintosh')
diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py
new file mode 100644
index 000000000..a94257bc0
--- /dev/null
+++ b/Tests/test_file_libtiff.py
@@ -0,0 +1,297 @@
+from tester import *
+
+from PIL import Image, TiffImagePlugin
+
+codecs = dir(Image.core)
+
+if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs:
+ skip("tiff support not available")
+
+def _assert_noerr(im):
+ """Helper tests that assert basic sanity about the g4 tiff reading"""
+ #1 bit
+ assert_equal(im.mode, "1")
+
+ # Does the data actually load
+ assert_no_exception(lambda: im.load())
+ assert_no_exception(lambda: im.getdata())
+
+ try:
+ assert_equal(im._compression, 'group4')
+ except:
+ print("No _compression")
+ print (dir(im))
+
+ # can we write it back out, in a different form.
+ out = tempfile("temp.png")
+ assert_no_exception(lambda: im.save(out))
+
+def test_g4_tiff():
+ """Test the ordinary file path load path"""
+
+ file = "Tests/images/lena_g4_500.tif"
+ im = Image.open(file)
+
+ assert_equal(im.size, (500,500))
+ _assert_noerr(im)
+
+def test_g4_large():
+ file = "Tests/images/pport_g4.tif"
+ im = Image.open(file)
+ _assert_noerr(im)
+
+def test_g4_tiff_file():
+ """Testing the string load path"""
+
+ file = "Tests/images/lena_g4_500.tif"
+ with open(file,'rb') as f:
+ im = Image.open(f)
+
+ assert_equal(im.size, (500,500))
+ _assert_noerr(im)
+
+def test_g4_tiff_bytesio():
+ """Testing the stringio loading code path"""
+ from io import BytesIO
+ file = "Tests/images/lena_g4_500.tif"
+ s = BytesIO()
+ with open(file,'rb') as f:
+ s.write(f.read())
+ s.seek(0)
+ im = Image.open(s)
+
+ assert_equal(im.size, (500,500))
+ _assert_noerr(im)
+
+def test_g4_eq_png():
+ """ Checking that we're actually getting the data that we expect"""
+ png = Image.open('Tests/images/lena_bw_500.png')
+ g4 = Image.open('Tests/images/lena_g4_500.tif')
+
+ assert_image_equal(g4, png)
+
+# see https://github.com/python-imaging/Pillow/issues/279
+def test_g4_fillorder_eq_png():
+ """ Checking that we're actually getting the data that we expect"""
+ png = Image.open('Tests/images/g4-fillorder-test.png')
+ g4 = Image.open('Tests/images/g4-fillorder-test.tif')
+
+ assert_image_equal(g4, png)
+
+def test_g4_write():
+ """Checking to see that the saved image is the same as what we wrote"""
+ file = "Tests/images/lena_g4_500.tif"
+ orig = Image.open(file)
+
+ out = tempfile("temp.tif")
+ rot = orig.transpose(Image.ROTATE_90)
+ assert_equal(rot.size,(500,500))
+ rot.save(out)
+
+ reread = Image.open(out)
+ assert_equal(reread.size,(500,500))
+ _assert_noerr(reread)
+ assert_image_equal(reread, rot)
+ assert_equal(reread.info['compression'], 'group4')
+
+ assert_equal(reread.info['compression'], orig.info['compression'])
+
+ assert_false(orig.tobytes() == reread.tobytes())
+
+def test_adobe_deflate_tiff():
+ file = "Tests/images/tiff_adobe_deflate.tif"
+ im = Image.open(file)
+
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (278, 374))
+ assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0))
+ assert_no_exception(lambda: im.load())
+
+def test_write_metadata():
+ """ Test metadata writing through libtiff """
+ img = Image.open('Tests/images/lena_g4.tif')
+ f = tempfile('temp.tiff')
+
+ img.save(f, tiffinfo = img.tag)
+
+ loaded = Image.open(f)
+
+ original = img.tag.named()
+ reloaded = loaded.tag.named()
+
+ # PhotometricInterpretation is set from SAVE_INFO, not the original image.
+ ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation']
+
+ for tag, value in reloaded.items():
+ if tag not in ignored:
+ if tag.endswith('Resolution'):
+ val = original[tag]
+ assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1],
+ msg="%s didn't roundtrip" % tag)
+ else:
+ assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
+
+ for tag, value in original.items():
+ if tag not in ignored:
+ if tag.endswith('Resolution'):
+ val = reloaded[tag]
+ assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1],
+ msg="%s didn't roundtrip" % tag)
+ else:
+ assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
+
+
+def test_g3_compression():
+ i = Image.open('Tests/images/lena_g4_500.tif')
+ out = tempfile("temp.tif")
+ i.save(out, compression='group3')
+
+ reread = Image.open(out)
+ assert_equal(reread.info['compression'], 'group3')
+ assert_image_equal(reread, i)
+
+def test_little_endian():
+ im = Image.open('Tests/images/16bit.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/16bit.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])
+
+def test_12bit_rawmode():
+ """ Are we generating the same interpretation of the image as Imagemagick is? """
+ TiffImagePlugin.READ_LIBTIFF = True
+ #Image.DEBUG = True
+ im = Image.open('Tests/images/12bit.cropped.tif')
+ im.load()
+ TiffImagePlugin.READ_LIBTIFF = False
+ # to make the target --
+ # convert 12bit.cropped.tif -depth 16 tmp.tif
+ # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
+ # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
+ # so we need to unshift so that the integer values are the same.
+
+ im2 = Image.open('Tests/images/12in16bit.tif')
+
+ if Image.DEBUG:
+ print (im.getpixel((0,0)))
+ print (im.getpixel((0,1)))
+ print (im.getpixel((0,2)))
+
+ print (im2.getpixel((0,0)))
+ print (im2.getpixel((0,1)))
+ print (im2.getpixel((0,2)))
+
+ assert_image_equal(im, im2)
+
+def test_blur():
+ # test case from irc, how to do blur on b/w image and save to compressed tif.
+ from PIL import ImageFilter
+ out = tempfile('temp.tif')
+ im = Image.open('Tests/images/pport_g4.tif')
+ im = im.convert('L')
+
+ im=im.filter(ImageFilter.GaussianBlur(4))
+ im.save(out, compression='tiff_adobe_deflate')
+
+ im2 = Image.open(out)
+ im2.load()
+
+ assert_image_equal(im, im2)
+
+
+def test_compressions():
+ im = lena('RGB')
+ out = tempfile('temp.tif')
+
+ TiffImagePlugin.READ_LIBTIFF = True
+ TiffImagePlugin.WRITE_LIBTIFF = True
+
+ for compression in ('packbits', 'tiff_lzw'):
+ im.save(out, compression=compression)
+ im2 = Image.open(out)
+ assert_image_equal(im, im2)
+
+ im.save(out, compression='jpeg')
+ im2 = Image.open(out)
+ assert_image_similar(im, im2, 30)
+
+ TiffImagePlugin.READ_LIBTIFF = False
+ TiffImagePlugin.WRITE_LIBTIFF = False
+
+
+
+
+def test_cmyk_save():
+ im = lena('CMYK')
+ out = tempfile('temp.tif')
+
+ im.save(out, compression='tiff_adobe_deflate')
+ im2 = Image.open(out)
+ assert_image_equal(im, im2)
+
+def xtest_bw_compression_wRGB():
+ """ This test passes, but when running all tests causes a failure due to
+ output on stderr from the error thrown by libtiff. We need to capture that
+ but not now"""
+
+ im = lena('RGB')
+ out = tempfile('temp.tif')
+
+ assert_exception(IOError, lambda: im.save(out, compression='tiff_ccitt'))
+ assert_exception(IOError, lambda: im.save(out, compression='group3'))
+ assert_exception(IOError, lambda: im.save(out, compression='group4'))
+
diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py
new file mode 100644
index 000000000..2ad71d6e6
--- /dev/null
+++ b/Tests/test_file_libtiff_small.py
@@ -0,0 +1,52 @@
+from tester import *
+
+from PIL import Image
+
+from test_file_libtiff import _assert_noerr
+
+codecs = dir(Image.core)
+
+if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs:
+ skip("tiff support not available")
+
+""" The small lena image was failing on open in the libtiff
+ decoder because the file pointer was set to the wrong place
+ by a spurious seek. It wasn't failing with the byteio method.
+
+ It was fixed by forcing an lseek to the beginning of the
+ file just before reading in libtiff. These tests remain
+ to ensure that it stays fixed. """
+
+
+def test_g4_lena_file():
+ """Testing the open file load path"""
+
+ file = "Tests/images/lena_g4.tif"
+ with open(file,'rb') as f:
+ im = Image.open(f)
+
+ assert_equal(im.size, (128,128))
+ _assert_noerr(im)
+
+def test_g4_lena_bytesio():
+ """Testing the bytesio loading code path"""
+ from io import BytesIO
+ file = "Tests/images/lena_g4.tif"
+ s = BytesIO()
+ with open(file,'rb') as f:
+ s.write(f.read())
+ s.seek(0)
+ im = Image.open(s)
+
+ assert_equal(im.size, (128,128))
+ _assert_noerr(im)
+
+def test_g4_lena():
+ """The 128x128 lena image fails for some reason. Investigating"""
+
+ file = "Tests/images/lena_g4.tif"
+ im = Image.open(file)
+
+ assert_equal(im.size, (128,128))
+ _assert_noerr(im)
+
diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py
new file mode 100644
index 000000000..7ed200e43
--- /dev/null
+++ b/Tests/test_file_msp.py
@@ -0,0 +1,15 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ file = tempfile("temp.msp")
+
+ lena("1").save(file)
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "1")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "MSP")
diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py
new file mode 100644
index 000000000..73d358229
--- /dev/null
+++ b/Tests/test_file_pcx.py
@@ -0,0 +1,39 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ file = tempfile("temp.pcx")
+
+ lena("1").save(file)
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "1")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "PCX")
+
+ lena("1").save(file)
+ im = Image.open(file)
+
+ lena("L").save(file)
+ im = Image.open(file)
+
+ lena("P").save(file)
+ im = Image.open(file)
+
+ lena("RGB").save(file)
+ im = Image.open(file)
+
+def test_pil184():
+ # Check reading of files where xmin/xmax is not zero.
+
+ file = "Tests/images/pil184.pcx"
+ im = Image.open(file)
+
+ assert_equal(im.size, (447, 144))
+ assert_equal(im.tile[0][1], (0, 0, 447, 144))
+
+ # Make sure all pixels are either 0 or 255.
+ assert_equal(im.histogram()[0] + im.histogram()[255], 447*144)
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
new file mode 100644
index 000000000..6a5954b79
--- /dev/null
+++ b/Tests/test_file_png.py
@@ -0,0 +1,252 @@
+from tester import *
+
+from PIL import Image
+from PIL import PngImagePlugin
+import zlib
+
+codecs = dir(Image.core)
+
+if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
+ skip("zip/deflate support not available")
+
+# sample png stream
+
+file = "Images/lena.png"
+data = open(file, "rb").read()
+
+# stuff to create inline PNG images
+
+MAGIC = PngImagePlugin._MAGIC
+
+def chunk(cid, *data):
+ file = BytesIO()
+ PngImagePlugin.putchunk(*(file, cid) + data)
+ return file.getvalue()
+
+o32 = PngImagePlugin.o32
+
+IHDR = chunk(b"IHDR", o32(1), o32(1), b'\x08\x02', b'\0\0\0')
+IDAT = chunk(b"IDAT")
+IEND = chunk(b"IEND")
+
+HEAD = MAGIC + IHDR
+TAIL = IDAT + IEND
+
+def load(data):
+ return Image.open(BytesIO(data))
+
+def roundtrip(im, **options):
+ out = BytesIO()
+ im.save(out, "PNG", **options)
+ out.seek(0)
+ return Image.open(out)
+
+# --------------------------------------------------------------------
+
+def test_sanity():
+
+ # internal version number
+ assert_match(Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$")
+
+ file = tempfile("temp.png")
+
+ lena("RGB").save(file)
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "PNG")
+
+ lena("1").save(file)
+ im = Image.open(file)
+
+ lena("L").save(file)
+ im = Image.open(file)
+
+ lena("P").save(file)
+ im = Image.open(file)
+
+ lena("RGB").save(file)
+ im = Image.open(file)
+
+ lena("I").save(file)
+ im = Image.open(file)
+
+# --------------------------------------------------------------------
+
+def test_broken():
+ # Check reading of totally broken files. In this case, the test
+ # file was checked into Subversion as a text file.
+
+ file = "Tests/images/broken.png"
+ assert_exception(IOError, lambda: Image.open(file))
+
+def test_bad_text():
+ # Make sure PIL can read malformed tEXt chunks (@PIL152)
+
+ im = load(HEAD + chunk(b'tEXt') + TAIL)
+ assert_equal(im.info, {})
+
+ im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL)
+ assert_equal(im.info, {'spam': 'egg'})
+
+ im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL)
+ assert_equal(im.info, {'spam': 'egg\x00'})
+
+def test_bad_ztxt():
+ # Test reading malformed zTXt chunks (python-imaging/Pillow#318)
+
+ im = load(HEAD + chunk(b'zTXt') + TAIL)
+ assert_equal(im.info, {})
+
+ im = load(HEAD + chunk(b'zTXt', b'spam') + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'zTXt', b'spam\0') + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'zTXt', b'spam\0\0') + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')[:1]) + TAIL)
+ assert_equal(im.info, {'spam': ''})
+
+ im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')) + TAIL)
+ assert_equal(im.info, {'spam': 'egg'})
+
+def test_interlace():
+
+ file = "Tests/images/pil123p.png"
+ im = Image.open(file)
+
+ assert_image(im, "P", (162, 150))
+ assert_true(im.info.get("interlace"))
+
+ assert_no_exception(lambda: im.load())
+
+ file = "Tests/images/pil123rgba.png"
+ im = Image.open(file)
+
+ assert_image(im, "RGBA", (162, 150))
+ assert_true(im.info.get("interlace"))
+
+ assert_no_exception(lambda: im.load())
+
+def test_load_transparent_p():
+ file = "Tests/images/pil123p.png"
+ im = Image.open(file)
+
+ assert_image(im, "P", (162, 150))
+ im = im.convert("RGBA")
+ assert_image(im, "RGBA", (162, 150))
+
+ # image has 124 uniqe qlpha values
+ assert_equal(len(im.split()[3].getcolors()), 124)
+
+def test_load_transparent_rgb():
+ file = "Tests/images/rgb_trns.png"
+ im = Image.open(file)
+
+ assert_image(im, "RGB", (64, 64))
+ im = im.convert("RGBA")
+ assert_image(im, "RGBA", (64, 64))
+
+ # image has 876 transparent pixels
+ assert_equal(im.split()[3].getcolors()[0][0], 876)
+
+def test_save_p_transparent_palette():
+ in_file = "Tests/images/pil123p.png"
+ im = Image.open(in_file)
+
+ file = tempfile("temp.png")
+ assert_no_exception(lambda: im.save(file))
+
+def test_save_p_single_transparency():
+ in_file = "Tests/images/p_trns_single.png"
+ im = Image.open(in_file)
+
+ file = tempfile("temp.png")
+ assert_no_exception(lambda: im.save(file))
+
+def test_save_l_transparency():
+ in_file = "Tests/images/l_trns.png"
+ im = Image.open(in_file)
+
+ file = tempfile("temp.png")
+ assert_no_exception(lambda: im.save(file))
+
+ # There are 559 transparent pixels.
+ im = im.convert('RGBA')
+ assert_equal(im.split()[3].getcolors()[0][0], 559)
+
+def test_save_rgb_single_transparency():
+ in_file = "Tests/images/caption_6_33_22.png"
+ im = Image.open(in_file)
+
+ file = tempfile("temp.png")
+ assert_no_exception(lambda: im.save(file))
+
+def test_load_verify():
+ # Check open/load/verify exception (@PIL150)
+
+ im = Image.open("Images/lena.png")
+ assert_no_exception(lambda: im.verify())
+
+ im = Image.open("Images/lena.png")
+ im.load()
+ assert_exception(RuntimeError, lambda: im.verify())
+
+def test_roundtrip_dpi():
+ # Check dpi roundtripping
+
+ im = Image.open(file)
+
+ im = roundtrip(im, dpi=(100, 100))
+ assert_equal(im.info["dpi"], (100, 100))
+
+def test_roundtrip_text():
+ # Check text roundtripping
+
+ im = Image.open(file)
+
+ info = PngImagePlugin.PngInfo()
+ info.add_text("TXT", "VALUE")
+ info.add_text("ZIP", "VALUE", 1)
+
+ im = roundtrip(im, pnginfo=info)
+ assert_equal(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'})
+ assert_equal(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'})
+
+def test_scary():
+ # Check reading of evil PNG file. For information, see:
+ # http://scary.beasts.org/security/CESA-2004-001.txt
+ # The first byte is removed from pngtest_bad.png
+ # to avoid classification as malware.
+
+ with open("Tests/images/pngtest_bad.png.bin", 'rb') as fd:
+ data = b'\x89' + fd.read()
+
+ pngfile = BytesIO(data)
+ assert_exception(IOError, lambda: Image.open(pngfile))
+
+def test_trns_rgb():
+ # Check writing and reading of tRNS chunks for RGB images.
+ # Independent file sample provided by Sebastian Spaeth.
+
+ file = "Tests/images/caption_6_33_22.png"
+ im = Image.open(file)
+ assert_equal(im.info["transparency"], (248, 248, 248))
+
+ # check saving transparency by default
+ im = roundtrip(im)
+ assert_equal(im.info["transparency"], (248, 248, 248))
+
+ im = roundtrip(im, transparency=(0, 1, 2))
+ assert_equal(im.info["transparency"], (0, 1, 2))
diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py
new file mode 100644
index 000000000..fccb94905
--- /dev/null
+++ b/Tests/test_file_ppm.py
@@ -0,0 +1,14 @@
+from tester import *
+
+from PIL import Image
+
+# sample ppm stream
+file = "Images/lena.ppm"
+data = open(file, "rb").read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "PPM")
diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py
new file mode 100644
index 000000000..ef2d40594
--- /dev/null
+++ b/Tests/test_file_psd.py
@@ -0,0 +1,14 @@
+from tester import *
+
+from PIL import Image
+
+# sample ppm stream
+file = "Images/lena.psd"
+data = open(file, "rb").read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "PSD")
diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py
new file mode 100644
index 000000000..fa33d3802
--- /dev/null
+++ b/Tests/test_file_tar.py
@@ -0,0 +1,28 @@
+from tester import *
+
+from PIL import Image, TarIO
+
+codecs = dir(Image.core)
+if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs:
+ skip("neither jpeg nor zip support not available")
+
+# sample ppm stream
+tarfile = "Images/lena.tar"
+
+def test_sanity():
+ if "zip_decoder" in codecs:
+ tar = TarIO.TarIO(tarfile, 'lena.png')
+ im = Image.open(tar)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "PNG")
+
+ if "jpeg_decoder" in codecs:
+ tar = TarIO.TarIO(tarfile, 'lena.jpg')
+ im = Image.open(tar)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "JPEG")
+
diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py
new file mode 100644
index 000000000..9041b2046
--- /dev/null
+++ b/Tests/test_file_tiff.py
@@ -0,0 +1,130 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ file = tempfile("temp.tif")
+
+ lena("RGB").save(file)
+
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "TIFF")
+
+ lena("1").save(file)
+ im = Image.open(file)
+
+ lena("L").save(file)
+ im = Image.open(file)
+
+ lena("P").save(file)
+ im = Image.open(file)
+
+ lena("RGB").save(file)
+ im = Image.open(file)
+
+ lena("I").save(file)
+ im = Image.open(file)
+
+def test_mac_tiff():
+ # Read RGBa images from Mac OS X [@PIL136]
+
+ file = "Tests/images/pil136.tiff"
+ im = Image.open(file)
+
+ assert_equal(im.mode, "RGBA")
+ assert_equal(im.size, (55, 43))
+ assert_equal(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))])
+ assert_no_exception(lambda: im.load())
+
+def test_gimp_tiff():
+ # Read TIFF JPEG images from GIMP [@PIL168]
+
+ codecs = dir(Image.core)
+ if "jpeg_decoder" not in codecs:
+ skip("jpeg support not available")
+
+ file = "Tests/images/pil168.tif"
+ im = Image.open(file)
+
+ assert_equal(im.mode, "RGB")
+ assert_equal(im.size, (256, 256))
+ assert_equal(im.tile, [
+ ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')),
+ ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')),
+ ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')),
+ ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')),
+ ])
+ assert_no_exception(lambda: im.load())
+
+def test_xyres_tiff():
+ from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION
+ file = "Tests/images/pil168.tif"
+ im = Image.open(file)
+ assert isinstance(im.tag.tags[X_RESOLUTION][0], tuple)
+ assert isinstance(im.tag.tags[Y_RESOLUTION][0], tuple)
+ #Try to read a file where X,Y_RESOLUTION are ints
+ im.tag.tags[X_RESOLUTION] = (72,)
+ im.tag.tags[Y_RESOLUTION] = (72,)
+ im._setup()
+ assert_equal(im.info['dpi'], (72., 72.))
+
+
+def test_little_endian():
+ im = Image.open('Tests/images/16bit.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/16bit.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')
+
+
+def test_12bit_rawmode():
+ """ Are we generating the same interpretation of the image as Imagemagick is? """
+
+ #Image.DEBUG = True
+ im = Image.open('Tests/images/12bit.cropped.tif')
+
+ # to make the target --
+ # convert 12bit.cropped.tif -depth 16 tmp.tif
+ # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
+ # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
+ # so we need to unshift so that the integer values are the same.
+
+ im2 = Image.open('Tests/images/12in16bit.tif')
+
+ if Image.DEBUG:
+ print (im.getpixel((0,0)))
+ print (im.getpixel((0,1)))
+ print (im.getpixel((0,2)))
+
+ print (im2.getpixel((0,0)))
+ print (im2.getpixel((0,1)))
+ print (im2.getpixel((0,2)))
+
+ assert_image_equal(im, im2)
diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py
new file mode 100644
index 000000000..354eb972f
--- /dev/null
+++ b/Tests/test_file_tiff_metadata.py
@@ -0,0 +1,80 @@
+from tester import *
+from PIL import Image, TiffImagePlugin, TiffTags
+
+tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys()))
+
+def test_rt_metadata():
+ """ Test writing arbitray metadata into the tiff image directory
+ Use case is ImageJ private tags, one numeric, one arbitrary
+ data. https://github.com/python-imaging/Pillow/issues/291
+ """
+
+ img = lena()
+
+ textdata = "This is some arbitrary metadata for a text field"
+ info = TiffImagePlugin.ImageFileDirectory()
+
+ info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata)
+ info[tag_ids['ImageJMetaData']] = textdata
+
+ f = tempfile("temp.tif")
+
+ img.save(f, tiffinfo=info)
+
+ loaded = Image.open(f)
+
+ assert_equal(loaded.tag[50838], (len(textdata),))
+ assert_equal(loaded.tag[50839], textdata)
+
+def test_read_metadata():
+ img = Image.open('Tests/images/lena_g4.tif')
+
+ known = {'YResolution': ((1207959552, 16777216),),
+ 'PlanarConfiguration': (1,),
+ 'BitsPerSample': (1,),
+ 'ImageLength': (128,),
+ 'Compression': (4,),
+ 'FillOrder': (1,),
+ 'DocumentName': 'lena.g4.tif',
+ 'RowsPerStrip': (128,),
+ 'ResolutionUnit': (1,),
+ 'PhotometricInterpretation': (0,),
+ 'PageNumber': (0, 1),
+ 'XResolution': ((1207959552, 16777216),),
+ 'ImageWidth': (128,),
+ 'Orientation': (1,),
+ 'StripByteCounts': (1796,),
+ 'SamplesPerPixel': (1,),
+ 'StripOffsets': (8,),
+ 'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'}
+
+ # assert_equal is equivalent, but less helpful in telling what's wrong.
+ named = img.tag.named()
+ for tag, value in named.items():
+ assert_equal(known[tag], value)
+
+ for tag, value in known.items():
+ assert_equal(value, named[tag])
+
+
+def test_write_metadata():
+ """ Test metadata writing through the python code """
+ img = Image.open('Tests/images/lena.tif')
+
+ f = tempfile('temp.tiff')
+ img.save(f, tiffinfo = img.tag)
+
+ loaded = Image.open(f)
+
+ original = img.tag.named()
+ reloaded = loaded.tag.named()
+
+ ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets']
+
+ for tag, value in reloaded.items():
+ if tag not in ignored:
+ assert_equal(original[tag], value, "%s didn't roundtrip" % tag)
+
+ for tag, value in original.items():
+ if tag not in ignored:
+ assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag)
diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py
new file mode 100644
index 000000000..a89dea3c4
--- /dev/null
+++ b/Tests/test_file_webp.py
@@ -0,0 +1,68 @@
+from tester import *
+
+from PIL import Image
+
+
+try:
+ from PIL import _webp
+except:
+ skip('webp support not installed')
+
+
+def test_version():
+ assert_no_exception(lambda: _webp.WebPDecoderVersion())
+ assert_no_exception(lambda: _webp.WebPDecoderBuggyAlpha())
+
+def test_read_rgb():
+
+ file_path = "Images/lena.webp"
+ image = Image.open(file_path)
+
+ assert_equal(image.mode, "RGB")
+ assert_equal(image.size, (128, 128))
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(lambda: image.load())
+ assert_no_exception(lambda: image.getdata())
+
+ # generated with: dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm
+ target = Image.open('Tests/images/lena_webp_bits.ppm')
+ assert_image_similar(image, target, 20.0)
+
+
+def test_write_rgb():
+ """
+ Can we write a RGB mode file to webp without error. Does it have the bits we
+ expect?
+
+ """
+
+ temp_file = tempfile("temp.webp")
+
+ lena("RGB").save(temp_file)
+
+ image = Image.open(temp_file)
+ image.load()
+
+ assert_equal(image.mode, "RGB")
+ assert_equal(image.size, (128, 128))
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(lambda: image.load())
+ assert_no_exception(lambda: image.getdata())
+
+ # If we're using the exact same version of webp, this test should pass.
+ # but it doesn't if the webp is generated on Ubuntu and tested on Fedora.
+
+ # generated with: dwebp -ppm temp.webp -o lena_webp_write.ppm
+ #target = Image.open('Tests/images/lena_webp_write.ppm')
+ #assert_image_equal(image, target)
+
+ # This test asserts that the images are similar. If the average pixel difference
+ # between the two images is less than the epsilon value, then we're going to
+ # accept that it's a reasonable lossy version of the image. The included lena images
+ # for webp are showing ~16 on Ubuntu, the jpegs are showing ~18.
+ target = lena("RGB")
+ assert_image_similar(image, target, 20.0)
+
+
+
+
diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py
new file mode 100644
index 000000000..5ac03b9d4
--- /dev/null
+++ b/Tests/test_file_webp_alpha.py
@@ -0,0 +1,82 @@
+from tester import *
+
+from PIL import Image
+
+try:
+ from PIL import _webp
+except:
+ skip('webp support not installed')
+
+
+if _webp.WebPDecoderBuggyAlpha():
+ skip("Buggy early version of webp installed, not testing transparency")
+
+def test_read_rgba():
+ # Generated with `cwebp transparent.png -o transparent.webp`
+ file_path = "Images/transparent.webp"
+ image = Image.open(file_path)
+
+ assert_equal(image.mode, "RGBA")
+ assert_equal(image.size, (200, 150))
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(lambda: image.load())
+ assert_no_exception(lambda: image.getdata())
+
+ orig_bytes = image.tobytes()
+
+ target = Image.open('Images/transparent.png')
+ assert_image_similar(image, target, 20.0)
+
+
+def test_write_lossless_rgb():
+ temp_file = tempfile("temp.webp")
+ #temp_file = "temp.webp"
+
+ pil_image = lena('RGBA')
+
+ mask = Image.new("RGBA", (64, 64), (128,128,128,128))
+ pil_image.paste(mask, (0,0), mask) # add some partially transparent bits.
+
+ pil_image.save(temp_file, lossless=True)
+
+ image = Image.open(temp_file)
+ image.load()
+
+ assert_equal(image.mode, "RGBA")
+ assert_equal(image.size, pil_image.size)
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(lambda: image.load())
+ assert_no_exception(lambda: image.getdata())
+
+
+ assert_image_equal(image, pil_image)
+
+def test_write_rgba():
+ """
+ Can we write a RGBA mode file to webp without error. Does it have the bits we
+ expect?
+
+ """
+
+ temp_file = tempfile("temp.webp")
+
+ pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20))
+ pil_image.save(temp_file)
+
+ if _webp.WebPDecoderBuggyAlpha():
+ return
+
+ image = Image.open(temp_file)
+ image.load()
+
+ assert_equal(image.mode, "RGBA")
+ assert_equal(image.size, (10, 10))
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(image.load)
+ assert_no_exception(image.getdata)
+
+ assert_image_similar(image, pil_image, 1.0)
+
+
+
+
diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py
new file mode 100644
index 000000000..ca2b5af19
--- /dev/null
+++ b/Tests/test_file_webp_lossless.py
@@ -0,0 +1,33 @@
+from tester import *
+
+from PIL import Image
+
+
+try:
+ from PIL import _webp
+except:
+ skip('webp support not installed')
+
+
+if (_webp.WebPDecoderVersion() < 0x0200):
+ skip('lossless not included')
+
+def test_write_lossless_rgb():
+ temp_file = tempfile("temp.webp")
+
+ lena("RGB").save(temp_file, lossless=True)
+
+ image = Image.open(temp_file)
+ image.load()
+
+ assert_equal(image.mode, "RGB")
+ assert_equal(image.size, (128, 128))
+ assert_equal(image.format, "WEBP")
+ assert_no_exception(lambda: image.load())
+ assert_no_exception(lambda: image.getdata())
+
+
+ assert_image_equal(image, lena("RGB"))
+
+
+
diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py
new file mode 100644
index 000000000..b4146c3ee
--- /dev/null
+++ b/Tests/test_file_webp_metadata.py
@@ -0,0 +1,101 @@
+from tester import *
+
+from PIL import Image
+
+try:
+ from PIL import _webp
+ if not _webp.HAVE_WEBPMUX:
+ skip('webpmux support not installed')
+except:
+ skip('webp support not installed')
+
+
+
+def test_read_exif_metadata():
+
+ file_path = "Images/flower.webp"
+ image = Image.open(file_path)
+
+ assert_equal(image.format, "WEBP")
+ exif_data = image.info.get("exif", None)
+ assert_true(exif_data)
+
+ exif = image._getexif()
+
+ #camera make
+ assert_equal(exif[271], "Canon")
+
+ jpeg_image = Image.open('Tests/images/flower.jpg')
+ expected_exif = jpeg_image.info['exif']
+
+ assert_equal(exif_data, expected_exif)
+
+
+def test_write_exif_metadata():
+ file_path = "Tests/images/flower.jpg"
+ image = Image.open(file_path)
+ expected_exif = image.info['exif']
+
+ buffer = BytesIO()
+
+ image.save(buffer, "webp", exif=expected_exif)
+
+ buffer.seek(0)
+ webp_image = Image.open(buffer)
+
+ webp_exif = webp_image.info.get('exif', None)
+ assert_true(webp_exif)
+ if webp_exif:
+ assert_equal(webp_exif, expected_exif, "Webp Exif didn't match")
+
+
+def test_read_icc_profile():
+
+ file_path = "Images/flower2.webp"
+ image = Image.open(file_path)
+
+ assert_equal(image.format, "WEBP")
+ assert_true(image.info.get("icc_profile", None))
+
+ icc = image.info['icc_profile']
+
+ jpeg_image = Image.open('Tests/images/flower2.jpg')
+ expected_icc = jpeg_image.info['icc_profile']
+
+ assert_equal(icc, expected_icc)
+
+
+def test_write_icc_metadata():
+ file_path = "Tests/images/flower2.jpg"
+ image = Image.open(file_path)
+ expected_icc_profile = image.info['icc_profile']
+
+ buffer = BytesIO()
+
+ image.save(buffer, "webp", icc_profile=expected_icc_profile)
+
+ buffer.seek(0)
+ webp_image = Image.open(buffer)
+
+ webp_icc_profile = webp_image.info.get('icc_profile', None)
+
+ assert_true(webp_icc_profile)
+ if webp_icc_profile:
+ assert_equal(webp_icc_profile, expected_icc_profile, "Webp ICC didn't match")
+
+
+def test_read_no_exif():
+ file_path = "Tests/images/flower.jpg"
+ image = Image.open(file_path)
+ expected_exif = image.info['exif']
+
+ buffer = BytesIO()
+
+ image.save(buffer, "webp")
+
+ buffer.seek(0)
+ webp_image = Image.open(buffer)
+
+ assert_false(webp_image._getexif())
+
+
diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py
new file mode 100644
index 000000000..f27a3a349
--- /dev/null
+++ b/Tests/test_file_xbm.py
@@ -0,0 +1,34 @@
+from tester import *
+
+from PIL import Image
+
+PIL151 = b"""
+#define basic_width 32
+#define basic_height 32
+static char basic_bits[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
+0x80, 0xff, 0xff, 0x01, 0x40, 0x00, 0x00, 0x02,
+0x20, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x08,
+0x10, 0x00, 0x00, 0x08,
+0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08,
+0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08,
+0x20, 0x00, 0x00, 0x04,
+0x20, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x02,
+0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
+};
+"""
+
+def test_pil151():
+
+ im = Image.open(BytesIO(PIL151))
+
+ assert_no_exception(lambda: im.load())
+ assert_equal(im.mode, '1')
+ assert_equal(im.size, (32, 32))
diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py
new file mode 100644
index 000000000..44135d028
--- /dev/null
+++ b/Tests/test_file_xpm.py
@@ -0,0 +1,14 @@
+from tester import *
+
+from PIL import Image
+
+# sample ppm stream
+file = "Images/lena.xpm"
+data = open(file, "rb").read()
+
+def test_sanity():
+ im = Image.open(file)
+ im.load()
+ assert_equal(im.mode, "P")
+ assert_equal(im.size, (128, 128))
+ assert_equal(im.format, "XPM")
diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py
new file mode 100644
index 000000000..366bb4468
--- /dev/null
+++ b/Tests/test_font_bdf.py
@@ -0,0 +1,13 @@
+from tester import *
+
+from PIL import Image, FontFile, BdfFontFile
+
+filename = "Images/courB08.bdf"
+
+def test_sanity():
+
+ file = open(filename, "rb")
+ font = BdfFontFile.BdfFontFile(file)
+
+ assert_true(isinstance(font, FontFile.FontFile))
+ assert_equal(len([_f for _f in font.glyph if _f]), 190)
diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py
new file mode 100644
index 000000000..60e6e0e26
--- /dev/null
+++ b/Tests/test_font_pcf.py
@@ -0,0 +1,31 @@
+from tester import *
+
+from PIL import Image, FontFile, PcfFontFile
+from PIL import ImageFont, ImageDraw
+
+codecs = dir(Image.core)
+
+if "zip_encoder" not in codecs or "zip_decoder" not in codecs:
+ skip("zlib support not available")
+
+fontname = "Tests/fonts/helvO18.pcf"
+tempname = tempfile("temp.pil", "temp.pbm")
+
+message = "hello, world"
+
+def test_sanity():
+
+ file = open(fontname, "rb")
+ font = PcfFontFile.PcfFontFile(file)
+ assert_true(isinstance(font, FontFile.FontFile))
+ assert_equal(len([_f for _f in font.glyph if _f]), 192)
+
+ font.save(tempname)
+
+def test_draw():
+
+ font = ImageFont.load(tempname)
+ image = Image.new("L", font.getsize(message), "white")
+ draw = ImageDraw.Draw(image)
+ draw.text((0, 0), message, font=font)
+ # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee")
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_image.py b/Tests/test_image.py
new file mode 100644
index 000000000..26c699e66
--- /dev/null
+++ b/Tests/test_image.py
@@ -0,0 +1,39 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ im = Image.new("L", (100, 100))
+ assert_equal(repr(im)[:45], " 8 bit lut for converting I->L images
+ see https://github.com/python-imaging/Pillow/issues/440
+ """
+
+ im = lena("I")
+ assert_no_exception(lambda: im.point(list(range(256))*256, 'L'))
diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py
new file mode 100644
index 000000000..b23f69834
--- /dev/null
+++ b/Tests/test_image_putalpha.py
@@ -0,0 +1,43 @@
+from tester import *
+
+from PIL import Image
+
+def test_interface():
+
+ im = Image.new("RGBA", (1, 1), (1, 2, 3, 0))
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 0))
+
+ im = Image.new("RGBA", (1, 1), (1, 2, 3))
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 255))
+
+ im.putalpha(Image.new("L", im.size, 4))
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4))
+
+ im.putalpha(5)
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 5))
+
+def test_promote():
+
+ im = Image.new("L", (1, 1), 1)
+ assert_equal(im.getpixel((0, 0)), 1)
+
+ im.putalpha(2)
+ assert_equal(im.mode, 'LA')
+ assert_equal(im.getpixel((0, 0)), (1, 2))
+
+ im = Image.new("RGB", (1, 1), (1, 2, 3))
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3))
+
+ im.putalpha(4)
+ assert_equal(im.mode, 'RGBA')
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4))
+
+def test_readonly():
+
+ im = Image.new("RGB", (1, 1), (1, 2, 3))
+ im.readonly = 1
+
+ im.putalpha(4)
+ assert_false(im.readonly)
+ assert_equal(im.mode, 'RGBA')
+ assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4))
diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py
new file mode 100644
index 000000000..e25359fdf
--- /dev/null
+++ b/Tests/test_image_putdata.py
@@ -0,0 +1,40 @@
+from tester import *
+
+import sys
+
+from PIL import Image
+
+def test_sanity():
+
+ im1 = lena()
+
+ data = list(im1.getdata())
+
+ im2 = Image.new(im1.mode, im1.size, 0)
+ im2.putdata(data)
+
+ assert_image_equal(im1, im2)
+
+ # readonly
+ im2 = Image.new(im1.mode, im2.size, 0)
+ im2.readonly = 1
+ im2.putdata(data)
+
+ assert_false(im2.readonly)
+ assert_image_equal(im1, im2)
+
+
+def test_long_integers():
+ # see bug-200802-systemerror
+ def put(value):
+ im = Image.new("RGBA", (1, 1))
+ im.putdata([value])
+ return im.getpixel((0, 0))
+ assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255))
+ assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255))
+ assert_equal(put(-1), (255, 255, 255, 255))
+ assert_equal(put(-1), (255, 255, 255, 255))
+ if sys.maxsize > 2**32:
+ assert_equal(put(sys.maxsize), (255, 255, 255, 255))
+ else:
+ assert_equal(put(sys.maxsize), (255, 255, 255, 127))
diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py
new file mode 100644
index 000000000..b7ebb8853
--- /dev/null
+++ b/Tests/test_image_putpalette.py
@@ -0,0 +1,28 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImagePalette
+
+def test_putpalette():
+ def palette(mode):
+ im = lena(mode).copy()
+ im.putpalette(list(range(256))*3)
+ p = im.getpalette()
+ if p:
+ return im.mode, p[:10]
+ return im.mode
+ assert_exception(ValueError, lambda: palette("1"))
+ assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
+ assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
+ assert_exception(ValueError, lambda: palette("I"))
+ assert_exception(ValueError, lambda: palette("F"))
+ assert_exception(ValueError, lambda: palette("RGB"))
+ assert_exception(ValueError, lambda: palette("RGBA"))
+ assert_exception(ValueError, lambda: palette("YCbCr"))
+
+def test_imagepalette():
+ im = lena("P")
+ assert_no_exception(lambda: im.putpalette(ImagePalette.negative()))
+ assert_no_exception(lambda: im.putpalette(ImagePalette.random()))
+ assert_no_exception(lambda: im.putpalette(ImagePalette.sepia()))
+ assert_no_exception(lambda: im.putpalette(ImagePalette.wedge()))
diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py
new file mode 100644
index 000000000..2b60bbd97
--- /dev/null
+++ b/Tests/test_image_putpixel.py
@@ -0,0 +1,43 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ im1 = lena()
+ im2 = Image.new(im1.mode, im1.size, 0)
+
+ for y in range(im1.size[1]):
+ for x in range(im1.size[0]):
+ pos = x, y
+ im2.putpixel(pos, im1.getpixel(pos))
+
+ assert_image_equal(im1, im2)
+
+ im2 = Image.new(im1.mode, im1.size, 0)
+ im2.readonly = 1
+
+ for y in range(im1.size[1]):
+ for x in range(im1.size[0]):
+ pos = x, y
+ im2.putpixel(pos, im1.getpixel(pos))
+
+ assert_false(im2.readonly)
+ assert_image_equal(im1, im2)
+
+ im2 = Image.new(im1.mode, im1.size, 0)
+
+ pix1 = im1.load()
+ pix2 = im2.load()
+
+ for y in range(im1.size[1]):
+ for x in range(im1.size[0]):
+ pix2[x, y] = pix1[x, y]
+
+ assert_image_equal(im1, im2)
+
+
+
+
+# see test_image_getpixel for more tests
+
diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py
new file mode 100644
index 000000000..70b5eb503
--- /dev/null
+++ b/Tests/test_image_quantize.py
@@ -0,0 +1,22 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ im = lena()
+
+ im = im.quantize()
+ assert_image(im, "P", im.size)
+
+ im = lena()
+ im = im.quantize(palette=lena("P"))
+ assert_image(im, "P", im.size)
+
+def test_octree_quantize():
+ im = lena()
+
+ im = im.quantize(100, Image.FASTOCTREE)
+ assert_image(im, "P", im.size)
+
+ assert len(im.getcolors()) == 100
\ No newline at end of file
diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py
new file mode 100644
index 000000000..4e228a396
--- /dev/null
+++ b/Tests/test_image_resize.py
@@ -0,0 +1,12 @@
+from tester import *
+
+from PIL import Image
+
+def test_resize():
+ def resize(mode, size):
+ out = lena(mode).resize(size)
+ assert_equal(out.mode, mode)
+ assert_equal(out.size, size)
+ for mode in "1", "P", "L", "RGB", "I", "F":
+ yield_test(resize, mode, (100, 100))
+ yield_test(resize, mode, (200, 200))
diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py
new file mode 100644
index 000000000..5e4782c87
--- /dev/null
+++ b/Tests/test_image_rotate.py
@@ -0,0 +1,15 @@
+from tester import *
+
+from PIL import Image
+
+def test_rotate():
+ def rotate(mode):
+ im = lena(mode)
+ out = im.rotate(45)
+ assert_equal(out.mode, mode)
+ assert_equal(out.size, im.size) # default rotate clips output
+ out = im.rotate(45, expand=1)
+ assert_equal(out.mode, mode)
+ assert_true(out.size != im.size)
+ for mode in "1", "P", "L", "RGB", "I", "F":
+ yield_test(rotate, mode)
diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py
new file mode 100644
index 000000000..7d4b6d9b3
--- /dev/null
+++ b/Tests/test_image_save.py
@@ -0,0 +1,5 @@
+from tester import *
+
+from PIL import Image
+
+success()
diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py
new file mode 100644
index 000000000..7d4b6d9b3
--- /dev/null
+++ b/Tests/test_image_seek.py
@@ -0,0 +1,5 @@
+from tester import *
+
+from PIL import Image
+
+success()
diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py
new file mode 100644
index 000000000..7d4b6d9b3
--- /dev/null
+++ b/Tests/test_image_show.py
@@ -0,0 +1,5 @@
+from tester import *
+
+from PIL import Image
+
+success()
diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py
new file mode 100644
index 000000000..07a779664
--- /dev/null
+++ b/Tests/test_image_split.py
@@ -0,0 +1,49 @@
+from tester import *
+
+from PIL import Image
+
+def test_split():
+ def split(mode):
+ layers = lena(mode).split()
+ return [(i.mode, i.size[0], i.size[1]) for i in layers]
+ assert_equal(split("1"), [('1', 128, 128)])
+ assert_equal(split("L"), [('L', 128, 128)])
+ assert_equal(split("I"), [('I', 128, 128)])
+ assert_equal(split("F"), [('F', 128, 128)])
+ assert_equal(split("P"), [('P', 128, 128)])
+ assert_equal(split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)])
+ assert_equal(split("RGBA"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)])
+ assert_equal(split("CMYK"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)])
+ assert_equal(split("YCbCr"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)])
+
+def test_split_merge():
+ def split_merge(mode):
+ return Image.merge(mode, lena(mode).split())
+ assert_image_equal(lena("1"), split_merge("1"))
+ assert_image_equal(lena("L"), split_merge("L"))
+ assert_image_equal(lena("I"), split_merge("I"))
+ assert_image_equal(lena("F"), split_merge("F"))
+ assert_image_equal(lena("P"), split_merge("P"))
+ assert_image_equal(lena("RGB"), split_merge("RGB"))
+ assert_image_equal(lena("RGBA"), split_merge("RGBA"))
+ assert_image_equal(lena("CMYK"), split_merge("CMYK"))
+ assert_image_equal(lena("YCbCr"), split_merge("YCbCr"))
+
+def test_split_open():
+ codecs = dir(Image.core)
+
+ if 'zip_encoder' in codecs:
+ file = tempfile("temp.png")
+ else:
+ file = tempfile("temp.pcx")
+
+ def split_open(mode):
+ lena(mode).save(file)
+ im = Image.open(file)
+ return len(im.split())
+ assert_equal(split_open("1"), 1)
+ assert_equal(split_open("L"), 1)
+ assert_equal(split_open("P"), 1)
+ assert_equal(split_open("RGB"), 3)
+ if 'zip_encoder' in codecs:
+ assert_equal(split_open("RGBA"), 4)
diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py
new file mode 100644
index 000000000..7d4b6d9b3
--- /dev/null
+++ b/Tests/test_image_tell.py
@@ -0,0 +1,5 @@
+from tester import *
+
+from PIL import Image
+
+success()
diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py
new file mode 100644
index 000000000..871dd1f54
--- /dev/null
+++ b/Tests/test_image_thumbnail.py
@@ -0,0 +1,36 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ im = lena()
+ im.thumbnail((100, 100))
+
+ assert_image(im, im.mode, (100, 100))
+
+def test_aspect():
+
+ im = lena()
+ im.thumbnail((100, 100))
+ assert_image(im, im.mode, (100, 100))
+
+ im = lena().resize((128, 256))
+ im.thumbnail((100, 100))
+ assert_image(im, im.mode, (50, 100))
+
+ im = lena().resize((128, 256))
+ im.thumbnail((50, 100))
+ assert_image(im, im.mode, (50, 100))
+
+ im = lena().resize((256, 128))
+ im.thumbnail((100, 100))
+ assert_image(im, im.mode, (100, 50))
+
+ im = lena().resize((256, 128))
+ im.thumbnail((100, 50))
+ assert_image(im, im.mode, (100, 50))
+
+ im = lena().resize((128, 128))
+ im.thumbnail((100, 100))
+ assert_image(im, im.mode, (100, 100))
diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py
new file mode 100644
index 000000000..6fb10dd53
--- /dev/null
+++ b/Tests/test_image_tobitmap.py
@@ -0,0 +1,15 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+
+ assert_exception(ValueError, lambda: lena().tobitmap())
+ assert_no_exception(lambda: lena().convert("1").tobitmap())
+
+ im1 = lena().convert("1")
+
+ bitmap = im1.tobitmap()
+
+ assert_true(isinstance(bitmap, bytes))
+ assert_image_equal(im1, fromstring(bitmap))
diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py
new file mode 100644
index 000000000..d42399993
--- /dev/null
+++ b/Tests/test_image_tobytes.py
@@ -0,0 +1,7 @@
+from tester import *
+
+from PIL import Image
+
+def test_sanity():
+ data = lena().tobytes()
+ assert_true(isinstance(data, bytes))
diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py
new file mode 100644
index 000000000..fdee6072f
--- /dev/null
+++ b/Tests/test_image_transform.py
@@ -0,0 +1,116 @@
+from tester import *
+
+from PIL import Image
+
+def test_extent():
+ im = lena('RGB')
+ (w,h) = im.size
+ transformed = im.transform(im.size, Image.EXTENT,
+ (0,0,
+ w//2,h//2), # ul -> lr
+ Image.BILINEAR)
+
+
+ scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
+
+ assert_image_similar(transformed, scaled, 10) # undone -- precision?
+
+def test_quad():
+ # one simple quad transform, equivalent to scale & crop upper left quad
+ im = lena('RGB')
+ (w,h) = im.size
+ transformed = im.transform(im.size, Image.QUAD,
+ (0,0,0,h//2,
+ w//2,h//2,w//2,0), # ul -> ccw around quad
+ Image.BILINEAR)
+
+ scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h))
+
+ assert_image_equal(transformed, scaled)
+
+def test_mesh():
+ # this should be a checkerboard of halfsized lenas in ul, lr
+ im = lena('RGBA')
+ (w,h) = im.size
+ transformed = im.transform(im.size, Image.MESH,
+ [((0,0,w//2,h//2), # box
+ (0,0,0,h,
+ w,h,w,0)), # ul -> ccw around quad
+ ((w//2,h//2,w,h), # box
+ (0,0,0,h,
+ w,h,w,0))], # ul -> ccw around quad
+ Image.BILINEAR)
+
+ #transformed.save('transformed.png')
+
+ scaled = im.resize((w//2, h//2), Image.BILINEAR)
+
+ checker = Image.new('RGBA', im.size)
+ checker.paste(scaled, (0,0))
+ checker.paste(scaled, (w//2,h//2))
+
+ assert_image_equal(transformed, checker)
+
+ # now, check to see that the extra area is (0,0,0,0)
+ blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0))
+
+ assert_image_equal(blank, transformed.crop((w//2,0,w,h//2)))
+ assert_image_equal(blank, transformed.crop((0,h//2,w//2,h)))
+
+def _test_alpha_premult(op):
+ # create image with half white, half black, with the black half transparent.
+ # do op,
+ # there should be no darkness in the white section.
+ im = Image.new('RGBA', (10,10), (0,0,0,0));
+ im2 = Image.new('RGBA', (5,10), (255,255,255,255));
+ im.paste(im2, (0,0))
+
+ im = op(im, (40,10))
+ im_background = Image.new('RGB', (40,10), (255,255,255))
+ im_background.paste(im, (0,0), im)
+
+ hist = im_background.histogram()
+ assert_equal(40*10, hist[-1])
+
+
+def test_alpha_premult_resize():
+
+ def op (im, sz):
+ return im.resize(sz, Image.LINEAR)
+
+ _test_alpha_premult(op)
+
+def test_alpha_premult_transform():
+
+ def op(im, sz):
+ (w,h) = im.size
+ return im.transform(sz, Image.EXTENT,
+ (0,0,
+ w,h),
+ Image.BILINEAR)
+
+ _test_alpha_premult(op)
+
+
+def test_blank_fill():
+ # attempting to hit
+ # https://github.com/python-imaging/Pillow/issues/254 reported
+ #
+ # issue is that transforms with transparent overflow area
+ # contained junk from previous images, especially on systems with
+ # constrained memory. So, attempt to fill up memory with a
+ # pattern, free it, and then run the mesh test again. Using a 1Mp
+ # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64
+ # bit 12.04 VM with 512 megs available, this fails with Pillow <
+ # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea
+ #
+ # Running by default, but I'd totally understand not doing it in
+ # the future
+
+ foo = [Image.new('RGBA',(1024,1024), (a,a,a,a))
+ for a in range(1,65)]
+
+ # Yeah. Watch some JIT optimize this out.
+ foo = None
+
+ test_mesh()
diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py
new file mode 100644
index 000000000..43b3ef9d3
--- /dev/null
+++ b/Tests/test_image_transpose.py
@@ -0,0 +1,34 @@
+from tester import *
+
+from PIL import Image
+
+FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT
+FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM
+ROTATE_90 = Image.ROTATE_90
+ROTATE_180 = Image.ROTATE_180
+ROTATE_270 = Image.ROTATE_270
+
+def test_sanity():
+
+ im = lena()
+
+ assert_no_exception(lambda: im.transpose(FLIP_LEFT_RIGHT))
+ assert_no_exception(lambda: im.transpose(FLIP_TOP_BOTTOM))
+
+ assert_no_exception(lambda: im.transpose(ROTATE_90))
+ assert_no_exception(lambda: im.transpose(ROTATE_180))
+ assert_no_exception(lambda: im.transpose(ROTATE_270))
+
+def test_roundtrip():
+
+ im = lena()
+
+ def transpose(first, second):
+ return im.transpose(first).transpose(second)
+
+ assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT))
+ assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM))
+
+ assert_image_equal(im, transpose(ROTATE_90, ROTATE_270))
+ assert_image_equal(im, transpose(ROTATE_180, ROTATE_180))
+
diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py
new file mode 100644
index 000000000..7d4b6d9b3
--- /dev/null
+++ b/Tests/test_image_verify.py
@@ -0,0 +1,5 @@
+from tester import *
+
+from PIL import Image
+
+success()
diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py
new file mode 100644
index 000000000..16eaaf55e
--- /dev/null
+++ b/Tests/test_imagechops.py
@@ -0,0 +1,56 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageChops
+
+def test_sanity():
+
+ im = lena("L")
+
+ ImageChops.constant(im, 128)
+ ImageChops.duplicate(im)
+ ImageChops.invert(im)
+ ImageChops.lighter(im, im)
+ ImageChops.darker(im, im)
+ ImageChops.difference(im, im)
+ ImageChops.multiply(im, im)
+ ImageChops.screen(im, im)
+
+ ImageChops.add(im, im)
+ ImageChops.add(im, im, 2.0)
+ ImageChops.add(im, im, 2.0, 128)
+ ImageChops.subtract(im, im)
+ ImageChops.subtract(im, im, 2.0)
+ ImageChops.subtract(im, im, 2.0, 128)
+
+ ImageChops.add_modulo(im, im)
+ ImageChops.subtract_modulo(im, im)
+
+ ImageChops.blend(im, im, 0.5)
+ ImageChops.composite(im, im, im)
+
+ ImageChops.offset(im, 10)
+ ImageChops.offset(im, 10, 20)
+
+def test_logical():
+
+ def table(op, a, b):
+ out = []
+ for x in (a, b):
+ imx = Image.new("1", (1, 1), x)
+ for y in (a, b):
+ imy = Image.new("1", (1, 1), y)
+ out.append(op(imx, imy).getpixel((0, 0)))
+ return tuple(out)
+
+ assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255))
+ assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255))
+ assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0))
+
+ assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255))
+ assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255))
+ assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0))
+
+ assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255))
+ assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255))
+ assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0))
diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py
new file mode 100644
index 000000000..d18132598
--- /dev/null
+++ b/Tests/test_imagecms.py
@@ -0,0 +1,159 @@
+from tester import *
+
+from PIL import Image
+try:
+ from PIL import ImageCms
+except ImportError:
+ skip()
+
+SRGB = "Tests/icc/sRGB.icm"
+
+def test_sanity():
+
+ # basic smoke test.
+ # this mostly follows the cms_test outline.
+
+ v = ImageCms.versions() # should return four strings
+ assert_equal(v[0], '1.0.0 pil')
+ assert_equal(list(map(type, v)), [str, str, str, str])
+
+ # internal version number
+ assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$")
+
+ i = ImageCms.profileToProfile(lena(), SRGB, SRGB)
+ assert_image(i, "RGB", (128, 128))
+
+ t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
+ i = ImageCms.applyTransform(lena(), t)
+ assert_image(i, "RGB", (128, 128))
+
+ p = ImageCms.createProfile("sRGB")
+ o = ImageCms.getOpenProfile(SRGB)
+ t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB")
+ i = ImageCms.applyTransform(lena(), t)
+ assert_image(i, "RGB", (128, 128))
+
+ t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB")
+ assert_equal(t.inputMode, "RGB")
+ assert_equal(t.outputMode, "RGB")
+ 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.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))
+
+
+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_imagecolor.py b/Tests/test_imagecolor.py
new file mode 100644
index 000000000..23f21744a
--- /dev/null
+++ b/Tests/test_imagecolor.py
@@ -0,0 +1,34 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageColor
+
+# --------------------------------------------------------------------
+# sanity
+
+assert_equal((255, 0, 0), ImageColor.getrgb("#f00"))
+assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000"))
+assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)"))
+assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)"))
+assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)"))
+assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)"))
+assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)"))
+assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)"))
+assert_equal((255, 0, 0), ImageColor.getrgb("red"))
+
+# --------------------------------------------------------------------
+# look for rounding errors (based on code by Tim Hatch)
+
+for color in list(ImageColor.colormap.keys()):
+ expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0))
+ actual = Image.new("L", (1, 1), color).getpixel((0, 0))
+ assert_equal(expected, actual)
+
+assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB"))
+assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB"))
+
+assert_equal(0, ImageColor.getcolor("black", "L"))
+assert_equal(255, ImageColor.getcolor("white", "L"))
+
+assert_equal(0, ImageColor.getcolor("black", "1"))
+assert_equal(255, ImageColor.getcolor("white", "1"))
diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py
new file mode 100644
index 000000000..f8b5c3c5c
--- /dev/null
+++ b/Tests/test_imagedraw.py
@@ -0,0 +1,28 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageDraw
+
+def test_sanity():
+
+ im = lena("RGB").copy()
+
+ draw = ImageDraw.ImageDraw(im)
+ draw = ImageDraw.Draw(im)
+
+ draw.ellipse(list(range(4)))
+ draw.line(list(range(10)))
+ draw.polygon(list(range(100)))
+ draw.rectangle(list(range(4)))
+
+ success()
+
+def test_deprecated():
+
+ im = lena().copy()
+
+ draw = ImageDraw.Draw(im)
+
+ assert_warning(DeprecationWarning, lambda: draw.setink(0))
+ assert_warning(DeprecationWarning, lambda: draw.setfill(0))
+
diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py
new file mode 100644
index 000000000..04f16bfa5
--- /dev/null
+++ b/Tests/test_imageenhance.py
@@ -0,0 +1,19 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageEnhance
+
+def test_sanity():
+
+ # FIXME: assert_image
+ assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5))
+ assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5))
+ assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5))
+ assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5))
+
+def test_crash():
+
+ # crashes on small images
+ im = Image.new("RGB", (1, 1))
+ assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5))
+
diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py
new file mode 100644
index 000000000..2447dfb22
--- /dev/null
+++ b/Tests/test_imagefile.py
@@ -0,0 +1,71 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageFile
+
+codecs = dir(Image.core)
+
+# save original block sizes
+MAXBLOCK = ImageFile.MAXBLOCK
+SAFEBLOCK = ImageFile.SAFEBLOCK
+
+def test_parser():
+
+ def roundtrip(format):
+
+ im = lena("L").resize((1000, 1000))
+ if format in ("MSP", "XBM"):
+ im = im.convert("1")
+
+ file = BytesIO()
+
+ im.save(file, format)
+
+ data = file.getvalue()
+
+ parser = ImageFile.Parser()
+ parser.feed(data)
+ imOut = parser.close()
+
+ return im, imOut
+
+ assert_image_equal(*roundtrip("BMP"))
+ assert_image_equal(*roundtrip("GIF"))
+ assert_image_equal(*roundtrip("IM"))
+ assert_image_equal(*roundtrip("MSP"))
+ if "zip_encoder" in codecs:
+ try:
+ # force multiple blocks in PNG driver
+ ImageFile.MAXBLOCK = 8192
+ assert_image_equal(*roundtrip("PNG"))
+ finally:
+ ImageFile.MAXBLOCK = MAXBLOCK
+ assert_image_equal(*roundtrip("PPM"))
+ assert_image_equal(*roundtrip("TIFF"))
+ assert_image_equal(*roundtrip("XBM"))
+ #assert_image_equal(*roundtrip("EPS")) #no eps_decoder
+ assert_image_equal(*roundtrip("TGA"))
+ assert_image_equal(*roundtrip("PCX"))
+
+ if "jpeg_encoder" in codecs:
+ im1, im2 = roundtrip("JPEG") # lossy compression
+ assert_image(im1, im2.mode, im2.size)
+
+ # XXX Why assert exception and why does it fail?
+ # https://github.com/python-imaging/Pillow/issues/78
+ #assert_exception(IOError, lambda: roundtrip("PDF"))
+
+def test_safeblock():
+
+ im1 = lena()
+
+ if "zip_encoder" not in codecs:
+ skip("PNG (zlib) encoder not available")
+
+ try:
+ ImageFile.SAFEBLOCK = 1
+ im2 = fromstring(tostring(im1, "PNG"))
+ finally:
+ ImageFile.SAFEBLOCK = SAFEBLOCK
+
+ assert_image_equal(im1, im2)
diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py
new file mode 100644
index 000000000..c63f07bb0
--- /dev/null
+++ b/Tests/test_imagefileio.py
@@ -0,0 +1,24 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageFileIO
+
+def test_fileio():
+
+ class DumbFile:
+ def __init__(self, data):
+ self.data = data
+ def read(self, bytes=None):
+ assert_equal(bytes, None)
+ return self.data
+ def close(self):
+ pass
+
+ im1 = lena()
+
+ io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM")))
+
+ im2 = Image.open(io)
+ assert_image_equal(im1, im2)
+
+
diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py
new file mode 100644
index 000000000..214f88024
--- /dev/null
+++ b/Tests/test_imagefilter.py
@@ -0,0 +1,31 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageFilter
+
+def test_sanity():
+ # see test_image_filter for more tests
+
+ assert_no_exception(lambda: ImageFilter.MaxFilter)
+ assert_no_exception(lambda: ImageFilter.MedianFilter)
+ assert_no_exception(lambda: ImageFilter.MinFilter)
+ assert_no_exception(lambda: ImageFilter.ModeFilter)
+ assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9))))
+ assert_no_exception(lambda: ImageFilter.GaussianBlur)
+ assert_no_exception(lambda: ImageFilter.GaussianBlur(5))
+ assert_no_exception(lambda: ImageFilter.UnsharpMask)
+ assert_no_exception(lambda: ImageFilter.UnsharpMask(10))
+
+ assert_no_exception(lambda: ImageFilter.BLUR)
+ assert_no_exception(lambda: ImageFilter.CONTOUR)
+ assert_no_exception(lambda: ImageFilter.DETAIL)
+ assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE)
+ assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE)
+ assert_no_exception(lambda: ImageFilter.EMBOSS)
+ assert_no_exception(lambda: ImageFilter.FIND_EDGES)
+ assert_no_exception(lambda: ImageFilter.SMOOTH)
+ assert_no_exception(lambda: ImageFilter.SMOOTH_MORE)
+ assert_no_exception(lambda: ImageFilter.SHARPEN)
+
+
+
diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py
new file mode 100644
index 000000000..9ac2cdd89
--- /dev/null
+++ b/Tests/test_imagefont.py
@@ -0,0 +1,135 @@
+from tester import *
+
+from PIL import Image
+from io import BytesIO
+import os
+
+try:
+ from PIL import ImageFont
+ ImageFont.core.getfont # check if freetype is available
+except ImportError:
+ skip()
+
+from PIL import ImageDraw
+
+font_path = "Tests/fonts/FreeMono.ttf"
+font_size=20
+
+def test_sanity():
+ assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$")
+
+def test_font_with_name():
+ assert_no_exception(lambda: ImageFont.truetype(font_path, font_size))
+ assert_no_exception(lambda: _render(font_path))
+ _clean()
+
+def _font_as_bytes():
+ with open(font_path, 'rb') as f:
+ font_bytes = BytesIO(f.read())
+ return font_bytes
+
+def test_font_with_filelike():
+ assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size))
+ assert_no_exception(lambda: _render(_font_as_bytes()))
+ # Usage note: making two fonts from the same buffer fails.
+ #shared_bytes = _font_as_bytes()
+ #assert_no_exception(lambda: _render(shared_bytes))
+ #assert_exception(Exception, lambda: _render(shared_bytes))
+ _clean()
+
+def test_font_with_open_file():
+ with open(font_path, 'rb') as f:
+ assert_no_exception(lambda: _render(f))
+ _clean()
+
+def test_font_old_parameters():
+ assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size))
+
+def _render(font):
+ txt = "Hello World!"
+ ttf = ImageFont.truetype(font, font_size)
+ w, h = ttf.getsize(txt)
+ img = Image.new("RGB", (256, 64), "white")
+ d = ImageDraw.Draw(img)
+ d.text((10, 10), txt, font=ttf, fill='black')
+
+ img.save('font.png')
+ return img
+
+def _clean():
+ os.unlink('font.png')
+
+def test_render_equal():
+ img_path = _render(font_path)
+ with open(font_path, 'rb') as f:
+ font_filelike = BytesIO(f.read())
+ img_filelike = _render(font_filelike)
+
+ assert_image_equal(img_path, img_filelike)
+ _clean()
+
+
+def test_render_multiline():
+ im = Image.new(mode='RGB', size=(300,100))
+ draw = ImageDraw.Draw(im)
+ ttf = ImageFont.truetype(font_path, font_size)
+ line_spacing = draw.textsize('A', font=ttf)[1] + 8
+ lines = ['hey you', 'you are awesome', 'this looks awkward']
+ y = 0
+ for line in lines:
+ draw.text((0, y), line, font=ttf)
+ y += line_spacing
+
+
+ target = 'Tests/images/multiline_text.png'
+ target_img = Image.open(target)
+
+ # some versions of freetype have different horizontal spacing.
+ # 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_imagegrab.py b/Tests/test_imagegrab.py
new file mode 100644
index 000000000..67ff71960
--- /dev/null
+++ b/Tests/test_imagegrab.py
@@ -0,0 +1,13 @@
+from tester import *
+
+from PIL import Image
+try:
+ from PIL import ImageGrab
+except ImportError as v:
+ skip(v)
+
+def test_grab():
+ im = ImageGrab.grab()
+ assert_image(im, im.mode, im.size)
+
+
diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py
new file mode 100644
index 000000000..eaeb711ba
--- /dev/null
+++ b/Tests/test_imagemath.py
@@ -0,0 +1,62 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageMath
+
+def pixel(im):
+ if hasattr(im, "im"):
+ return "%s %r" % (im.mode, im.getpixel((0, 0)))
+ else:
+ if isinstance(im, type(0)):
+ return int(im) # hack to deal with booleans
+ print(im)
+
+A = Image.new("L", (1, 1), 1)
+B = Image.new("L", (1, 1), 2)
+F = Image.new("F", (1, 1), 3)
+I = Image.new("I", (1, 1), 4)
+
+images = {"A": A, "B": B, "F": F, "I": I}
+
+def test_sanity():
+ assert_equal(ImageMath.eval("1"), 1)
+ assert_equal(ImageMath.eval("1+A", A=2), 3)
+ assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3")
+ assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3")
+ assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0")
+ assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3")
+
+def test_ops():
+
+ assert_equal(pixel(ImageMath.eval("-A", images)), "I -1")
+ assert_equal(pixel(ImageMath.eval("+B", images)), "L 2")
+
+ assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3")
+ assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1")
+ assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2")
+ assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0")
+ assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4")
+ assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647")
+
+ assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0")
+ assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0")
+ assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0")
+ assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5")
+ assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0")
+ assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0")
+
+def test_logical():
+ assert_equal(pixel(ImageMath.eval("not A", images)), 0)
+ assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2")
+ assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1")
+
+def test_convert():
+ assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3")
+ assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0")
+ assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)")
+
+def test_compare():
+ assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1")
+ assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2")
+ assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1")
+ assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0")
diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py
new file mode 100644
index 000000000..54b04435f
--- /dev/null
+++ b/Tests/test_imagemode.py
@@ -0,0 +1,23 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageMode
+
+ImageMode.getmode("1")
+ImageMode.getmode("L")
+ImageMode.getmode("P")
+ImageMode.getmode("RGB")
+ImageMode.getmode("I")
+ImageMode.getmode("F")
+
+m = ImageMode.getmode("1")
+assert_equal(m.mode, "1")
+assert_equal(m.bands, ("1",))
+assert_equal(m.basemode, "L")
+assert_equal(m.basetype, "L")
+
+m = ImageMode.getmode("RGB")
+assert_equal(m.mode, "RGB")
+assert_equal(m.bands, ("R", "G", "B"))
+assert_equal(m.basemode, "RGB")
+assert_equal(m.basetype, "L")
diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py
new file mode 100644
index 000000000..8ed5ccefa
--- /dev/null
+++ b/Tests/test_imageops.py
@@ -0,0 +1,81 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageOps
+
+class Deformer:
+ def getmesh(self, im):
+ x, y = im.size
+ return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]
+
+deformer = Deformer()
+
+def test_sanity():
+
+ ImageOps.autocontrast(lena("L"))
+ ImageOps.autocontrast(lena("RGB"))
+
+ ImageOps.autocontrast(lena("L"), cutoff=10)
+ ImageOps.autocontrast(lena("L"), ignore=[0, 255])
+
+ ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255))
+ ImageOps.colorize(lena("L"), "black", "white")
+
+ ImageOps.crop(lena("L"), 1)
+ ImageOps.crop(lena("RGB"), 1)
+
+ ImageOps.deform(lena("L"), deformer)
+ ImageOps.deform(lena("RGB"), deformer)
+
+ ImageOps.equalize(lena("L"))
+ ImageOps.equalize(lena("RGB"))
+
+ ImageOps.expand(lena("L"), 1)
+ ImageOps.expand(lena("RGB"), 1)
+ ImageOps.expand(lena("L"), 2, "blue")
+ ImageOps.expand(lena("RGB"), 2, "blue")
+
+ ImageOps.fit(lena("L"), (128, 128))
+ ImageOps.fit(lena("RGB"), (128, 128))
+
+ ImageOps.flip(lena("L"))
+ ImageOps.flip(lena("RGB"))
+
+ ImageOps.grayscale(lena("L"))
+ ImageOps.grayscale(lena("RGB"))
+
+ ImageOps.invert(lena("L"))
+ ImageOps.invert(lena("RGB"))
+
+ ImageOps.mirror(lena("L"))
+ ImageOps.mirror(lena("RGB"))
+
+ ImageOps.posterize(lena("L"), 4)
+ ImageOps.posterize(lena("RGB"), 4)
+
+ ImageOps.solarize(lena("L"))
+ ImageOps.solarize(lena("RGB"))
+
+ success()
+
+def test_1pxfit():
+ # Division by zero in equalize if image is 1 pixel high
+ newimg = ImageOps.fit(lena("RGB").resize((1,1)), (35,35))
+ assert_equal(newimg.size,(35,35))
+
+ newimg = ImageOps.fit(lena("RGB").resize((1,100)), (35,35))
+ assert_equal(newimg.size,(35,35))
+
+ newimg = ImageOps.fit(lena("RGB").resize((100,1)), (35,35))
+ assert_equal(newimg.size,(35,35))
+
+def test_pil163():
+ # Division by zero in equalize if < 255 pixels in image (@PIL163)
+
+ i = lena("RGB").resize((15, 16))
+
+ ImageOps.equalize(i.convert("L"))
+ ImageOps.equalize(i.convert("P"))
+ ImageOps.equalize(i.convert("RGB"))
+
+ success()
diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py
new file mode 100644
index 000000000..83a93b8e0
--- /dev/null
+++ b/Tests/test_imageops_usm.py
@@ -0,0 +1,55 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageOps
+from PIL import ImageFilter
+
+im = Image.open("Images/lena.ppm")
+
+def test_ops_api():
+
+ i = ImageOps.gaussian_blur(im, 2.0)
+ assert_equal(i.mode, "RGB")
+ assert_equal(i.size, (128, 128))
+ # i.save("blur.bmp")
+
+ i = ImageOps.usm(im, 2.0, 125, 8)
+ assert_equal(i.mode, "RGB")
+ assert_equal(i.size, (128, 128))
+ # i.save("usm.bmp")
+
+def test_filter_api():
+
+ filter = ImageFilter.GaussianBlur(2.0)
+ i = im.filter(filter)
+ assert_equal(i.mode, "RGB")
+ assert_equal(i.size, (128, 128))
+
+ filter = ImageFilter.UnsharpMask(2.0, 125, 8)
+ i = im.filter(filter)
+ assert_equal(i.mode, "RGB")
+ assert_equal(i.size, (128, 128))
+
+def test_usm():
+
+ usm = ImageOps.unsharp_mask
+ assert_exception(ValueError, lambda: usm(im.convert("1")))
+ assert_no_exception(lambda: usm(im.convert("L")))
+ assert_exception(ValueError, lambda: usm(im.convert("I")))
+ assert_exception(ValueError, lambda: usm(im.convert("F")))
+ assert_no_exception(lambda: usm(im.convert("RGB")))
+ assert_no_exception(lambda: usm(im.convert("RGBA")))
+ assert_no_exception(lambda: usm(im.convert("CMYK")))
+ assert_exception(ValueError, lambda: usm(im.convert("YCbCr")))
+
+def test_blur():
+
+ blur = ImageOps.gaussian_blur
+ assert_exception(ValueError, lambda: blur(im.convert("1")))
+ assert_no_exception(lambda: blur(im.convert("L")))
+ assert_exception(ValueError, lambda: blur(im.convert("I")))
+ assert_exception(ValueError, lambda: blur(im.convert("F")))
+ assert_no_exception(lambda: blur(im.convert("RGB")))
+ assert_no_exception(lambda: blur(im.convert("RGBA")))
+ assert_no_exception(lambda: blur(im.convert("CMYK")))
+ assert_exception(ValueError, lambda: blur(im.convert("YCbCr")))
diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py
new file mode 100644
index 000000000..a22addda9
--- /dev/null
+++ b/Tests/test_imagepalette.py
@@ -0,0 +1,44 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImagePalette
+
+ImagePalette = ImagePalette.ImagePalette
+
+def test_sanity():
+
+ assert_no_exception(lambda: ImagePalette("RGB", list(range(256))*3))
+ assert_exception(ValueError, lambda: ImagePalette("RGB", list(range(256))*2))
+
+def test_getcolor():
+
+ palette = ImagePalette()
+
+ map = {}
+ for i in range(256):
+ map[palette.getcolor((i, i, i))] = i
+
+ assert_equal(len(map), 256)
+ assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3)))
+
+def test_file():
+
+ palette = ImagePalette()
+
+ file = tempfile("temp.lut")
+
+ palette.save(file)
+
+ from PIL.ImagePalette import load, raw
+
+ p = load(file)
+
+ # load returns raw palette information
+ assert_equal(len(p[0]), 768)
+ assert_equal(p[1], "RGB")
+
+ p = raw(p[1], p[0])
+ assert_true(isinstance(p, ImagePalette))
+
+
+
diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py
new file mode 100644
index 000000000..9e9e63c3a
--- /dev/null
+++ b/Tests/test_imagepath.py
@@ -0,0 +1,54 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImagePath
+
+import array
+
+def test_path():
+
+ p = ImagePath.Path(list(range(10)))
+
+ # sequence interface
+ assert_equal(len(p), 5)
+ assert_equal(p[0], (0.0, 1.0))
+ assert_equal(p[-1], (8.0, 9.0))
+ assert_equal(list(p[:1]), [(0.0, 1.0)])
+ assert_equal(list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)])
+
+ # method sanity check
+ assert_equal(p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)])
+ assert_equal(p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0])
+
+ assert_equal(p.getbbox(), (0.0, 1.0, 8.0, 9.0))
+
+ assert_equal(p.compact(5), 2)
+ assert_equal(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)])
+
+ p.transform((1,0,1,0,1,1))
+ assert_equal(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)])
+
+ # alternative constructors
+ p = ImagePath.Path([0, 1])
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path([0.0, 1.0])
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path([0, 1])
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path([(0, 1)])
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path(p)
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path(p.tolist(0))
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path(p.tolist(1))
+ assert_equal(list(p), [(0.0, 1.0)])
+ p = ImagePath.Path(array.array("f", [0, 1]))
+ assert_equal(list(p), [(0.0, 1.0)])
+
+ arr = array.array("f", [0, 1])
+ if hasattr(arr, 'tobytes'):
+ p = ImagePath.Path(arr.tobytes())
+ else:
+ p = ImagePath.Path(arr.tostring())
+ assert_equal(list(p), [(0.0, 1.0)])
diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py
new file mode 100644
index 000000000..73d1f4b1c
--- /dev/null
+++ b/Tests/test_imageqt.py
@@ -0,0 +1,37 @@
+from tester import *
+
+from PIL import Image
+
+try:
+ from PyQt5.QtGui import QImage, qRgb, qRgba
+except:
+ try:
+ from PyQt4.QtGui import QImage, qRgb, qRgba
+ except:
+ skip('PyQT4 or 5 not installed')
+
+from PIL import ImageQt
+
+def test_rgb():
+ # from https://qt-project.org/doc/qt-4.8/qcolor.html
+ # typedef QRgb
+ # An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int.
+
+ assert_equal(qRgb(0,0,0), qRgba(0,0,0,255))
+
+ def checkrgb(r,g,b):
+ val = ImageQt.rgb(r,g,b)
+ val = val % 2**24 # drop the alpha
+ assert_equal(val >> 16, r)
+ assert_equal(((val >> 8 ) % 2**8), g)
+ assert_equal(val % 2**8, b)
+
+ checkrgb(0,0,0)
+ checkrgb(255,0,0)
+ checkrgb(0,255,0)
+ checkrgb(0,0,255)
+
+
+def test_image():
+ for mode in ('1', 'RGB', 'RGBA', 'L', 'P'):
+ assert_no_exception(lambda: ImageQt.ImageQt(lena(mode)))
diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py
new file mode 100644
index 000000000..3329b1a05
--- /dev/null
+++ b/Tests/test_imagesequence.py
@@ -0,0 +1,22 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageSequence
+
+def test_sanity():
+
+ file = tempfile("temp.im")
+
+ im = lena("RGB")
+ im.save(file)
+
+ seq = ImageSequence.Iterator(im)
+
+ index = 0
+ for frame in seq:
+ assert_image_equal(im, frame)
+ assert_equal(im.tell(), index)
+ index = index + 1
+
+ assert_equal(index, 1)
+
diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py
new file mode 100644
index 000000000..99ec005c8
--- /dev/null
+++ b/Tests/test_imageshow.py
@@ -0,0 +1,6 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageShow
+
+success()
diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py
new file mode 100644
index 000000000..02a461e22
--- /dev/null
+++ b/Tests/test_imagestat.py
@@ -0,0 +1,52 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageStat
+
+def test_sanity():
+
+ im = lena()
+
+ st = ImageStat.Stat(im)
+ st = ImageStat.Stat(im.histogram())
+ st = ImageStat.Stat(im, Image.new("1", im.size, 1))
+
+ assert_no_exception(lambda: st.extrema)
+ assert_no_exception(lambda: st.sum)
+ assert_no_exception(lambda: st.mean)
+ assert_no_exception(lambda: st.median)
+ assert_no_exception(lambda: st.rms)
+ assert_no_exception(lambda: st.sum2)
+ assert_no_exception(lambda: st.var)
+ assert_no_exception(lambda: st.stddev)
+ assert_exception(AttributeError, lambda: st.spam)
+
+ assert_exception(TypeError, lambda: ImageStat.Stat(1))
+
+def test_lena():
+
+ im = lena()
+
+ st = ImageStat.Stat(im)
+
+ # verify a few values
+ assert_equal(st.extrema[0], (61, 255))
+ assert_equal(st.median[0], 197)
+ assert_equal(st.sum[0], 2954416)
+ assert_equal(st.sum[1], 2027250)
+ assert_equal(st.sum[2], 1727331)
+
+def test_constant():
+
+ im = Image.new("L", (128, 128), 128)
+
+ st = ImageStat.Stat(im)
+
+ assert_equal(st.extrema[0], (128, 128))
+ assert_equal(st.sum[0], 128**3)
+ assert_equal(st.sum2[0], 128**4)
+ assert_equal(st.mean[0], 128)
+ assert_equal(st.median[0], 128)
+ assert_equal(st.rms[0], 128)
+ assert_equal(st.var[0], 0)
+ assert_equal(st.stddev[0], 0)
diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py
new file mode 100644
index 000000000..5c39c9283
--- /dev/null
+++ b/Tests/test_imagetk.py
@@ -0,0 +1,9 @@
+from tester import *
+
+from PIL import Image
+try:
+ from PIL import ImageTk
+except ImportError as v:
+ skip(v)
+
+success()
diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py
new file mode 100644
index 000000000..884e6bb1c
--- /dev/null
+++ b/Tests/test_imagetransform.py
@@ -0,0 +1,18 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageTransform
+
+im = Image.new("L", (100, 100))
+
+seq = tuple(range(10))
+
+def test_sanity():
+ transform = ImageTransform.AffineTransform(seq[:6])
+ assert_no_exception(lambda: im.transform((100, 100), transform))
+ transform = ImageTransform.ExtentTransform(seq[:4])
+ assert_no_exception(lambda: im.transform((100, 100), transform))
+ transform = ImageTransform.QuadTransform(seq[:8])
+ assert_no_exception(lambda: im.transform((100, 100), transform))
+ transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])])
+ assert_no_exception(lambda: im.transform((100, 100), transform))
diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py
new file mode 100644
index 000000000..268a75b6f
--- /dev/null
+++ b/Tests/test_imagewin.py
@@ -0,0 +1,6 @@
+from tester import *
+
+from PIL import Image
+from PIL import ImageWin
+
+success()
diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py
new file mode 100644
index 000000000..93aa694d8
--- /dev/null
+++ b/Tests/test_lib_image.py
@@ -0,0 +1,30 @@
+from tester import *
+
+from PIL import Image
+
+def test_setmode():
+
+ im = Image.new("L", (1, 1), 255)
+ im.im.setmode("1")
+ assert_equal(im.im.getpixel((0, 0)), 255)
+ im.im.setmode("L")
+ assert_equal(im.im.getpixel((0, 0)), 255)
+
+ im = Image.new("1", (1, 1), 1)
+ im.im.setmode("L")
+ assert_equal(im.im.getpixel((0, 0)), 255)
+ im.im.setmode("1")
+ assert_equal(im.im.getpixel((0, 0)), 255)
+
+ im = Image.new("RGB", (1, 1), (1, 2, 3))
+ im.im.setmode("RGB")
+ assert_equal(im.im.getpixel((0, 0)), (1, 2, 3))
+ im.im.setmode("RGBA")
+ assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255))
+ im.im.setmode("RGBX")
+ assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255))
+ im.im.setmode("RGB")
+ assert_equal(im.im.getpixel((0, 0)), (1, 2, 3))
+
+ assert_exception(ValueError, lambda: im.im.setmode("L"))
+ assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE"))
diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py
new file mode 100644
index 000000000..7675348b3
--- /dev/null
+++ b/Tests/test_lib_pack.py
@@ -0,0 +1,138 @@
+from tester import *
+
+from PIL import Image
+
+def pack():
+ pass # not yet
+
+def test_pack():
+
+ def pack(mode, rawmode):
+ if len(mode) == 1:
+ im = Image.new(mode, (1, 1), 1)
+ else:
+ im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)])
+
+ if py3:
+ return list(im.tobytes("raw", rawmode))
+ else:
+ return [ord(c) for c in im.tobytes("raw", rawmode)]
+
+ order = 1 if Image._ENDIAN == '<' else -1
+
+ assert_equal(pack("1", "1"), [128])
+ assert_equal(pack("1", "1;I"), [0])
+ assert_equal(pack("1", "1;R"), [1])
+ assert_equal(pack("1", "1;IR"), [0])
+
+ assert_equal(pack("L", "L"), [1])
+
+ assert_equal(pack("I", "I"), [1, 0, 0, 0][::order])
+
+ assert_equal(pack("F", "F"), [0, 0, 128, 63][::order])
+
+ assert_equal(pack("LA", "LA"), [1, 2])
+
+ assert_equal(pack("RGB", "RGB"), [1, 2, 3])
+ assert_equal(pack("RGB", "RGB;L"), [1, 2, 3])
+ assert_equal(pack("RGB", "BGR"), [3, 2, 1])
+ assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255?
+ assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0])
+ assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3])
+ assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1])
+
+ assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255?
+
+ assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4])
+
+ assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4])
+ assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3])
+
+def test_unpack():
+
+ def unpack(mode, rawmode, bytes_):
+ im = None
+
+ if py3:
+ data = bytes(range(1,bytes_+1))
+ else:
+ data = ''.join(chr(i) for i in range(1,bytes_+1))
+
+ im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1)
+
+ return im.getpixel((0, 0))
+
+ def unpack_1(mode, rawmode, value):
+ assert mode == "1"
+ im = None
+
+ if py3:
+ im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1)
+ else:
+ im = Image.frombytes(mode, (8, 1), chr(value), "raw", rawmode, 0, 1)
+
+ return tuple(im.getdata())
+
+ X = 255
+
+ assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X))
+ assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0))
+ assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0))
+ assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X))
+
+ assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0))
+ assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X))
+ assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X))
+ assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0))
+
+ assert_equal(unpack("L", "L;2", 1), 0)
+ assert_equal(unpack("L", "L;4", 1), 0)
+ assert_equal(unpack("L", "L", 1), 1)
+ assert_equal(unpack("L", "L;I", 1), 254)
+ assert_equal(unpack("L", "L;R", 1), 128)
+ assert_equal(unpack("L", "L;16", 2), 2) # little endian
+ assert_equal(unpack("L", "L;16B", 2), 1) # big endian
+
+ assert_equal(unpack("LA", "LA", 2), (1, 2))
+ assert_equal(unpack("LA", "LA;L", 2), (1, 2))
+
+ assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3))
+ assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3))
+ 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))
+ assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4))
+ assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2))
+
+ assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4))
+ 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))
+
+ assert_exception(ValueError, lambda: unpack("L", "L", 0))
+ assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2))
+ assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2))
+
+run()
diff --git a/Tests/test_locale.py b/Tests/test_locale.py
new file mode 100644
index 000000000..2683c561b
--- /dev/null
+++ b/Tests/test_locale.py
@@ -0,0 +1,31 @@
+from tester import *
+from PIL import Image
+
+import locale
+
+# ref https://github.com/python-imaging/Pillow/issues/272
+## on windows, in polish locale:
+
+## import locale
+## print locale.setlocale(locale.LC_ALL, 'polish')
+## import string
+## print len(string.whitespace)
+## print ord(string.whitespace[6])
+
+## Polish_Poland.1250
+## 7
+## 160
+
+# one of string.whitespace is not freely convertable into ascii.
+
+path = "Images/lena.jpg"
+
+def test_sanity():
+ assert_no_exception(lambda: Image.open(path))
+ try:
+ locale.setlocale(locale.LC_ALL, "polish")
+ except:
+ skip('polish locale not available')
+ import string
+ assert_no_exception(lambda: Image.open(path))
+
diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py
new file mode 100644
index 000000000..4c1798509
--- /dev/null
+++ b/Tests/test_mode_i16.py
@@ -0,0 +1,105 @@
+from tester import *
+
+from PIL import Image
+
+def verify(im1):
+ im2 = lena("I")
+ assert_equal(im1.size, im2.size)
+ pix1 = im1.load()
+ pix2 = im2.load()
+ for y in range(im1.size[1]):
+ for x in range(im1.size[0]):
+ xy = x, y
+ if pix1[xy] != pix2[xy]:
+ failure(
+ "got %r from mode %s at %s, expected %r" %
+ (pix1[xy], im1.mode, xy, pix2[xy])
+ )
+ return
+ success()
+
+def test_basic():
+ # PIL 1.1 has limited support for 16-bit image data. Check that
+ # create/copy/transform and save works as expected.
+
+ def basic(mode):
+
+ imIn = lena("I").convert(mode)
+ verify(imIn)
+
+ w, h = imIn.size
+
+ imOut = imIn.copy()
+ verify(imOut) # copy
+
+ imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h))
+ verify(imOut) # transform
+
+ filename = tempfile("temp.im")
+ imIn.save(filename)
+
+ imOut = Image.open(filename)
+
+ verify(imIn)
+ verify(imOut)
+
+ imOut = imIn.crop((0, 0, w, h))
+ verify(imOut)
+
+ imOut = Image.new(mode, (w, h), None)
+ imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0))
+ imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0))
+
+ verify(imIn)
+ verify(imOut)
+
+ imIn = Image.new(mode, (1, 1), 1)
+ assert_equal(imIn.getpixel((0, 0)), 1)
+
+ imIn.putpixel((0, 0), 2)
+ assert_equal(imIn.getpixel((0, 0)), 2)
+
+ if mode == "L":
+ max = 255
+ else:
+ max = 32767
+
+ imIn = Image.new(mode, (1, 1), 256)
+ assert_equal(imIn.getpixel((0, 0)), min(256, max))
+
+ imIn.putpixel((0, 0), 512)
+ assert_equal(imIn.getpixel((0, 0)), min(512, max))
+
+ basic("L")
+
+ basic("I;16")
+ basic("I;16B")
+ basic("I;16L")
+
+ basic("I")
+
+
+def test_tobytes():
+
+ def tobytes(mode):
+ return Image.new(mode, (1, 1), 1).tobytes()
+
+ order = 1 if Image._ENDIAN == '<' else -1
+
+ assert_equal(tobytes("L"), b"\x01")
+ assert_equal(tobytes("I;16"), b"\x01\x00")
+ assert_equal(tobytes("I;16B"), b"\x00\x01")
+ assert_equal(tobytes("I"), b"\x01\x00\x00\x00"[::order])
+
+
+def test_convert():
+
+ im = lena("I")
+
+ verify(im.convert("I;16"))
+ verify(im.convert("I;16").convert("L"))
+ verify(im.convert("I;16").convert("I"))
+
+ verify(im.convert("I;16B"))
+ verify(im.convert("I;16B").convert("L"))
+ verify(im.convert("I;16B").convert("I"))
diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py
new file mode 100644
index 000000000..4833cabb2
--- /dev/null
+++ b/Tests/test_numpy.py
@@ -0,0 +1,120 @@
+from tester import *
+
+from PIL import Image
+import struct
+
+try:
+ import site
+ import numpy
+except ImportError:
+ skip()
+
+def test_numpy_to_image():
+
+ def to_image(dtype, bands=1, bool=0):
+ if bands == 1:
+ if bool:
+ data = [0, 1] * 50
+ else:
+ data = list(range(100))
+ a = numpy.array(data, dtype=dtype)
+ a.shape = 10, 10
+ i = Image.fromarray(a)
+ if list(i.getdata()) != data:
+ print("data mismatch for", dtype)
+ else:
+ data = list(range(100))
+ a = numpy.array([[x]*bands for x in data], dtype=dtype)
+ a.shape = 10, 10, bands
+ i = Image.fromarray(a)
+ if list(i.split()[0].getdata()) != list(range(100)):
+ print("data mismatch for", dtype)
+ # print dtype, list(i.getdata())
+ return i
+
+ # assert_image(to_image(numpy.bool, bool=1), "1", (10, 10))
+ # assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10))
+
+ assert_exception(TypeError, lambda: to_image(numpy.uint))
+ assert_image(to_image(numpy.uint8), "L", (10, 10))
+ assert_exception(TypeError, lambda: to_image(numpy.uint16))
+ assert_exception(TypeError, lambda: to_image(numpy.uint32))
+ assert_exception(TypeError, lambda: to_image(numpy.uint64))
+
+ assert_image(to_image(numpy.int8), "I", (10, 10))
+ if Image._ENDIAN == '<': # Little endian
+ assert_image(to_image(numpy.int16), "I;16", (10, 10))
+ else:
+ assert_image(to_image(numpy.int16), "I;16B", (10, 10))
+ assert_image(to_image(numpy.int32), "I", (10, 10))
+ assert_exception(TypeError, lambda: to_image(numpy.int64))
+
+ assert_image(to_image(numpy.float), "F", (10, 10))
+ assert_image(to_image(numpy.float32), "F", (10, 10))
+ assert_image(to_image(numpy.float64), "F", (10, 10))
+
+ assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10))
+ assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10))
+
+
+# based on an erring example at http://is.gd/6F0esS (which resolves to)
+# http://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function
+def test_3d_array():
+ a = numpy.ones((10, 10, 10), dtype=numpy.uint8)
+ assert_image(Image.fromarray(a[1, :, :]), "L", (10, 10))
+ assert_image(Image.fromarray(a[:, 1, :]), "L", (10, 10))
+ assert_image(Image.fromarray(a[:, :, 1]), "L", (10, 10))
+
+
+def _test_img_equals_nparray(img, np):
+ assert_equal(img.size, np.shape[0:2])
+ px = img.load()
+ for x in range(0, img.size[0], int(img.size[0]/10)):
+ for y in range(0, img.size[1], int(img.size[1]/10)):
+ assert_deep_equal(px[x,y], np[y,x])
+
+
+def test_16bit():
+ img = Image.open('Tests/images/16bit.cropped.tif')
+ np_img = numpy.array(img)
+ _test_img_equals_nparray(img, np_img)
+ assert_equal(np_img.dtype, numpy.dtype('u2'),
+ ("I;16L", '= (3,0))
+
+# some test helpers
+
+_target = None
+_tempfiles = []
+_logfile = None
+
+def success():
+ import sys
+ success.count += 1
+ if _logfile:
+ print(sys.argv[0], success.count, failure.count, file=_logfile)
+
+def failure(msg=None, frame=None):
+ import sys, linecache
+ failure.count += 1
+ if _target:
+ if frame is None:
+ frame = sys._getframe()
+ while frame.f_globals.get("__name__") != _target.__name__:
+ frame = frame.f_back
+ location = (frame.f_code.co_filename, frame.f_lineno)
+ prefix = "%s:%d: " % location
+ line = linecache.getline(*location)
+ print(prefix + line.strip() + " failed:")
+ if msg:
+ print("- " + msg)
+ if _logfile:
+ print(sys.argv[0], success.count, failure.count, file=_logfile)
+
+success.count = failure.count = 0
+
+# predicates
+
+def assert_true(v, msg=None):
+ if v:
+ success()
+ else:
+ failure(msg or "got %r, expected true value" % v)
+
+def assert_false(v, msg=None):
+ if v:
+ failure(msg or "got %r, expected false value" % v)
+ else:
+ success()
+
+def assert_equal(a, b, msg=None):
+ if a == b:
+ success()
+ else:
+ failure(msg or "got %r, expected %r" % (a, b))
+
+def assert_almost_equal(a, b, msg=None, eps=1e-6):
+ if abs(a-b) < eps:
+ success()
+ else:
+ failure(msg or "got %r, expected %r" % (a, b))
+
+def assert_deep_equal(a, b, msg=None):
+ try:
+ if len(a) == len(b):
+ if all([x==y for x,y in zip(a,b)]):
+ success()
+ else:
+ failure(msg or "got %s, expected %s" % (a,b))
+ else:
+ failure(msg or "got length %s, expected %s" % (len(a), len(b)))
+ except:
+ assert_equal(a,b,msg)
+
+
+def assert_match(v, pattern, msg=None):
+ import re
+ if re.match(pattern, v):
+ success()
+ else:
+ failure(msg or "got %r, doesn't match pattern %r" % (v, pattern))
+
+def assert_exception(exc_class, func):
+ import sys, traceback
+ try:
+ func()
+ except exc_class:
+ success()
+ except:
+ failure("expected %r exception, got %r" % (
+ exc_class.__name__, sys.exc_info()[0].__name__))
+ traceback.print_exc()
+ else:
+ failure("expected %r exception, got no exception" % exc_class.__name__)
+
+def assert_no_exception(func):
+ import sys, traceback
+ try:
+ func()
+ except:
+ failure("expected no exception, got %r" % sys.exc_info()[0].__name__)
+ traceback.print_exc()
+ else:
+ success()
+
+def assert_warning(warn_class, func):
+ # note: this assert calls func three times!
+ import warnings
+ def warn_error(message, category, **options):
+ raise category(message)
+ def warn_ignore(message, category, **options):
+ pass
+ warn = warnings.warn
+ result = None
+ try:
+ warnings.warn = warn_ignore
+ assert_no_exception(func)
+ result = func()
+ warnings.warn = warn_error
+ assert_exception(warn_class, func)
+ finally:
+ warnings.warn = warn # restore
+ return result
+
+# helpers
+
+from io import BytesIO
+
+def fromstring(data):
+ from PIL import Image
+ return Image.open(BytesIO(data))
+
+def tostring(im, format, **options):
+ out = BytesIO()
+ im.save(out, format, **options)
+ return out.getvalue()
+
+def lena(mode="RGB", cache={}):
+ from PIL import Image
+ im = cache.get(mode)
+ if im is None:
+ if mode == "RGB":
+ im = Image.open("Images/lena.ppm")
+ elif mode == "F":
+ im = lena("L").convert(mode)
+ elif mode[:4] == "I;16":
+ im = lena("I").convert(mode)
+ else:
+ im = lena("RGB").convert(mode)
+ cache[mode] = im
+ return im
+
+def assert_image(im, mode, size, msg=None):
+ if mode is not None and im.mode != mode:
+ failure(msg or "got mode %r, expected %r" % (im.mode, mode))
+ elif size is not None and im.size != size:
+ failure(msg or "got size %r, expected %r" % (im.size, size))
+ else:
+ success()
+
+def assert_image_equal(a, b, msg=None):
+ if a.mode != b.mode:
+ failure(msg or "got mode %r, expected %r" % (a.mode, b.mode))
+ elif a.size != b.size:
+ failure(msg or "got size %r, expected %r" % (a.size, b.size))
+ elif a.tobytes() != b.tobytes():
+ failure(msg or "got different content")
+ # generate better diff?
+ else:
+ success()
+
+def assert_image_similar(a, b, epsilon, msg=None):
+ epsilon = float(epsilon)
+ if a.mode != b.mode:
+ return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode))
+ elif a.size != b.size:
+ return failure(msg or "got size %r, expected %r" % (a.size, b.size))
+ diff = 0
+ try:
+ ord(b'0')
+ for abyte,bbyte in zip(a.tobytes(),b.tobytes()):
+ diff += abs(ord(abyte)-ord(bbyte))
+ except:
+ for abyte,bbyte in zip(a.tobytes(),b.tobytes()):
+ diff += abs(abyte-bbyte)
+ ave_diff = float(diff)/(a.size[0]*a.size[1])
+ if epsilon < ave_diff:
+ failure(msg or "average pixel value difference %.4f > epsilon %.4f" %(ave_diff, epsilon))
+ else:
+ success()
+
+def tempfile(template, *extra):
+ import os, os.path, sys, tempfile
+ files = []
+ root = os.path.join(tempfile.gettempdir(), 'pillow-tests')
+ try:
+ os.mkdir(root)
+ except OSError:
+ pass
+ for temp in (template,) + extra:
+ assert temp[:5] in ("temp.", "temp_")
+ name = os.path.basename(sys.argv[0])
+ name = temp[:4] + os.path.splitext(name)[0][4:]
+ name = name + "_%d" % len(_tempfiles) + temp[4:]
+ name = os.path.join(root, name)
+ files.append(name)
+ _tempfiles.extend(files)
+ return files[0]
+
+# test runner
+
+def run():
+ global _target, _tests, run
+ import sys, traceback
+ _target = sys.modules["__main__"]
+ run = None # no need to run twice
+ tests = []
+ for name, value in list(vars(_target).items()):
+ if name[:5] == "test_" and type(value) is type(success):
+ tests.append((value.__code__.co_firstlineno, name, value))
+ tests.sort() # sort by line
+ for lineno, name, func in tests:
+ try:
+ _tests = []
+ func()
+ for func, args in _tests:
+ func(*args)
+ except:
+ t, v, tb = sys.exc_info()
+ tb = tb.tb_next
+ if tb:
+ failure(frame=tb.tb_frame)
+ traceback.print_exception(t, v, tb)
+ else:
+ print("%s:%d: cannot call test function: %s" % (
+ sys.argv[0], lineno, v))
+ failure.count += 1
+
+def yield_test(function, *args):
+ # collect delayed/generated tests
+ _tests.append((function, args))
+
+def skip(msg=None):
+ import os
+ print("skip")
+ os._exit(0) # don't run exit handlers
+
+def _setup():
+ global _logfile
+ def report():
+ if run:
+ run()
+ if success.count and not failure.count:
+ print("ok")
+ # only clean out tempfiles if test passed
+ import os, os.path, tempfile
+ for file in _tempfiles:
+ try:
+ os.remove(file)
+ except OSError:
+ pass # report?
+ temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests')
+ try:
+ os.rmdir(temp_root)
+ except OSError:
+ pass
+
+ if "--coverage" in sys.argv:
+ import coverage
+ coverage.stop()
+ # The coverage module messes up when used from inside an
+ # atexit handler. Do an explicit save to make sure that
+ # we actually flush the coverage cache.
+ coverage.the_coverage.save()
+ import atexit, sys
+ atexit.register(report)
+ if "--coverage" in sys.argv:
+ import coverage
+ coverage.start()
+ if "--log" in sys.argv:
+ _logfile = open("test.log", "a")
+
+
+_setup()
diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py
new file mode 100644
index 000000000..8162e713c
--- /dev/null
+++ b/Tests/threaded_save.py
@@ -0,0 +1,56 @@
+from PIL import Image
+
+import sys, time
+import io
+import threading, queue
+
+try:
+ format = sys.argv[1]
+except:
+ format = "PNG"
+
+im = Image.open("Images/lena.ppm")
+im.load()
+
+queue = queue.Queue()
+
+result = []
+
+class Worker(threading.Thread):
+ def run(self):
+ while 1:
+ im = queue.get()
+ if im is None:
+ queue.task_done()
+ sys.stdout.write("x")
+ break
+ f = io.BytesIO()
+ im.save(f, format, optimize=1)
+ data = f.getvalue()
+ result.append(len(data))
+ im = Image.open(io.BytesIO(data))
+ im.load()
+ sys.stdout.write(".")
+ queue.task_done()
+
+t0 = time.time()
+
+threads = 20
+jobs = 100
+
+for i in range(threads):
+ w = Worker()
+ w.start()
+
+for i in range(jobs):
+ queue.put(im)
+
+for i in range(threads):
+ queue.put(None)
+
+queue.join()
+
+print()
+print(time.time() - t0)
+print(len(result), sum(result))
+print(result)
diff --git a/Tests/versions.py b/Tests/versions.py
new file mode 100644
index 000000000..a4e4a0bc2
--- /dev/null
+++ b/Tests/versions.py
@@ -0,0 +1,23 @@
+from PIL import Image
+
+def version(module, version):
+ v = getattr(module.core, version + "_version", None)
+ if v:
+ print(version, v)
+
+version(Image, "jpeglib")
+version(Image, "zlib")
+
+try:
+ from PIL import ImageFont
+except ImportError:
+ pass
+else:
+ version(ImageFont, "freetype2")
+
+try:
+ from PIL import ImageCms
+except ImportError:
+ pass
+else:
+ version(ImageCms, "littlecms")
diff --git a/Tk/install.txt b/Tk/install.txt
index e24d36301..0e2ade06d 100644
--- a/Tk/install.txt
+++ b/Tk/install.txt
@@ -1,5 +1,5 @@
====================================================================
-Using PIL With Tkinter
+Using PIL With Tkinter
====================================================================
Starting with 1.0 final (release candidate 2 and later, to be
diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c
index 5963ee24c..3ebe49d48 100644
--- a/Tk/tkImaging.c
+++ b/Tk/tkImaging.c
@@ -11,11 +11,11 @@
*
* To use this module, import the _imagingtk module (ImageTk does
* this for you).
- *
+ *
* If you're using Python in an embedded context, you can add the
* following lines to your Tcl_AppInit function (in tkappinit.c)
* instead. Put them after the calls to Tcl_Init and Tk_Init:
- *
+ *
* {
* extern void TkImaging_Init(Tcl_Interp* interp);
* TkImaging_Init(interp);
@@ -48,9 +48,8 @@
for the Tcl_CreateCommand command. */
#define USE_COMPAT_CONST
-#include "tk.h"
-
#include "Imaging.h"
+#include "tk.h"
#include
@@ -58,7 +57,7 @@
static Imaging
ImagingFind(const char* name)
{
- long id;
+ Py_ssize_t id;
/* FIXME: use CObject instead? */
id = atol(name);
diff --git a/_imaging.c b/_imaging.c
index c21897dec..c62f0257d 100644
--- a/_imaging.c
+++ b/_imaging.c
@@ -65,17 +65,23 @@
* 2005-10-02 fl Added access proxy
* 2006-06-18 fl Always draw last point in polyline
*
- * Copyright (c) 1997-2006 by Secret Labs AB
+ * Copyright (c) 1997-2006 by Secret Labs AB
* Copyright (c) 1995-2006 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
*/
+#define PILLOW_VERSION "2.3.0"
#include "Python.h"
+#ifdef HAVE_LIBZ
+#include "zlib.h"
+#endif
+
#include "Imaging.h"
+#include "py3.h"
/* Configuration stuff. Feel free to undef things you don't need. */
#define WITH_IMAGECHOPS /* ImageChops support */
@@ -95,7 +101,7 @@
/* PIL Plus extensions */
#undef WITH_CRACKCODE /* pil plus */
-#undef VERBOSE
+#undef VERBOSE
#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
@@ -103,21 +109,8 @@
#define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)])
#define S16(v) ((v) < 32768 ? (v) : ((v) - 65536))
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-#define Py_ssize_t int
-#define ssizeargfunc intargfunc
-#define ssizessizeargfunc intintargfunc
-#define ssizeobjargproc intobjargproc
-#define ssizessizeobjargproc intintobjargproc
-#endif
-
/* -------------------------------------------------------------------- */
-/* OBJECT ADMINISTRATION */
+/* OBJECT ADMINISTRATION */
/* -------------------------------------------------------------------- */
typedef struct {
@@ -126,7 +119,7 @@ typedef struct {
ImagingAccess access;
} ImagingObject;
-staticforward PyTypeObject Imaging_Type;
+static PyTypeObject Imaging_Type;
#ifdef WITH_IMAGEDRAW
@@ -148,7 +141,7 @@ typedef struct {
Glyph glyphs[256];
} ImagingFontObject;
-staticforward PyTypeObject ImagingFont_Type;
+static PyTypeObject ImagingFont_Type;
typedef struct {
PyObject_HEAD
@@ -157,7 +150,7 @@ typedef struct {
int blend;
} ImagingDrawObject;
-staticforward PyTypeObject ImagingDraw_Type;
+static PyTypeObject ImagingDraw_Type;
#endif
@@ -167,20 +160,20 @@ typedef struct {
int readonly;
} PixelAccessObject;
-staticforward PyTypeObject PixelAccess_Type;
+static PyTypeObject PixelAccess_Type;
-PyObject*
+PyObject*
PyImagingNew(Imaging imOut)
{
ImagingObject* imagep;
if (!imOut)
- return NULL;
+ return NULL;
imagep = PyObject_New(ImagingObject, &Imaging_Type);
if (imagep == NULL) {
- ImagingDelete(imOut);
- return NULL;
+ ImagingDelete(imOut);
+ return NULL;
}
#ifdef VERBOSE
@@ -202,18 +195,18 @@ _dealloc(ImagingObject* imagep)
#endif
if (imagep->access)
- ImagingAccessDelete(imagep->image, imagep->access);
+ ImagingAccessDelete(imagep->image, imagep->access);
ImagingDelete(imagep->image);
PyObject_Del(imagep);
}
-#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type)
+#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type)
Imaging PyImaging_AsImaging(PyObject *op)
{
if (!PyImaging_Check(op)) {
- PyErr_BadInternalCall();
- return NULL;
+ PyErr_BadInternalCall();
+ return NULL;
}
return ((ImagingObject *)op)->image;
@@ -243,43 +236,46 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie)
/* -------------------------------------------------------------------- */
/* Python compatibility API */
-#if PY_VERSION_HEX < 0x02020000
-
-int PyImaging_CheckBuffer(PyObject *buffer)
-{
- PyBufferProcs *procs = buffer->ob_type->tp_as_buffer;
- if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount &&
- procs->bf_getsegcount(buffer, NULL) == 1)
- return 1;
- return 0;
-}
-
-int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr)
-{
- PyBufferProcs *procs = buffer->ob_type->tp_as_buffer;
- return procs->bf_getreadbuffer(buffer, 0, ptr);
-}
-
-#else
-
int PyImaging_CheckBuffer(PyObject* buffer)
{
- return PyObject_CheckReadBuffer(buffer);
+#if PY_VERSION_HEX >= 0x03000000
+ return PyObject_CheckBuffer(buffer);
+#else
+ return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer);
+#endif
}
-int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr)
+int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view)
{
/* must call check_buffer first! */
-#if PY_VERSION_HEX < 0x02050000
- int n = 0;
+#if PY_VERSION_HEX >= 0x03000000
+ return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE);
#else
- Py_ssize_t n = 0;
-#endif
- PyObject_AsReadBuffer(buffer, ptr, &n);
- return (int) n;
-}
+ /* Use new buffer protocol if available
+ (mmap doesn't support this in 2.7, go figure) */
+ if (PyObject_CheckBuffer(buffer)) {
+ return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE);
+ }
+ /* Pretend we support the new protocol; PyBuffer_Release happily ignores
+ calling bf_releasebuffer on objects that don't support it */
+ view->buf = NULL;
+ view->len = 0;
+ view->readonly = 1;
+ view->format = NULL;
+ view->ndim = 0;
+ view->shape = NULL;
+ view->strides = NULL;
+ view->suboffsets = NULL;
+ view->itemsize = 0;
+ view->internal = NULL;
+
+ Py_INCREF(buffer);
+ view->obj = buffer;
+
+ return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len);
#endif
+}
/* -------------------------------------------------------------------- */
/* EXCEPTION REROUTING */
@@ -291,6 +287,7 @@ static const char* wrong_mode = "unrecognized image mode";
static const char* wrong_raw_mode = "unrecognized raw mode";
static const char* outside_image = "image index out of range";
static const char* outside_palette = "palette index out of range";
+static const char* wrong_palette_size = "invalid palette size";
static const char* no_palette = "image has no palette";
static const char* readonly = "image is readonly";
/* static const char* no_content = "image has no content"; */
@@ -339,7 +336,7 @@ ImagingError_Clear(void)
}
/* -------------------------------------------------------------------- */
-/* HELPERS */
+/* HELPERS */
/* -------------------------------------------------------------------- */
static int
@@ -372,14 +369,14 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type)
void* list;
if (!PySequence_Check(arg)) {
- PyErr_SetString(PyExc_TypeError, must_be_sequence);
- return NULL;
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
}
n = PyObject_Length(arg);
if (length && wrong_length && n != *length) {
- PyErr_SetString(PyExc_ValueError, wrong_length);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, wrong_length);
+ return NULL;
}
list = malloc(n * (type & 0xff));
@@ -472,33 +469,33 @@ getpixel(Imaging im, ImagingAccess access, int x, int y)
} pixel;
if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- PyErr_SetString(PyExc_IndexError, outside_image);
- return NULL;
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return NULL;
}
access->get_pixel(im, x, y, &pixel);
switch (im->type) {
case IMAGING_TYPE_UINT8:
- switch (im->bands) {
- case 1:
- return PyInt_FromLong(pixel.b[0]);
- case 2:
- return Py_BuildValue("ii", pixel.b[0], pixel.b[1]);
- case 3:
- return Py_BuildValue("iii", pixel.b[0], pixel.b[1], pixel.b[2]);
- case 4:
- return Py_BuildValue("iiii", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]);
- }
- break;
+ switch (im->bands) {
+ case 1:
+ return PyInt_FromLong(pixel.b[0]);
+ case 2:
+ return Py_BuildValue("BB", pixel.b[0], pixel.b[1]);
+ case 3:
+ return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]);
+ case 4:
+ return Py_BuildValue("BBBB", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]);
+ }
+ break;
case IMAGING_TYPE_INT32:
- return PyInt_FromLong(pixel.i);
+ return PyInt_FromLong(pixel.i);
case IMAGING_TYPE_FLOAT32:
- return PyFloat_FromDouble(pixel.f);
+ return PyFloat_FromDouble(pixel.f);
case IMAGING_TYPE_SPECIAL:
- if (strncmp(im->mode, "I;16", 4) == 0)
- return PyInt_FromLong(pixel.h);
- break;
+ if (strncmp(im->mode, "I;16", 4) == 0)
+ return PyInt_FromLong(pixel.h);
+ break;
}
/* unknown type */
@@ -527,8 +524,17 @@ getink(PyObject* color, Imaging im, char* ink)
ink[1] = ink[2] = ink[3] = 0;
} else {
a = 255;
- if (PyInt_Check(color)) {
- r = PyInt_AS_LONG(color);
+#if PY_VERSION_HEX >= 0x03000000
+ if (PyLong_Check(color)) {
+ r = (int) PyLong_AsLong(color);
+#else
+ if (PyInt_Check(color) || PyLong_Check(color)) {
+ if (PyInt_Check(color))
+ r = PyInt_AS_LONG(color);
+ else
+ r = (int) PyLong_AsLong(color);
+#endif
+
/* compatibility: ABGR */
a = (UINT8) (r >> 24);
b = (UINT8) (r >> 16);
@@ -581,10 +587,10 @@ getink(PyObject* color, Imaging im, char* ink)
}
/* -------------------------------------------------------------------- */
-/* FACTORIES */
+/* FACTORIES */
/* -------------------------------------------------------------------- */
-static PyObject*
+static PyObject*
_fill(PyObject* self, PyObject* args)
{
char* mode;
@@ -592,12 +598,12 @@ _fill(PyObject* self, PyObject* args)
PyObject* color;
char buffer[4];
Imaging im;
-
+
xsize = ysize = 256;
color = NULL;
if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color))
- return NULL;
+ return NULL;
im = ImagingNew(mode, xsize, ysize);
if (!im)
@@ -616,107 +622,121 @@ _fill(PyObject* self, PyObject* args)
return PyImagingNew(im);
}
-static PyObject*
+static PyObject*
_new(PyObject* self, PyObject* args)
{
char* mode;
int xsize, ysize;
if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingNew(mode, xsize, ysize));
}
-static PyObject*
+static PyObject*
_new_array(PyObject* self, PyObject* args)
{
char* mode;
int xsize, ysize;
if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingNewArray(mode, xsize, ysize));
}
-static PyObject*
+static PyObject*
_new_block(PyObject* self, PyObject* args)
{
char* mode;
int xsize, ysize;
if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingNewBlock(mode, xsize, ysize));
}
-static PyObject*
+static PyObject*
_getcount(PyObject* self, PyObject* args)
{
if (!PyArg_ParseTuple(args, ":getcount"))
- return NULL;
+ return NULL;
return PyInt_FromLong(ImagingNewCount);
}
-static PyObject*
+static PyObject*
_linear_gradient(PyObject* self, PyObject* args)
{
char* mode;
if (!PyArg_ParseTuple(args, "s", &mode))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingFillLinearGradient(mode));
}
-static PyObject*
+static PyObject*
_radial_gradient(PyObject* self, PyObject* args)
{
char* mode;
if (!PyArg_ParseTuple(args, "s", &mode))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingFillRadialGradient(mode));
}
-static PyObject*
+static PyObject*
_open_ppm(PyObject* self, PyObject* args)
{
char* filename;
if (!PyArg_ParseTuple(args, "s", &filename))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingOpenPPM(filename));
}
-static PyObject*
+static PyObject*
+_alpha_composite(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep1;
+ ImagingObject* imagep2;
+
+ if (!PyArg_ParseTuple(args, "O!O!",
+ &Imaging_Type, &imagep1,
+ &Imaging_Type, &imagep2))
+ return NULL;
+
+ return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image));
+}
+
+static PyObject*
_blend(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
ImagingObject* imagep2;
double alpha;
-
+
alpha = 0.5;
if (!PyArg_ParseTuple(args, "O!O!|d",
- &Imaging_Type, &imagep1,
- &Imaging_Type, &imagep2,
- &alpha))
- return NULL;
+ &Imaging_Type, &imagep1,
+ &Imaging_Type, &imagep2,
+ &alpha))
+ return NULL;
return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image,
- (float) alpha));
+ (float) alpha));
}
/* -------------------------------------------------------------------- */
-/* METHODS */
+/* METHODS */
/* -------------------------------------------------------------------- */
-static PyObject*
+static PyObject*
_convert(ImagingObject* self, PyObject* args)
{
char* mode;
@@ -724,31 +744,31 @@ _convert(ImagingObject* self, PyObject* args)
ImagingObject *paletteimage = NULL;
if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage))
- return NULL;
+ return NULL;
if (paletteimage != NULL) {
- if (!PyImaging_Check(paletteimage)) {
- PyObject_Print((PyObject *)paletteimage, stderr, 0);
- PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'");
- return NULL;
- }
- if (paletteimage->image->palette == NULL) {
- PyErr_SetString(PyExc_ValueError, "null palette");
- return NULL;
- }
+ if (!PyImaging_Check(paletteimage)) {
+ PyObject_Print((PyObject *)paletteimage, stderr, 0);
+ PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'");
+ return NULL;
+ }
+ if (paletteimage->image->palette == NULL) {
+ PyErr_SetString(PyExc_ValueError, "null palette");
+ return NULL;
+ }
}
return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither));
}
-static PyObject*
+static PyObject*
_convert2(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
ImagingObject* imagep2;
if (!PyArg_ParseTuple(args, "O!O!",
- &Imaging_Type, &imagep1,
- &Imaging_Type, &imagep2))
- return NULL;
+ &Imaging_Type, &imagep1,
+ &Imaging_Type, &imagep2))
+ return NULL;
if (!ImagingConvert2(imagep1->image, imagep2->image))
return NULL;
@@ -757,41 +777,56 @@ _convert2(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_convert_matrix(ImagingObject* self, PyObject* args)
{
char* mode;
float m[12];
if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m+0, m+1, m+2, m+3)) {
- PyErr_Clear();
- if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode,
- m+0, m+1, m+2, m+3,
- m+4, m+5, m+6, m+7,
- m+8, m+9, m+10, m+11))
- return NULL;
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode,
+ m+0, m+1, m+2, m+3,
+ m+4, m+5, m+6, m+7,
+ m+8, m+9, m+10, m+11))
+ return NULL;
}
return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
}
-static PyObject*
+static PyObject*
+_convert_transparent(ImagingObject* self, PyObject* args)
+{
+ char* mode;
+ int r,g,b;
+ if (PyArg_ParseTuple(args, "s(iii)", &mode, &r, &g, &b)) {
+ return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b));
+ }
+ PyErr_Clear();
+ if (PyArg_ParseTuple(args, "si", &mode, &r)) {
+ return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0));
+ }
+ return NULL;
+}
+
+static PyObject*
_copy(ImagingObject* self, PyObject* args)
{
if (!PyArg_ParseTuple(args, ""))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingCopy(self->image));
}
-static PyObject*
+static PyObject*
_copy2(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep1;
ImagingObject* imagep2;
if (!PyArg_ParseTuple(args, "O!O!",
- &Imaging_Type, &imagep1,
- &Imaging_Type, &imagep2))
- return NULL;
+ &Imaging_Type, &imagep1,
+ &Imaging_Type, &imagep2))
+ return NULL;
if (!ImagingCopy2(imagep1->image, imagep2->image))
return NULL;
@@ -800,28 +835,28 @@ _copy2(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_crop(ImagingObject* self, PyObject* args)
{
int x0, y0, x1, y1;
if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1));
}
-static PyObject*
+static PyObject*
_expand(ImagingObject* self, PyObject* args)
{
int x, y;
int mode = 0;
if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingExpand(self->image, x, y, mode));
}
-static PyObject*
+static PyObject*
_filter(ImagingObject* self, PyObject* args)
{
PyObject* imOut;
@@ -832,9 +867,9 @@ _filter(ImagingObject* self, PyObject* args)
float divisor, offset;
PyObject* kernel = NULL;
if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
- &divisor, &offset, &kernel))
+ &divisor, &offset, &kernel))
return NULL;
-
+
/* get user-defined kernel */
kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32);
if (!kerneldata)
@@ -854,7 +889,7 @@ _filter(ImagingObject* self, PyObject* args)
}
#ifdef WITH_UNSHARPMASK
-static PyObject*
+static PyObject*
_gaussian_blur(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@@ -876,7 +911,7 @@ _gaussian_blur(ImagingObject* self, PyObject* args)
}
#endif
-static PyObject*
+static PyObject*
_getpalette(ImagingObject* self, PyObject* args)
{
PyObject* palette;
@@ -887,29 +922,40 @@ _getpalette(ImagingObject* self, PyObject* args)
char* mode = "RGB";
char* rawmode = "RGB";
if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode))
- return NULL;
+ return NULL;
if (!self->image->palette) {
- PyErr_SetString(PyExc_ValueError, no_palette);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
}
pack = ImagingFindPacker(mode, rawmode, &bits);
if (!pack) {
- PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
}
- palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8);
+ palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8);
if (!palette)
- return NULL;
+ return NULL;
- pack((UINT8*) PyString_AsString(palette),
- self->image->palette->palette, palettesize);
+ pack((UINT8*) PyBytes_AsString(palette),
+ self->image->palette->palette, palettesize);
return palette;
}
+static PyObject*
+_getpalettemode(ImagingObject* self, PyObject* args)
+{
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ return PyUnicode_FromString(self->image->palette->mode);
+}
+
static inline int
_getxy(PyObject* xy, int* x, int *y)
{
@@ -917,7 +963,7 @@ _getxy(PyObject* xy, int* x, int *y)
if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2)
goto badarg;
-
+
value = PyTuple_GET_ITEM(xy, 0);
if (PyInt_Check(value))
*x = PyInt_AS_LONG(value);
@@ -951,7 +997,7 @@ _getxy(PyObject* xy, int* x, int *y)
return -1;
}
-static PyObject*
+static PyObject*
_getpixel(ImagingObject* self, PyObject* args)
{
PyObject* xy;
@@ -996,7 +1042,7 @@ _histogram(ImagingObject* self, PyObject* args)
PyObject* extremap = NULL;
ImagingObject* maskp = NULL;
if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
- return NULL;
+ return NULL;
if (extremap) {
ep = &extrema;
@@ -1030,19 +1076,19 @@ _histogram(ImagingObject* self, PyObject* args)
h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
if (!h)
- return NULL;
+ return NULL;
/* Build an integer list containing the histogram */
list = PyList_New(h->bands * 256);
for (i = 0; i < h->bands * 256; i++) {
- PyObject* item;
- item = PyInt_FromLong(h->histogram[i]);
- if (item == NULL) {
- Py_DECREF(list);
- list = NULL;
- break;
- }
- PyList_SetItem(list, i, item);
+ PyObject* item;
+ item = PyInt_FromLong(h->histogram[i]);
+ if (item == NULL) {
+ Py_DECREF(list);
+ list = NULL;
+ break;
+ }
+ PyList_SetItem(list, i, item);
}
ImagingHistogramDelete(h);
@@ -1051,28 +1097,28 @@ _histogram(ImagingObject* self, PyObject* args)
}
#ifdef WITH_MODEFILTER
-static PyObject*
+static PyObject*
_modefilter(ImagingObject* self, PyObject* args)
{
int size;
if (!PyArg_ParseTuple(args, "i", &size))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingModeFilter(self->image, size));
}
#endif
-static PyObject*
+static PyObject*
_offset(ImagingObject* self, PyObject* args)
{
int xoffset, yoffset;
if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset));
}
-static PyObject*
+static PyObject*
_paste(ImagingObject* self, PyObject* args)
{
int status;
@@ -1082,10 +1128,10 @@ _paste(ImagingObject* self, PyObject* args)
int x0, y0, x1, y1;
ImagingObject* maskp = NULL;
if (!PyArg_ParseTuple(args, "O(iiii)|O!",
- &source,
- &x0, &y0, &x1, &y1,
- &Imaging_Type, &maskp))
- return NULL;
+ &source,
+ &x0, &y0, &x1, &y1,
+ &Imaging_Type, &maskp))
+ return NULL;
if (PyImaging_Check(source))
status = ImagingPaste(
@@ -1123,7 +1169,7 @@ _point(ImagingObject* self, PyObject* args)
PyObject* list;
char* mode;
if (!PyArg_ParseTuple(args, "Oz", &list, &mode))
- return NULL;
+ return NULL;
if (mode && !strcmp(mode, "F")) {
FLOAT32* data;
@@ -1194,7 +1240,7 @@ _point_transform(ImagingObject* self, PyObject* args)
double scale = 1.0;
double offset = 0.0;
if (!PyArg_ParseTuple(args, "|dd", &scale, &offset))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingPointTransform(self->image, scale, offset));
}
@@ -1203,31 +1249,33 @@ static PyObject*
_putdata(ImagingObject* self, PyObject* args)
{
Imaging image;
- int n, i, x, y;
+ // i & n are # pixels, require py_ssize_t. x can be as large as n. y, just because.
+ Py_ssize_t n, i, x, y;
PyObject* data;
double scale = 1.0;
double offset = 0.0;
+
if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset))
- return NULL;
+ return NULL;
if (!PySequence_Check(data)) {
- PyErr_SetString(PyExc_TypeError, must_be_sequence);
- return NULL;
+ PyErr_SetString(PyExc_TypeError, must_be_sequence);
+ return NULL;
}
image = self->image;
n = PyObject_Length(data);
- if (n > (int) (image->xsize * image->ysize)) {
- PyErr_SetString(PyExc_TypeError, "too many data entries");
- return NULL;
+ if (n > (Py_ssize_t) (image->xsize * image->ysize)) {
+ PyErr_SetString(PyExc_TypeError, "too many data entries");
+ return NULL;
}
if (image->image8) {
- if (PyString_Check(data)) {
+ if (PyBytes_Check(data)) {
unsigned char* p;
- p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data);
+ p = (unsigned char*) PyBytes_AS_STRING(data);
if (scale == 1.0 && offset == 0.0)
/* Plain string data */
for (i = y = 0; i < n; i += image->xsize, y++) {
@@ -1236,7 +1284,7 @@ _putdata(ImagingObject* self, PyObject* args)
x = image->xsize;
memcpy(image->image8[y], p+i, x);
}
- else
+ else
/* Scaled and clipped string data */
for (i = x = y = 0; i < n; i++) {
image->image8[y][x] = CLIP((int) (p[i] * scale + offset));
@@ -1312,14 +1360,18 @@ _putdata(ImagingObject* self, PyObject* args)
break;
default:
for (i = x = y = 0; i < n; i++) {
- char ink[4];
+ union {
+ char ink[4];
+ INT32 inkint;
+ } u;
+
PyObject *op = PySequence_GetItem(data, i);
- if (!op || !getink(op, image, ink)) {
+ if (!op || !getink(op, image, u.ink)) {
Py_DECREF(op);
return NULL;
}
/* FIXME: what about scale and offset? */
- image->image32[y][x] = *((INT32*) ink);
+ image->image32[y][x] = u.inkint;
Py_XDECREF(op);
if (++x >= (int) image->xsize)
x = 0, y++;
@@ -1335,15 +1387,14 @@ _putdata(ImagingObject* self, PyObject* args)
#ifdef WITH_QUANTIZE
-#include "Quant.h"
-static PyObject*
+static PyObject*
_quantize(ImagingObject* self, PyObject* args)
{
int colours = 256;
int method = 0;
int kmeans = 0;
if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans))
- return NULL;
+ return NULL;
if (!self->image->xsize || !self->image->ysize) {
/* no content; return an empty image */
@@ -1356,7 +1407,7 @@ _quantize(ImagingObject* self, PyObject* args)
}
#endif
-static PyObject*
+static PyObject*
_putpalette(ImagingObject* self, PyObject* args)
{
ImagingShuffler unpack;
@@ -1365,18 +1416,23 @@ _putpalette(ImagingObject* self, PyObject* args)
char* rawmode;
UINT8* palette;
int palettesize;
- if (!PyArg_ParseTuple(args, "ss#", &rawmode, &palette, &palettesize))
- return NULL;
+ if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize))
+ return NULL;
if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) {
- PyErr_SetString(PyExc_ValueError, wrong_mode);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, wrong_mode);
+ return NULL;
}
unpack = ImagingFindUnpacker("RGB", rawmode, &bits);
if (!unpack) {
- PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
+ if ( palettesize * 8 / bits > 256) {
+ PyErr_SetString(PyExc_ValueError, wrong_palette_size);
+ return NULL;
}
ImagingPaletteDelete(self->image->palette);
@@ -1391,22 +1447,22 @@ _putpalette(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_putpalettealpha(ImagingObject* self, PyObject* args)
{
int index;
int alpha = 0;
if (!PyArg_ParseTuple(args, "i|i", &index, &alpha))
- return NULL;
+ return NULL;
if (!self->image->palette) {
- PyErr_SetString(PyExc_ValueError, no_palette);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
}
if (index < 0 || index >= 256) {
- PyErr_SetString(PyExc_ValueError, outside_palette);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, outside_palette);
+ return NULL;
}
strcpy(self->image->palette->mode, "RGBA");
@@ -1416,7 +1472,35 @@ _putpalettealpha(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
+_putpalettealphas(ImagingObject* self, PyObject* args)
+{
+ int i;
+ UINT8 *values;
+ int length;
+ if (!PyArg_ParseTuple(args, "s#", &values, &length))
+ return NULL;
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ if (length > 256) {
+ PyErr_SetString(PyExc_ValueError, outside_palette);
+ return NULL;
+ }
+
+ strcpy(self->image->palette->mode, "RGBA");
+ for (i=0; iimage->palette->palette[i*4+3] = (UINT8) values[i];
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
_putpixel(ImagingObject* self, PyObject* args)
{
Imaging im;
@@ -1425,13 +1509,13 @@ _putpixel(ImagingObject* self, PyObject* args)
int x, y;
PyObject* color;
if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color))
- return NULL;
+ return NULL;
im = self->image;
-
+
if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- PyErr_SetString(PyExc_IndexError, outside_image);
- return NULL;
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return NULL;
}
if (!getink(color, im, ink))
@@ -1445,18 +1529,18 @@ _putpixel(ImagingObject* self, PyObject* args)
}
#ifdef WITH_RANKFILTER
-static PyObject*
+static PyObject*
_rankfilter(ImagingObject* self, PyObject* args)
{
int size, rank;
if (!PyArg_ParseTuple(args, "ii", &size, &rank))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingRankFilter(self->image, size, rank));
}
#endif
-static PyObject*
+static PyObject*
_resize(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@@ -1465,38 +1549,38 @@ _resize(ImagingObject* self, PyObject* args)
int xsize, ysize;
int filter = IMAGING_TRANSFORM_NEAREST;
if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
- return NULL;
+ return NULL;
imIn = self->image;
imOut = ImagingNew(imIn->mode, xsize, ysize);
if (imOut)
- (void) ImagingResize(imOut, imIn, filter);
-
+ (void) ImagingResize(imOut, imIn, filter);
+
return PyImagingNew(imOut);
}
-static PyObject*
+static PyObject*
_rotate(ImagingObject* self, PyObject* args)
{
Imaging imOut;
Imaging imIn;
-
+
double theta;
int filter = IMAGING_TRANSFORM_NEAREST;
if (!PyArg_ParseTuple(args, "d|i", &theta, &filter))
- return NULL;
+ return NULL;
imIn = self->image;
theta = fmod(theta, 360.0);
if (theta < 0.0)
- theta += 360;
+ theta += 360;
if (filter && imIn->type != IMAGING_TYPE_SPECIAL) {
/* Rotate with resampling filter */
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
- (void) ImagingRotate(imOut, imIn, theta, filter);
+ (void) ImagingRotate(imOut, imIn, theta, filter);
} else if (theta == 90.0 || theta == 270.0) {
/* Use fast version */
imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
@@ -1527,7 +1611,7 @@ _rotate(ImagingObject* self, PyObject* args)
#define IS_RGB(mode)\
(!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
-static PyObject*
+static PyObject*
im_setmode(ImagingObject* self, PyObject* args)
{
/* attempt to modify the mode of an image in place */
@@ -1537,7 +1621,7 @@ im_setmode(ImagingObject* self, PyObject* args)
char* mode;
int modelen;
if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen))
- return NULL;
+ return NULL;
im = self->image;
@@ -1565,7 +1649,7 @@ im_setmode(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_stretch(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@@ -1575,14 +1659,14 @@ _stretch(ImagingObject* self, PyObject* args)
int xsize, ysize;
int filter = IMAGING_TRANSFORM_NEAREST;
if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
- return NULL;
+ return NULL;
imIn = self->image;
/* two-pass resize: minimize size of intermediate image */
- if (imIn->xsize * ysize < xsize * imIn->ysize)
+ if ((Py_ssize_t) imIn->xsize * ysize < (Py_ssize_t) xsize * imIn->ysize)
imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize);
- else
+ else
imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
if (!imTemp)
return NULL;
@@ -1607,11 +1691,11 @@ _stretch(ImagingObject* self, PyObject* args)
}
ImagingDelete(imTemp);
-
+
return PyImagingNew(imOut);
}
-static PyObject*
+static PyObject*
_transform2(ImagingObject* self, PyObject* args)
{
static const char* wrong_number = "wrong number of matrix entries";
@@ -1629,10 +1713,10 @@ _transform2(ImagingObject* self, PyObject* args)
int fill = 1;
if (!PyArg_ParseTuple(args, "(iiii)O!iO|ii",
&x0, &y0, &x1, &y1,
- &Imaging_Type, &imagep,
+ &Imaging_Type, &imagep,
&method, &data,
&filter, &fill))
- return NULL;
+ return NULL;
switch (method) {
case IMAGING_TRANSFORM_AFFINE:
@@ -1686,7 +1770,7 @@ _transform2(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_transpose(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@@ -1694,10 +1778,10 @@ _transpose(ImagingObject* self, PyObject* args)
int op;
if (!PyArg_ParseTuple(args, "i", &op))
- return NULL;
+ return NULL;
imIn = self->image;
-
+
switch (op) {
case 0: /* flip left right */
case 1: /* flip top bottom */
@@ -1736,7 +1820,7 @@ _transpose(ImagingObject* self, PyObject* args)
}
#ifdef WITH_UNSHARPMASK
-static PyObject*
+static PyObject*
_unsharp_mask(ImagingObject* self, PyObject* args)
{
Imaging imIn;
@@ -1762,25 +1846,25 @@ _unsharp_mask(ImagingObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-static PyObject*
+static PyObject*
_isblock(ImagingObject* self, PyObject* args)
{
return PyInt_FromLong((long) self->image->block);
}
-static PyObject*
+static PyObject*
_getbbox(ImagingObject* self, PyObject* args)
{
int bbox[4];
if (!ImagingGetBBox(self->image, bbox)) {
- Py_INCREF(Py_None);
- return Py_None;
+ Py_INCREF(Py_None);
+ return Py_None;
}
return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]);
}
-static PyObject*
+static PyObject*
_getcolors(ImagingObject* self, PyObject* args)
{
ImagingColorItem* items;
@@ -1789,7 +1873,7 @@ _getcolors(ImagingObject* self, PyObject* args)
int maxcolors = 256;
if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors))
- return NULL;
+ return NULL;
items = ImagingGetColors(self->image, maxcolors, &colors);
if (!items)
@@ -1814,7 +1898,7 @@ _getcolors(ImagingObject* self, PyObject* args)
return out;
}
-static PyObject*
+static PyObject*
_getextrema(ImagingObject* self, PyObject* args)
{
union {
@@ -1823,7 +1907,7 @@ _getextrema(ImagingObject* self, PyObject* args)
FLOAT32 f[2];
} extrema;
int status;
-
+
status = ImagingGetExtrema(self->image, &extrema);
if (status < 0)
return NULL;
@@ -1831,7 +1915,7 @@ _getextrema(ImagingObject* self, PyObject* args)
if (status)
switch (self->image->type) {
case IMAGING_TYPE_UINT8:
- return Py_BuildValue("ii", extrema.u[0], extrema.u[1]);
+ return Py_BuildValue("BB", extrema.u[0], extrema.u[1]);
case IMAGING_TYPE_INT32:
return Py_BuildValue("ii", extrema.i[0], extrema.i[1]);
case IMAGING_TYPE_FLOAT32:
@@ -1842,7 +1926,7 @@ _getextrema(ImagingObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_getprojection(ImagingObject* self, PyObject* args)
{
unsigned char* xprofile;
@@ -1853,15 +1937,16 @@ _getprojection(ImagingObject* self, PyObject* args)
yprofile = malloc(self->image->ysize);
if (xprofile == NULL || yprofile == NULL) {
- free(xprofile);
- free(yprofile);
- return PyErr_NoMemory();
+ free(xprofile);
+ free(yprofile);
+ return PyErr_NoMemory();
}
ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile);
- result = Py_BuildValue("s#s#", xprofile, self->image->xsize,
- yprofile, self->image->ysize);
+ result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH,
+ xprofile, self->image->xsize,
+ yprofile, self->image->ysize);
free(xprofile);
free(yprofile);
@@ -1871,45 +1956,45 @@ _getprojection(ImagingObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-static PyObject*
+static PyObject*
_getband(ImagingObject* self, PyObject* args)
{
int band;
if (!PyArg_ParseTuple(args, "i", &band))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingGetBand(self->image, band));
}
-static PyObject*
+static PyObject*
_fillband(ImagingObject* self, PyObject* args)
{
int band;
int color;
if (!PyArg_ParseTuple(args, "ii", &band, &color))
- return NULL;
+ return NULL;
if (!ImagingFillBand(self->image, band, color))
return NULL;
-
+
Py_INCREF(Py_None);
return Py_None;
}
-static PyObject*
+static PyObject*
_putband(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
int band;
if (!PyArg_ParseTuple(args, "O!i",
- &Imaging_Type, &imagep,
- &band))
- return NULL;
+ &Imaging_Type, &imagep,
+ &band))
+ return NULL;
if (!ImagingPutBand(self->image, imagep->image, band))
- return NULL;
+ return NULL;
Py_INCREF(Py_None);
return Py_None;
@@ -1919,68 +2004,68 @@ _putband(ImagingObject* self, PyObject* args)
#ifdef WITH_IMAGECHOPS
-static PyObject*
+static PyObject*
_chop_invert(ImagingObject* self, PyObject* args)
{
return PyImagingNew(ImagingNegative(self->image));
}
-static PyObject*
+static PyObject*
_chop_lighter(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopLighter(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_darker(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopDarker(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_difference(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopDifference(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_multiply(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopMultiply(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_screen(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopScreen(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_add(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@@ -1991,14 +2076,14 @@ _chop_add(ImagingObject* self, PyObject* args)
offset = 0;
if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep,
- &scale, &offset))
- return NULL;
+ &scale, &offset))
+ return NULL;
return PyImagingNew(ImagingChopAdd(self->image, imagep->image,
- scale, offset));
+ scale, offset));
}
-static PyObject*
+static PyObject*
_chop_subtract(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
@@ -2009,64 +2094,64 @@ _chop_subtract(ImagingObject* self, PyObject* args)
offset = 0;
if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep,
- &scale, &offset))
- return NULL;
+ &scale, &offset))
+ return NULL;
return PyImagingNew(ImagingChopSubtract(self->image, imagep->image,
- scale, offset));
+ scale, offset));
}
-static PyObject*
+static PyObject*
_chop_and(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopAnd(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_or(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopOr(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_xor(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopXor(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_add_modulo(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image));
}
-static PyObject*
+static PyObject*
_chop_subtract_modulo(ImagingObject* self, PyObject* args)
{
ImagingObject* imagep;
if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image));
}
@@ -2088,19 +2173,19 @@ _font_new(PyObject* self_, PyObject* args)
ImagingObject* imagep;
unsigned char* glyphdata;
int glyphdata_length;
- if (!PyArg_ParseTuple(args, "O!s#",
- &Imaging_Type, &imagep,
- &glyphdata, &glyphdata_length))
+ if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH,
+ &Imaging_Type, &imagep,
+ &glyphdata, &glyphdata_length))
return NULL;
if (glyphdata_length != 256 * 20) {
- PyErr_SetString(PyExc_ValueError, wrong_length);
- return NULL;
+ PyErr_SetString(PyExc_ValueError, wrong_length);
+ return NULL;
}
self = PyObject_New(ImagingFontObject, &ImagingFont_Type);
if (self == NULL)
- return NULL;
+ return NULL;
/* glyph bitmap */
self->bitmap = imagep->image;
@@ -2218,12 +2303,6 @@ static struct PyMethodDef _font_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_font_getattr(ImagingFontObject* self, char* name)
-{
- return Py_FindMethod(_font_methods, (PyObject*) self, name);
-}
-
/* -------------------------------------------------------------------- */
static PyObject*
@@ -2238,7 +2317,7 @@ _draw_new(PyObject* self_, PyObject* args)
self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type);
if (self == NULL)
- return NULL;
+ return NULL;
/* keep a reference to the image object */
Py_INCREF(imagep);
@@ -2260,7 +2339,7 @@ _draw_dealloc(ImagingDrawObject* self)
extern int PyPath_Flatten(PyObject* data, double **xy);
-static PyObject*
+static PyObject*
_draw_ink(ImagingDrawObject* self, PyObject* args)
{
INT32 ink = 0;
@@ -2275,7 +2354,7 @@ _draw_ink(ImagingDrawObject* self, PyObject* args)
return PyInt_FromLong((int) ink);
}
-static PyObject*
+static PyObject*
_draw_arc(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@@ -2285,7 +2364,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "(iiii)iii|i",
&x0, &y0, &x1, &y1,
&start, &end, &ink))
- return NULL;
+ return NULL;
if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end,
&ink, op) < 0)
@@ -2295,7 +2374,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_bitmap(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@@ -2305,17 +2384,16 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
ImagingObject* bitmap;
int ink;
if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
if (n != 1) {
- PyErr_SetString(
- PyExc_TypeError,
- "coordinate list must contain exactly 1 coordinate"
- );
- return NULL;
+ PyErr_SetString(PyExc_TypeError,
+ "coordinate list must contain exactly 1 coordinate"
+ );
+ return NULL;
}
n = ImagingDrawBitmap(
@@ -2332,7 +2410,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_chord(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@@ -2340,7 +2418,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
int start, end;
if (!PyArg_ParseTuple(args, "(iiii)iiii",
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
- return NULL;
+ return NULL;
if (ImagingDrawChord(self->image->image, x0, y0, x1, y1,
start, end, &ink, fill, self->blend) < 0)
@@ -2350,7 +2428,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_ellipse(ImagingDrawObject* self, PyObject* args)
{
double* xy;
@@ -2360,24 +2438,24 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
int ink;
int fill = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
if (n != 2) {
- PyErr_SetString(
- PyExc_TypeError,
- "coordinate list must contain exactly 2 coordinates"
- );
- return NULL;
+ PyErr_SetString(PyExc_TypeError,
+ "coordinate list must contain exactly 2 coordinates"
+ );
+ return NULL;
}
- n = ImagingDrawEllipse(
- self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3],
- &ink, fill, self->blend
- );
-
+ n = ImagingDrawEllipse(self->image->image,
+ (int) xy[0], (int) xy[1],
+ (int) xy[2], (int) xy[3],
+ &ink, fill, self->blend
+ );
+
free(xy);
if (n < 0)
@@ -2387,23 +2465,23 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_line(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
int ink;
if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink))
- return NULL;
+ return NULL;
if (ImagingDrawLine(self->image->image, x0, y0, x1, y1,
&ink, self->blend) < 0)
- return NULL;
+ return NULL;
Py_INCREF(Py_None);
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_lines(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@@ -2413,15 +2491,15 @@ _draw_lines(ImagingDrawObject* self, PyObject* args)
int ink;
int width = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
if (width <= 1) {
double *p = NULL;
- for (i = 0; i < n-1; i++) {
+ for (i = 0; i < n-1; i++) {
p = &xy[i+i];
if (ImagingDrawLine(
self->image->image,
@@ -2456,22 +2534,22 @@ _draw_lines(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_point(ImagingDrawObject* self, PyObject* args)
{
int x, y;
int ink;
if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink))
- return NULL;
+ return NULL;
if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0)
- return NULL;
+ return NULL;
Py_INCREF(Py_None);
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_points(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@@ -2480,19 +2558,19 @@ _draw_points(ImagingDrawObject* self, PyObject* args)
PyObject *data;
int ink;
if (!PyArg_ParseTuple(args, "Oi", &data, &ink))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
for (i = 0; i < n; i++) {
- double *p = &xy[i+i];
- if (ImagingDrawPoint(self->image->image, (int) p[0], (int) p[1],
+ double *p = &xy[i+i];
+ if (ImagingDrawPoint(self->image->image, (int) p[0], (int) p[1],
&ink, self->blend) < 0) {
- free(xy);
- return NULL;
- }
+ free(xy);
+ return NULL;
+ }
}
free(xy);
@@ -2501,12 +2579,12 @@ _draw_points(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-#ifdef WITH_ARROW
+#ifdef WITH_ARROW
/* from outline.c */
extern ImagingOutline PyOutline_AsOutline(PyObject* outline);
-static PyObject*
+static PyObject*
_draw_outline(ImagingDrawObject* self, PyObject* args)
{
ImagingOutline outline;
@@ -2515,7 +2593,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
int ink;
int fill = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill))
- return NULL;
+ return NULL;
outline = PyOutline_AsOutline(outline_);
if (!outline) {
@@ -2525,7 +2603,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
if (ImagingDrawOutline(self->image->image, outline,
&ink, fill, self->blend) < 0)
- return NULL;
+ return NULL;
Py_INCREF(Py_None);
return Py_None;
@@ -2533,7 +2611,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args)
#endif
-static PyObject*
+static PyObject*
_draw_pieslice(ImagingDrawObject* self, PyObject* args)
{
int x0, y0, x1, y1;
@@ -2541,7 +2619,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
int start, end;
if (!PyArg_ParseTuple(args, "(iiii)iiii",
&x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
- return NULL;
+ return NULL;
if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1,
start, end, &ink, fill, self->blend) < 0)
@@ -2551,7 +2629,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_polygon(ImagingDrawObject* self, PyObject* args)
{
double *xy;
@@ -2562,33 +2640,32 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
int ink;
int fill = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
if (n < 2) {
- PyErr_SetString(
- PyExc_TypeError,
- "coordinate list must contain at least 2 coordinates"
- );
- return NULL;
+ PyErr_SetString(PyExc_TypeError,
+ "coordinate list must contain at least 2 coordinates"
+ );
+ return NULL;
}
/* Copy list of vertices to array */
ixy = (int*) malloc(n * 2 * sizeof(int));
for (i = 0; i < n; i++) {
- ixy[i+i] = (int) xy[i+i];
- ixy[i+i+1] = (int) xy[i+i+1];
+ ixy[i+i] = (int) xy[i+i];
+ ixy[i+i+1] = (int) xy[i+i+1];
}
free(xy);
if (ImagingDrawPolygon(self->image->image, n, ixy,
&ink, fill, self->blend) < 0) {
- free(ixy);
- return NULL;
+ free(ixy);
+ return NULL;
}
free(ixy);
@@ -2597,7 +2674,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw_rectangle(ImagingDrawObject* self, PyObject* args)
{
double* xy;
@@ -2607,24 +2684,24 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
int ink;
int fill = 0;
if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
- return NULL;
+ return NULL;
n = PyPath_Flatten(data, &xy);
if (n < 0)
- return NULL;
+ return NULL;
if (n != 2) {
- PyErr_SetString(
- PyExc_TypeError,
- "coordinate list must contain exactly 2 coordinates"
- );
- return NULL;
+ PyErr_SetString(PyExc_TypeError,
+ "coordinate list must contain exactly 2 coordinates"
+ );
+ return NULL;
}
- n = ImagingDrawRectangle(
- self->image->image, (int) xy[0], (int) xy[1],
- (int) xy[2], (int) xy[3], &ink, fill, self->blend
- );
-
+ n = ImagingDrawRectangle(self->image->image,
+ (int) xy[0], (int) xy[1],
+ (int) xy[2], (int) xy[3],
+ &ink, fill, self->blend
+ );
+
free(xy);
if (n < 0)
@@ -2656,12 +2733,6 @@ static struct PyMethodDef _draw_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_draw_getattr(ImagingDrawObject* self, char* name)
-{
- return Py_FindMethod(_draw_methods, (PyObject*) self, name);
-}
-
#endif
@@ -2676,7 +2747,7 @@ pixel_access_new(ImagingObject* imagep, PyObject* args)
self = PyObject_New(PixelAccessObject, &PixelAccess_Type);
if (self == NULL)
- return NULL;
+ return NULL;
/* keep a reference to the image object */
Py_INCREF(imagep);
@@ -2720,8 +2791,8 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color)
return -1;
if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
- PyErr_SetString(PyExc_IndexError, outside_image);
- return -1;
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return -1;
}
if (!color) /* FIXME: raise exception? */
@@ -2736,12 +2807,12 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color)
}
/* -------------------------------------------------------------------- */
-/* EFFECTS (experimental) */
+/* EFFECTS (experimental) */
/* -------------------------------------------------------------------- */
#ifdef WITH_EFFECTS
-static PyObject*
+static PyObject*
_effect_mandelbrot(ImagingObject* self, PyObject* args)
{
int xsize = 512;
@@ -2755,29 +2826,29 @@ _effect_mandelbrot(ImagingObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "|(ii)(dddd)i", &xsize, &ysize,
&extent[0], &extent[1], &extent[2], &extent[3],
&quality))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality));
}
-static PyObject*
+static PyObject*
_effect_noise(ImagingObject* self, PyObject* args)
{
int xsize, ysize;
float sigma = 128;
if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma));
}
-static PyObject*
+static PyObject*
_effect_spread(ImagingObject* self, PyObject* args)
{
int dist;
if (!PyArg_ParseTuple(args, "i", &dist))
- return NULL;
+ return NULL;
return PyImagingNew(ImagingEffectSpread(self->image, dist));
}
@@ -2785,10 +2856,10 @@ _effect_spread(ImagingObject* self, PyObject* args)
#endif
/* -------------------------------------------------------------------- */
-/* UTILITIES */
+/* UTILITIES */
/* -------------------------------------------------------------------- */
-static PyObject*
+static PyObject*
_crc32(PyObject* self, PyObject* args)
{
unsigned char* buffer;
@@ -2798,8 +2869,9 @@ _crc32(PyObject* self, PyObject* args)
hi = lo = 0;
- if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo))
- return NULL;
+ if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"|(ii)",
+ &buffer, &bytes, &hi, &lo))
+ return NULL;
crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF);
@@ -2808,48 +2880,47 @@ _crc32(PyObject* self, PyObject* args)
return Py_BuildValue("ii", (crc >> 16) & 0xFFFF, crc & 0xFFFF);
}
-static PyObject*
+static PyObject*
_getcodecstatus(PyObject* self, PyObject* args)
{
int status;
char* msg;
if (!PyArg_ParseTuple(args, "i", &status))
- return NULL;
+ return NULL;
switch (status) {
case IMAGING_CODEC_OVERRUN:
- msg = "buffer overrun"; break;
+ msg = "buffer overrun"; break;
case IMAGING_CODEC_BROKEN:
- msg = "broken data stream"; break;
+ msg = "broken data stream"; break;
case IMAGING_CODEC_UNKNOWN:
- msg = "unrecognized data stream contents"; break;
+ msg = "unrecognized data stream contents"; break;
case IMAGING_CODEC_CONFIG:
- msg = "codec configuration error"; break;
+ msg = "codec configuration error"; break;
case IMAGING_CODEC_MEMORY:
- msg = "out of memory"; break;
+ msg = "out of memory"; break;
default:
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
- return PyString_FromString(msg);
+ return PyUnicode_FromString(msg);
}
/* -------------------------------------------------------------------- */
-/* DEBUGGING HELPERS */
+/* DEBUGGING HELPERS */
/* -------------------------------------------------------------------- */
#ifdef WITH_DEBUG
-static PyObject*
+static PyObject*
_save_ppm(ImagingObject* self, PyObject* args)
{
char* filename;
if (!PyArg_ParseTuple(args, "s", &filename))
- return NULL;
+ return NULL;
if (!ImagingSavePPM(self->image, filename))
return NULL;
@@ -2876,6 +2947,7 @@ static struct PyMethodDef methods[] = {
{"convert", (PyCFunction)_convert, 1},
{"convert2", (PyCFunction)_convert2, 1},
{"convert_matrix", (PyCFunction)_convert_matrix, 1},
+ {"convert_transparent", (PyCFunction)_convert_transparent, 1},
{"copy", (PyCFunction)_copy, 1},
{"copy2", (PyCFunction)_copy2, 1},
#ifdef WITH_CRACKCODE
@@ -2917,10 +2989,12 @@ static struct PyMethodDef methods[] = {
{"fillband", (PyCFunction)_fillband, 1},
{"setmode", (PyCFunction)im_setmode, 1},
-
+
{"getpalette", (PyCFunction)_getpalette, 1},
+ {"getpalettemode", (PyCFunction)_getpalettemode, 1},
{"putpalette", (PyCFunction)_putpalette, 1},
{"putpalettealpha", (PyCFunction)_putpalettealpha, 1},
+ {"putpalettealphas", (PyCFunction)_putpalettealphas, 1},
#ifdef WITH_IMAGECHOPS
/* Channel operations (ImageChops) */
@@ -2964,29 +3038,48 @@ static struct PyMethodDef methods[] = {
/* attributes */
-static PyObject*
-_getattr(ImagingObject* self, char* name)
+static PyObject*
+_getattr_mode(ImagingObject* self, void* closure)
{
- PyObject* res;
-
- res = Py_FindMethod(methods, (PyObject*) self, name);
- if (res)
- return res;
- PyErr_Clear();
- if (strcmp(name, "mode") == 0)
- return PyString_FromString(self->image->mode);
- if (strcmp(name, "size") == 0)
- return Py_BuildValue("ii", self->image->xsize, self->image->ysize);
- if (strcmp(name, "bands") == 0)
- return PyInt_FromLong(self->image->bands);
- if (strcmp(name, "id") == 0)
- return PyInt_FromLong((long) self->image);
- if (strcmp(name, "ptr") == 0)
- return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL);
- PyErr_SetString(PyExc_AttributeError, name);
- return NULL;
+ return PyUnicode_FromString(self->image->mode);
}
+static PyObject*
+_getattr_size(ImagingObject* self, void* closure)
+{
+ return Py_BuildValue("ii", self->image->xsize, self->image->ysize);
+}
+
+static PyObject*
+_getattr_bands(ImagingObject* self, void* closure)
+{
+ return PyInt_FromLong(self->image->bands);
+}
+
+static PyObject*
+_getattr_id(ImagingObject* self, void* closure)
+{
+ return PyInt_FromSsize_t((Py_ssize_t) self->image);
+}
+
+static PyObject*
+_getattr_ptr(ImagingObject* self, void* closure)
+{
+#if (PY_VERSION_HEX >= 0x02070000 && PY_VERSION_HEX < 0x03000000) || PY_VERSION_HEX >= 0x03010000
+ return PyCapsule_New(self->image, IMAGING_MAGIC, NULL);
+#else
+ return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL);
+#endif
+}
+
+static struct PyGetSetDef getsetters[] = {
+ { "mode", (getter) _getattr_mode },
+ { "size", (getter) _getattr_size },
+ { "bands", (getter) _getattr_bands },
+ { "id", (getter) _getattr_id },
+ { "ptr", (getter) _getattr_ptr },
+ { NULL }
+};
/* basic sequence semantics */
@@ -2995,7 +3088,7 @@ image_length(ImagingObject *self)
{
Imaging im = self->image;
- return im->xsize * im->ysize;
+ return (Py_ssize_t) im->xsize * im->ysize;
}
static PyObject *
@@ -3014,7 +3107,7 @@ image_item(ImagingObject *self, Py_ssize_t i)
}
static PySequenceMethods image_as_sequence = {
- (inquiry) image_length, /*sq_length*/
+ (lenfunc) image_length, /*sq_length*/
(binaryfunc) NULL, /*sq_concat*/
(ssizeargfunc) NULL, /*sq_repeat*/
(ssizeargfunc) image_item, /*sq_item*/
@@ -3026,64 +3119,123 @@ static PySequenceMethods image_as_sequence = {
/* type description */
-statichere PyTypeObject Imaging_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "ImagingCore", /*tp_name*/
- sizeof(ImagingObject), /*tp_size*/
- 0, /*tp_itemsize*/
+static PyTypeObject Imaging_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "ImagingCore", /*tp_name*/
+ sizeof(ImagingObject), /*tp_size*/
+ 0, /*tp_itemsize*/
/* methods */
- (destructor)_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- (getattrfunc)_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
+ (destructor)_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
0, /*tp_as_number */
&image_as_sequence, /*tp_as_sequence */
0, /*tp_as_mapping */
- 0 /*tp_hash*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ getsetters, /*tp_getset*/
};
#ifdef WITH_IMAGEDRAW
-statichere PyTypeObject ImagingFont_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "ImagingFont", /*tp_name*/
- sizeof(ImagingFontObject), /*tp_size*/
- 0, /*tp_itemsize*/
+static PyTypeObject ImagingFont_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "ImagingFont", /*tp_name*/
+ sizeof(ImagingFontObject), /*tp_size*/
+ 0, /*tp_itemsize*/
/* methods */
- (destructor)_font_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- (getattrfunc)_font_getattr, /*tp_getattr*/
+ (destructor)_font_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ _font_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
-statichere PyTypeObject ImagingDraw_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "ImagingDraw", /*tp_name*/
- sizeof(ImagingDrawObject), /*tp_size*/
- 0, /*tp_itemsize*/
+static PyTypeObject ImagingDraw_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "ImagingDraw", /*tp_name*/
+ sizeof(ImagingDrawObject), /*tp_size*/
+ 0, /*tp_itemsize*/
/* methods */
- (destructor)_draw_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- (getattrfunc)_draw_getattr, /*tp_getattr*/
+ (destructor)_draw_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ _draw_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
#endif
static PyMappingMethods pixel_access_as_mapping = {
- (inquiry) NULL, /*mp_length*/
+ (lenfunc) NULL, /*mp_length*/
(binaryfunc) pixel_access_getitem, /*mp_subscript*/
(objobjargproc) pixel_access_setitem, /*mp_ass_subscript*/
};
/* type description */
-statichere PyTypeObject PixelAccess_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "PixelAccess", sizeof(PixelAccessObject), 0,
+static PyTypeObject PixelAccess_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "PixelAccess", sizeof(PixelAccessObject), 0,
/* methods */
(destructor)pixel_access_dealloc, /*tp_dealloc*/
0, /*tp_print*/
@@ -3109,6 +3261,7 @@ extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
@@ -3127,6 +3280,7 @@ extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args);
extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args);
/* Display support etc (in display.c) */
#ifdef WIN32
@@ -3152,6 +3306,7 @@ extern PyObject* PyImaging_MapBuffer(PyObject* self, PyObject* args);
static PyMethodDef functions[] = {
/* Object factories */
+ {"alpha_composite", (PyCFunction)_alpha_composite, 1},
{"blend", (PyCFunction)_blend, 1},
{"fill", (PyCFunction)_fill, 1},
{"new", (PyCFunction)_new, 1},
@@ -3175,6 +3330,10 @@ static PyMethodDef functions[] = {
{"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
#endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
+#ifdef HAVE_LIBTIFF
+ {"libtiff_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
+ {"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
+#endif
{"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
{"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},
@@ -3237,7 +3396,7 @@ static PyMethodDef functions[] = {
#ifdef WITH_IMAGEPATH
{"path", (PyCFunction)PyPath_Create, 1},
#endif
-
+
/* Experimental arrow graphics stuff */
#ifdef WITH_ARROW
{"outline", (PyCFunction)PyOutline_Create, 1},
@@ -3246,36 +3405,77 @@ static PyMethodDef functions[] = {
{NULL, NULL} /* sentinel */
};
-DL_EXPORT(void)
-init_imaging(void)
-{
- PyObject* m;
- PyObject* d;
+static int
+setup_module(PyObject* m) {
+ PyObject* d = PyModule_GetDict(m);
+
+ /* Ready object types */
+ if (PyType_Ready(&Imaging_Type) < 0)
+ return -1;
- /* Patch object type */
- Imaging_Type.ob_type = &PyType_Type;
#ifdef WITH_IMAGEDRAW
- ImagingFont_Type.ob_type = &PyType_Type;
- ImagingDraw_Type.ob_type = &PyType_Type;
+ if (PyType_Ready(&ImagingFont_Type) < 0)
+ return -1;
+
+ if (PyType_Ready(&ImagingDraw_Type) < 0)
+ return -1;
#endif
- PixelAccess_Type.ob_type = &PyType_Type;
+ if (PyType_Ready(&PixelAccess_Type) < 0)
+ return -1;
ImagingAccessInit();
- m = Py_InitModule("_imaging", functions);
- d = PyModule_GetDict(m);
-
#ifdef HAVE_LIBJPEG
{
extern const char* ImagingJpegVersion(void);
- PyDict_SetItemString(d, "jpeglib_version", PyString_FromString(ImagingJpegVersion()));
+ PyDict_SetItemString(d, "jpeglib_version", PyUnicode_FromString(ImagingJpegVersion()));
}
#endif
#ifdef HAVE_LIBZ
+ /* zip encoding strategies */
+ PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY);
+ PyModule_AddIntConstant(m, "FILTERED", Z_FILTERED);
+ PyModule_AddIntConstant(m, "HUFFMAN_ONLY", Z_HUFFMAN_ONLY);
+ PyModule_AddIntConstant(m, "RLE", Z_RLE);
+ PyModule_AddIntConstant(m, "FIXED", Z_FIXED);
{
extern const char* ImagingZipVersion(void);
- PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion()));
+ PyDict_SetItemString(d, "zlib_version", PyUnicode_FromString(ImagingZipVersion()));
}
#endif
+
+ PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(PILLOW_VERSION));
+
+ return 0;
}
+
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__imaging(void) {
+ PyObject* m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imaging", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ functions, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+
+ if (setup_module(m) < 0)
+ return NULL;
+
+ return m;
+}
+#else
+PyMODINIT_FUNC
+init_imaging(void)
+{
+ PyObject* m = Py_InitModule("_imaging", functions);
+ setup_module(m);
+}
+#endif
+
diff --git a/_imagingcms.c b/_imagingcms.c
index e9a3b00c5..99da647ae 100644
--- a/_imagingcms.c
+++ b/_imagingcms.c
@@ -1,4 +1,4 @@
-/*
+/*
* pyCMS
* a Python / PIL interface to the littleCMS ICC Color Management System
* Copyright (C) 2002-2003 Kevin Cazabon
@@ -6,11 +6,11 @@
* http://www.cazabon.com
* Adapted/reworked for PIL by Fredrik Lundh
* Copyright (c) 2009 Fredrik Lundh
- *
+ *
* pyCMS home page: http://www.cazabon.com/pyCMS
* littleCMS home page: http://www.littlecms.com
* (littleCMS is Copyright (C) 1998-2001 Marti Maria)
- *
+ *
* Originally released under LGPL. Graciously donated to PIL in
* March 2009, for distribution under the standard PIL license
*/
@@ -24,27 +24,22 @@ http://www.cazabon.com\n\
"
#include "Python.h"
-#include "lcms.h"
+#include "lcms2.h"
#include "Imaging.h"
-
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
-#if LCMS_VERSION < 117
-#define LCMSBOOL BOOL
-#endif
+#include "py3.h"
#ifdef WIN32
+#include
+#include
#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
@@ -59,7 +54,7 @@ http://www.cazabon.com\n\
/* known to-do list with current version:
Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all PIL modes (it probably isn't for the more obscure ones)
-
+
Add support for creating custom RGB profiles on the fly
Add support for checking presence of a specific tag in a profile
Add support for other littleCMS features as required
@@ -83,9 +78,9 @@ typedef struct {
cmsHPROFILE profile;
} CmsProfileObject;
-staticforward PyTypeObject CmsProfile_Type;
+static PyTypeObject CmsProfile_Type;
-#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type)
+#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type)
static PyObject*
cms_profile_new(cmsHPROFILE profile)
@@ -110,8 +105,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");
@@ -128,14 +121,19 @@ cms_profile_fromstring(PyObject* self, PyObject* args)
char* pProfile;
int nProfile;
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile))
+ return NULL;
+#else
if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile))
return NULL;
-
- cmsErrorAction(LCMS_ERROR_IGNORE);
+#endif
hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
- if (!hProfile)
+ if (!hProfile) {
PyErr_SetString(PyExc_IOError, "cannot open profile from string");
+ return NULL;
+ }
return cms_profile_new(hProfile);
}
@@ -156,9 +154,9 @@ typedef struct {
cmsHTRANSFORM transform;
} CmsTransformObject;
-staticforward PyTypeObject CmsTransform_Type;
+static PyTypeObject CmsTransform_Type;
-#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type)
+#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type)
static PyObject*
cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out)
@@ -188,25 +186,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) {
@@ -239,6 +237,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. */
@@ -265,12 +267,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 */
@@ -289,12 +289,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 */
@@ -332,8 +330,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)
@@ -359,10 +355,8 @@ 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)
return NULL;
@@ -373,21 +367,19 @@ buildProofTransform(PyObject *self, PyObject *args)
static PyObject *
cms_transform_apply(CmsTransformObject *self, PyObject *args)
{
- long idIn;
- long idOut;
+ Py_ssize_t idIn;
+ Py_ssize_t idOut;
Imaging im;
Imaging imOut;
int result;
- if (!PyArg_ParseTuple(args, "ll:apply", &idIn, &idOut))
+ if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut))
return NULL;
im = (Imaging) idIn;
imOut = (Imaging) idOut;
- cmsErrorAction(LCMS_ERROR_IGNORE);
-
result = pyCMSdoTransform(im, imOut, self->transform);
return Py_BuildValue("i", result);
@@ -401,32 +393,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");
@@ -442,7 +436,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;
@@ -461,7 +455,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;
@@ -480,7 +474,7 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args)
}
if (ok)
- return PyString_FromStringAndSize(filename, filename_size-1);
+ return PyUnicode_FromStringAndSize(filename, filename_size-1);
Py_INCREF(Py_None);
return Py_None;
@@ -493,6 +487,7 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args)
static PyMethodDef pyCMSdll_methods[] = {
{"profile_open", cms_profile_open, 1},
+ {"profile_frombytes", cms_profile_fromstring, 1},
{"profile_fromstring", cms_profile_fromstring, 1},
/* profile and transform functions */
@@ -513,40 +508,121 @@ static struct PyMethodDef cms_profile_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-cms_profile_getattr(CmsProfileObject* self, char* name)
+static PyObject*
+_profile_getattr(CmsProfileObject* self, cmsInfoType field)
{
- if (!strcmp(name, "product_name"))
- return PyString_FromString(cmsTakeProductName(self->profile));
- if (!strcmp(name, "product_desc"))
- return PyString_FromString(cmsTakeProductDesc(self->profile));
- if (!strcmp(name, "product_info"))
- return PyString_FromString(cmsTakeProductInfo(self->profile));
- if (!strcmp(name, "rendering_intent"))
- return PyInt_FromLong(cmsTakeRenderingIntent(self->profile));
- if (!strcmp(name, "pcs"))
- return PyString_FromString(findICmode(cmsGetPCS(self->profile)));
- if (!strcmp(name, "color_space"))
- return PyString_FromString(findICmode(cmsGetColorSpace(self->profile)));
- /* FIXME: add more properties (creation_datetime etc) */
-
- return Py_FindMethod(cms_profile_methods, (PyObject*) self, name);
+ // 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("");
}
-statichere PyTypeObject CmsProfile_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "CmsProfile", sizeof(CmsProfileObject), 0,
+static PyObject*
+cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure)
+{
+ // 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_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(cmsGetHeaderRenderingIntent(self->profile));
+}
+
+static PyObject*
+cms_profile_getattr_pcs(CmsProfileObject* self, void* closure)
+{
+ return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile)));
+}
+
+static PyObject*
+cms_profile_getattr_color_space(CmsProfileObject* self, void* closure)
+{
+ return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile)));
+}
+
+/* FIXME: add more properties (creation_datetime etc) */
+static struct PyGetSetDef cms_profile_getsetters[] = {
+ { "product_desc", (getter) cms_profile_getattr_product_desc },
+ { "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 },
+ { NULL }
+};
+
+static PyTypeObject CmsProfile_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "CmsProfile", sizeof(CmsProfileObject), 0,
/* methods */
(destructor) cms_profile_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc) cms_profile_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number */
- 0, /*tp_as_sequence */
- 0, /*tp_as_mapping */
- 0 /*tp_hash*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ cms_profile_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ cms_profile_getsetters, /*tp_getset*/
};
static struct PyMethodDef cms_transform_methods[] = {
@@ -554,55 +630,101 @@ static struct PyMethodDef cms_transform_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-cms_transform_getattr(CmsTransformObject* self, char* name)
+static PyObject*
+cms_transform_getattr_inputMode(CmsTransformObject* self, void* closure)
{
- if (!strcmp(name, "inputMode"))
- return PyString_FromString(self->mode_in);
- if (!strcmp(name, "outputMode"))
- return PyString_FromString(self->mode_out);
-
- return Py_FindMethod(cms_transform_methods, (PyObject*) self, name);
+ return PyUnicode_FromString(self->mode_in);
}
-statichere PyTypeObject CmsTransform_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "CmsTransform", sizeof(CmsTransformObject), 0,
+static PyObject*
+cms_transform_getattr_outputMode(CmsTransformObject* self, void* closure)
+{
+ return PyUnicode_FromString(self->mode_out);
+}
+
+static struct PyGetSetDef cms_transform_getsetters[] = {
+ { "inputMode", (getter) cms_transform_getattr_inputMode },
+ { "outputMode", (getter) cms_transform_getattr_outputMode },
+ { NULL }
+};
+
+static PyTypeObject CmsTransform_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "CmsTransform", sizeof(CmsTransformObject), 0,
/* methods */
(destructor) cms_transform_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc) cms_transform_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_as_number */
- 0, /*tp_as_sequence */
- 0, /*tp_as_mapping */
- 0 /*tp_hash*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ cms_transform_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ cms_transform_getsetters, /*tp_getset*/
};
-DL_EXPORT(void)
-init_imagingcms(void)
-{
- PyObject *m;
+static int
+setup_module(PyObject* m) {
PyObject *d;
PyObject *v;
- /* Patch up object types */
- CmsProfile_Type.ob_type = &PyType_Type;
- CmsTransform_Type.ob_type = &PyType_Type;
-
- m = Py_InitModule("_imagingcms", pyCMSdll_methods);
d = PyModule_GetDict(m);
-#if PY_VERSION_HEX >= 0x02020000
- v = PyString_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100);
-#else
- {
- char buffer[100];
- sprintf(buffer, "%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100);
- v = PyString_FromString(buffer);
- }
-#endif
+ /* Ready object types */
+ PyType_Ready(&CmsProfile_Type);
+ PyType_Ready(&CmsTransform_Type);
+
+ d = PyModule_GetDict(m);
+
+ v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100);
PyDict_SetItemString(d, "littlecms_version", v);
+
+ return 0;
}
+
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__imagingcms(void) {
+ PyObject* m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imagingcms", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ pyCMSdll_methods, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+
+ if (setup_module(m) < 0)
+ return NULL;
+
+ return m;
+}
+#else
+PyMODINIT_FUNC
+init_imagingcms(void)
+{
+ PyObject *m = Py_InitModule("_imagingcms", pyCMSdll_methods);
+ setup_module(m);
+}
+#endif
+
diff --git a/_imagingft.c b/_imagingft.c
index 935808718..d99bb91b2 100644
--- a/_imagingft.c
+++ b/_imagingft.c
@@ -35,21 +35,10 @@
#include
#endif
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
+#include FT_GLYPH_H
-#if PY_VERSION_HEX >= 0x01060000
-#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE)
-/* defining this enables unicode support (default under 1.6a1 and later) */
-#define HAVE_UNICODE
-#endif
-#endif
-
-#if !defined(Py_RETURN_NONE)
-#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
-#endif
+#define KEEP_PY_UNICODE
+#include "py3.h"
#if !defined(FT_LOAD_TARGET_MONO)
#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
@@ -70,7 +59,11 @@ struct {
const char* message;
} ft_errors[] =
+#if defined(USE_FREETYPE_2_1)
+#include FT_ERRORS_H
+#else
#include
+#endif
/* -------------------------------------------------------------------- */
/* font objects */
@@ -82,7 +75,7 @@ typedef struct {
FT_Face face;
} FontObject;
-staticforward PyTypeObject Font_Type;
+static PyTypeObject Font_Type;
/* round a 26.6 pixel coordinate to the nearest larger integer */
#define PIXEL(x) ((((x)+63) & -64)>>6)
@@ -110,25 +103,16 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
FontObject* self;
int error;
- char* filename;
+ char* filename = NULL;
int size;
int index = 0;
- unsigned char* encoding = NULL;
+ unsigned char* encoding;
+ unsigned char* font_bytes;
+ int font_bytes_size = 0;
static char* kwlist[] = {
- "filename", "size", "index", "encoding", NULL
+ "filename", "size", "index", "encoding", "font_bytes", NULL
};
-#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000
- if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist,
- Py_FileSystemDefaultEncoding, &filename,
- &size, &index, &encoding))
- return NULL;
-#else
- if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist,
- &filename, &size, &index, &encoding))
- return NULL;
-#endif
-
if (!library) {
PyErr_SetString(
PyExc_IOError,
@@ -136,12 +120,24 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
);
return NULL;
}
-
- self = PyObject_New(FontObject, &Font_Type);
- if (!self)
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist,
+ Py_FileSystemDefaultEncoding, &filename,
+ &size, &index, &encoding, &font_bytes,
+ &font_bytes_size))
return NULL;
- error = FT_New_Face(library, filename, index, &self->face);
+ self = PyObject_New(FontObject, &Font_Type);
+ if (!self) {
+ if (filename)
+ PyMem_Free(filename);
+ return NULL;
+ }
+
+ if (filename && font_bytes_size <= 0) {
+ error = FT_New_Face(library, filename, index, &self->face);
+ } else {
+ error = FT_New_Memory_Face(library, (FT_Byte*)font_bytes, font_bytes_size, index, &self->face);
+ }
if (!error)
error = FT_Set_Pixel_Sizes(self->face, 0, size);
@@ -152,6 +148,8 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
);
error = FT_Select_Charmap(self->face, encoding_tag);
}
+ if (filename)
+ PyMem_Free(filename);
if (error) {
PyObject_Del(self);
@@ -160,11 +158,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw)
return (PyObject*) self;
}
-
+
static int
font_getchar(PyObject* string, int index, FT_ULong* char_out)
{
-#if defined(HAVE_UNICODE)
if (PyUnicode_Check(string)) {
Py_UNICODE* p = PyUnicode_AS_UNICODE(string);
int size = PyUnicode_GET_SIZE(string);
@@ -173,7 +170,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
*char_out = p[index];
return 1;
}
-#endif
+
+#if PY_VERSION_HEX < 0x03000000
if (PyString_Check(string)) {
unsigned char* p = (unsigned char*) PyString_AS_STRING(string);
int size = PyString_GET_SIZE(string);
@@ -182,16 +180,18 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out)
*char_out = (unsigned char) p[index];
return 1;
}
+#endif
+
return 0;
}
static PyObject*
font_getsize(FontObject* self, PyObject* args)
{
- int i, x;
+ int i, x, y_max, y_min;
FT_ULong ch;
FT_Face face;
- int xoffset;
+ int xoffset, yoffset;
FT_Bool kerning = FT_HAS_KERNING(self->face);
FT_UInt last_index = 0;
@@ -201,20 +201,23 @@ font_getsize(FontObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "O:getsize", &string))
return NULL;
-#if defined(HAVE_UNICODE)
- if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyUnicode_Check(string)) {
#else
- if (!PyString_Check(string)) {
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
#endif
PyErr_SetString(PyExc_TypeError, "expected string");
return NULL;
}
face = NULL;
- xoffset = 0;
+ xoffset = yoffset = 0;
+ y_max = y_min = 0;
for (x = i = 0; font_getchar(string, i, &ch); i++) {
int index, error;
+ FT_BBox bbox;
+ FT_Glyph glyph;
face = self->face;
index = FT_Get_Char_Index(face, ch);
if (kerning && last_index && index) {
@@ -229,7 +232,20 @@ font_getsize(FontObject* self, PyObject* args)
if (i == 0)
xoffset = face->glyph->metrics.horiBearingX;
x += face->glyph->metrics.horiAdvance;
+
+ FT_Get_Glyph(face->glyph, &glyph);
+ FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox);
+ if (bbox.yMax > y_max)
+ y_max = bbox.yMax;
+ if (bbox.yMin < y_min)
+ y_min = bbox.yMin;
+
+ /* find max distance of baseline from top */
+ if (face->glyph->metrics.horiBearingY > yoffset)
+ yoffset = face->glyph->metrics.horiBearingY;
+
last_index = index;
+ FT_Done_Glyph(glyph);
}
if (face) {
@@ -245,12 +261,15 @@ font_getsize(FontObject* self, PyObject* args)
face->glyph->metrics.horiBearingX;
if (offset < 0)
x -= offset;
+ /* difference between the font ascender and the distance of
+ * the baseline from the top */
+ yoffset = PIXEL(self->face->size->metrics.ascender - yoffset);
}
return Py_BuildValue(
"(ii)(ii)",
- PIXEL(x), PIXEL(self->face->size->metrics.height),
- PIXEL(xoffset), 0
+ PIXEL(x), PIXEL(y_max - y_min),
+ PIXEL(xoffset), yoffset
);
}
@@ -267,10 +286,10 @@ font_getabc(FontObject* self, PyObject* args)
if (!PyArg_ParseTuple(args, "O:getabc", &string))
return NULL;
-#if defined(HAVE_UNICODE)
- if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyUnicode_Check(string)) {
#else
- if (!PyString_Check(string)) {
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
#endif
PyErr_SetString(PyExc_TypeError, "expected string");
return NULL;
@@ -285,7 +304,7 @@ font_getabc(FontObject* self, PyObject* args)
return geterror(error);
a = face->glyph->metrics.horiBearingX / 64.0;
b = face->glyph->metrics.width / 64.0;
- c = (face->glyph->metrics.horiAdvance -
+ c = (face->glyph->metrics.horiAdvance -
face->glyph->metrics.horiBearingX -
face->glyph->metrics.width) / 64.0;
} else
@@ -310,15 +329,17 @@ font_render(FontObject* self, PyObject* args)
/* render string into given buffer (the buffer *must* have
the right size, or this will crash) */
PyObject* string;
- long id;
+ Py_ssize_t id;
int mask = 0;
- if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask))
+ int temp;
+ int xx, x0, x1;
+ if (!PyArg_ParseTuple(args, "On|i:render", &string, &id, &mask))
return NULL;
-#if defined(HAVE_UNICODE)
- if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyUnicode_Check(string)) {
#else
- if (!PyString_Check(string)) {
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
#endif
PyErr_SetString(PyExc_TypeError, "expected string");
return NULL;
@@ -330,6 +351,18 @@ font_render(FontObject* self, PyObject* args)
if (mask)
load_flags |= FT_LOAD_TARGET_MONO;
+ ascender = 0;
+ for (i = 0; font_getchar(string, i, &ch); i++) {
+ index = FT_Get_Char_Index(self->face, ch);
+ error = FT_Load_Glyph(self->face, index, load_flags);
+ if (error)
+ return geterror(error);
+ glyph = self->face->glyph;
+ temp = (glyph->bitmap.rows - glyph->bitmap_top);
+ if (temp > ascender)
+ ascender = temp;
+ }
+
for (x = i = 0; font_getchar(string, i, &ch); i++) {
if (i == 0 && self->face->glyph->metrics.horiBearingX < 0)
x = -PIXEL(self->face->glyph->metrics.horiBearingX);
@@ -340,24 +373,26 @@ font_render(FontObject* self, PyObject* args)
&delta);
x += delta.x >> 6;
}
+
error = FT_Load_Glyph(self->face, index, load_flags);
if (error)
return geterror(error);
+
glyph = self->face->glyph;
+
+ source = (unsigned char*) glyph->bitmap.buffer;
+ xx = x + glyph->bitmap_left;
+ x0 = 0;
+ x1 = glyph->bitmap.width;
+ if (xx < 0)
+ x0 = -xx;
+ if (xx + x1 > im->xsize)
+ x1 = im->xsize - xx;
+
if (mask) {
/* use monochrome mask (on palette images, etc) */
- int xx, x0, x1;
- source = (unsigned char*) glyph->bitmap.buffer;
- ascender = PIXEL(self->face->size->metrics.ascender);
- xx = x + glyph->bitmap_left;
- x0 = 0;
- x1 = glyph->bitmap.width;
- if (xx < 0)
- x0 = -xx;
- if (xx + x1 > im->xsize)
- x1 = im->xsize - xx;
for (y = 0; y < glyph->bitmap.rows; y++) {
- int yy = y + ascender - glyph->bitmap_top;
+ int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender);
if (yy >= 0 && yy < im->ysize) {
/* blend this glyph into the buffer */
unsigned char *target = im->image8[yy] + xx;
@@ -375,18 +410,8 @@ font_render(FontObject* self, PyObject* args)
}
} else {
/* use antialiased rendering */
- int xx, x0, x1;
- source = (unsigned char*) glyph->bitmap.buffer;
- ascender = PIXEL(self->face->size->metrics.ascender);
- xx = x + glyph->bitmap_left;
- x0 = 0;
- x1 = glyph->bitmap.width;
- if (xx < 0)
- x0 = -xx;
- if (xx + x1 > im->xsize)
- x1 = im->xsize - xx;
for (y = 0; y < glyph->bitmap.rows; y++) {
- int yy = y + ascender - glyph->bitmap_top;
+ int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender);
if (yy >= 0 && yy < im->ysize) {
/* blend this glyph into the buffer */
int i;
@@ -420,49 +445,89 @@ static PyMethodDef font_methods[] = {
{NULL, NULL}
};
-static PyObject*
-font_getattr(FontObject* self, char* name)
+static PyObject*
+font_getattr_family(FontObject* self, void* closure)
{
- PyObject* res;
-
- res = Py_FindMethod(font_methods, (PyObject*) self, name);
-
- if (res)
- return res;
-
- PyErr_Clear();
-
- /* attributes */
- if (!strcmp(name, "family")) {
- if (self->face->family_name)
- return PyString_FromString(self->face->family_name);
- Py_RETURN_NONE;
- }
- if (!strcmp(name, "style")) {
- if (self->face->style_name)
- return PyString_FromString(self->face->style_name);
- Py_RETURN_NONE;
- }
- if (!strcmp(name, "ascent"))
- return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender));
- if (!strcmp(name, "descent"))
- return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender));
-
- if (!strcmp(name, "glyphs"))
- /* number of glyphs provided by this font */
- return PyInt_FromLong(self->face->num_glyphs);
-
- PyErr_SetString(PyExc_AttributeError, name);
- return NULL;
+#if PY_VERSION_HEX >= 0x03000000
+ if (self->face->family_name)
+ return PyUnicode_FromString(self->face->family_name);
+#else
+ if (self->face->family_name)
+ return PyString_FromString(self->face->family_name);
+#endif
+ Py_RETURN_NONE;
}
-statichere PyTypeObject Font_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, "Font", sizeof(FontObject), 0,
+static PyObject*
+font_getattr_style(FontObject* self, void* closure)
+{
+#if PY_VERSION_HEX >= 0x03000000
+ if (self->face->style_name)
+ return PyUnicode_FromString(self->face->style_name);
+#else
+ if (self->face->style_name)
+ return PyString_FromString(self->face->style_name);
+#endif
+ Py_RETURN_NONE;
+}
+
+static PyObject*
+font_getattr_ascent(FontObject* self, void* closure)
+{
+ return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender));
+}
+
+static PyObject*
+font_getattr_descent(FontObject* self, void* closure)
+{
+ return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender));
+}
+
+static PyObject*
+font_getattr_glyphs(FontObject* self, void* closure)
+{
+ return PyInt_FromLong(self->face->num_glyphs);
+}
+
+static struct PyGetSetDef font_getsetters[] = {
+ { "family", (getter) font_getattr_family },
+ { "style", (getter) font_getattr_style },
+ { "ascent", (getter) font_getattr_ascent },
+ { "descent", (getter) font_getattr_descent },
+ { "glyphs", (getter) font_getattr_glyphs },
+ { NULL }
+};
+
+static PyTypeObject Font_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "Font", sizeof(FontObject), 0,
/* methods */
(destructor)font_dealloc, /* tp_dealloc */
0, /* tp_print */
- (getattrfunc)font_getattr, /* tp_getattr */
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ font_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ font_getsetters, /*tp_getset*/
};
static PyMethodDef _functions[] = {
@@ -470,33 +535,58 @@ static PyMethodDef _functions[] = {
{NULL, NULL}
};
-DL_EXPORT(void)
-init_imagingft(void)
-{
- PyObject* m;
+static int
+setup_module(PyObject* m) {
PyObject* d;
PyObject* v;
int major, minor, patch;
- /* Patch object type */
- Font_Type.ob_type = &PyType_Type;
-
- m = Py_InitModule("_imagingft", _functions);
d = PyModule_GetDict(m);
+ /* Ready object type */
+ PyType_Ready(&Font_Type);
+
if (FT_Init_FreeType(&library))
- return; /* leave it uninitalized */
+ return 0; /* leave it uninitalized */
FT_Library_Version(library, &major, &minor, &patch);
-#if PY_VERSION_HEX >= 0x02020000
- v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
+#if PY_VERSION_HEX >= 0x03000000
+ v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch);
#else
- {
- char buffer[100];
- sprintf(buffer, "%d.%d.%d", major, minor, patch);
- v = PyString_FromString(buffer);
- }
+ v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
#endif
PyDict_SetItemString(d, "freetype2_version", v);
+
+ return 0;
}
+
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__imagingft(void) {
+ PyObject* m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imagingft", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ _functions, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+
+ if (setup_module(m) < 0)
+ return NULL;
+
+ return m;
+}
+#else
+PyMODINIT_FUNC
+init_imagingft(void)
+{
+ PyObject* m = Py_InitModule("_imagingft", _functions);
+ setup_module(m);
+}
+#endif
+
diff --git a/_imagingmath.c b/_imagingmath.c
index 928986bb3..c6334edeb 100644
--- a/_imagingmath.c
+++ b/_imagingmath.c
@@ -16,6 +16,7 @@
#include "Python.h"
#include "Imaging.h"
+#include "py3.h"
#include "math.h"
#include "float.h"
@@ -81,7 +82,7 @@ void name(Imaging out, Imaging im1, Imaging im2)\
/* --------------------------------------------------------------------
* some day, we should add FPE protection mechanisms. see pyfpe.h for
* details.
- *
+ *
* PyFPE_START_PROTECT("Error in foobar", return 0)
* PyFPE_END_PROTECT(result)
*/
@@ -173,15 +174,15 @@ _unop(PyObject* self, PyObject* args)
Imaging im1;
void (*unop)(Imaging, Imaging);
- long op, i0, i1;
- if (!PyArg_ParseTuple(args, "lll", &op, &i0, &i1))
+ Py_ssize_t op, i0, i1;
+ if (!PyArg_ParseTuple(args, "nnn", &op, &i0, &i1))
return NULL;
out = (Imaging) i0;
im1 = (Imaging) i1;
unop = (void*) op;
-
+
unop(out, im1);
Py_INCREF(Py_None);
@@ -196,8 +197,8 @@ _binop(PyObject* self, PyObject* args)
Imaging im2;
void (*binop)(Imaging, Imaging, Imaging);
- long op, i0, i1, i2;
- if (!PyArg_ParseTuple(args, "llll", &op, &i0, &i1, &i2))
+ Py_ssize_t op, i0, i1, i2;
+ if (!PyArg_ParseTuple(args, "nnnn", &op, &i0, &i1, &i2))
return NULL;
out = (Imaging) i0;
@@ -205,7 +206,7 @@ _binop(PyObject* self, PyObject* args)
im2 = (Imaging) i2;
binop = (void*) op;
-
+
binop(out, im1, im2);
Py_INCREF(Py_None);
@@ -221,20 +222,15 @@ static PyMethodDef _functions[] = {
static void
install(PyObject *d, char* name, void* value)
{
- PyObject *v = PyInt_FromLong((long) value);
+ PyObject *v = PyInt_FromSsize_t((Py_ssize_t) value);
if (!v || PyDict_SetItemString(d, name, v))
PyErr_Clear();
Py_XDECREF(v);
}
-DL_EXPORT(void)
-init_imagingmath(void)
-{
- PyObject* m;
- PyObject* d;
-
- m = Py_InitModule("_imagingmath", _functions);
- d = PyModule_GetDict(m);
+static int
+setup_module(PyObject* m) {
+ PyObject* d = PyModule_GetDict(m);
install(d, "abs_I", abs_I);
install(d, "neg_I", neg_I);
@@ -281,4 +277,35 @@ init_imagingmath(void)
install(d, "gt_F", gt_F);
install(d, "ge_F", ge_F);
+ return 0;
}
+
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__imagingmath(void) {
+ PyObject* m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imagingmath", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ _functions, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+
+ if (setup_module(m) < 0)
+ return NULL;
+
+ return m;
+}
+#else
+PyMODINIT_FUNC
+init_imagingmath(void)
+{
+ PyObject* m = Py_InitModule("_imagingmath", _functions);
+ setup_module(m);
+}
+#endif
+
diff --git a/_imagingtk.c b/_imagingtk.c
index 6165a2acb..c379f7dc5 100644
--- a/_imagingtk.c
+++ b/_imagingtk.c
@@ -30,14 +30,14 @@ typedef struct {
Tcl_Interp* interp;
} TkappObject;
-static PyObject*
+static PyObject*
_tkinit(PyObject* self, PyObject* args)
{
Tcl_Interp* interp;
- long arg;
+ Py_ssize_t arg;
int is_interp;
- if (!PyArg_ParseTuple(args, "li", &arg, &is_interp))
+ if (!PyArg_ParseTuple(args, "ni", &arg, &is_interp))
return NULL;
if (is_interp)
@@ -63,8 +63,24 @@ static PyMethodDef functions[] = {
{NULL, NULL} /* sentinel */
};
-DL_EXPORT(void)
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__imagingtk(void) {
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_imagingtk", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ functions, /* m_methods */
+ };
+
+ return PyModule_Create(&module_def);
+}
+#else
+PyMODINIT_FUNC
init_imagingtk(void)
{
Py_InitModule("_imagingtk", functions);
}
+#endif
+
diff --git a/_webp.c b/_webp.c
new file mode 100644
index 000000000..c201813d7
--- /dev/null
+++ b/_webp.c
@@ -0,0 +1,279 @@
+#define PY_SSIZE_T_CLEAN
+#include
+#include "py3.h"
+#include
+#include
+#include
+
+#ifdef HAVE_WEBPMUX
+#include
+#endif
+
+PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args)
+{
+ int width;
+ int height;
+ int lossless;
+ float quality_factor;
+ uint8_t *rgb;
+ uint8_t *icc_bytes;
+ uint8_t *exif_bytes;
+ uint8_t *output;
+ char *mode;
+ Py_ssize_t size;
+ Py_ssize_t icc_size;
+ Py_ssize_t exif_size;
+ size_t ret_size;
+
+ if (!PyArg_ParseTuple(args, "s#iiifss#s#",
+ (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode,
+ &icc_bytes, &icc_size, &exif_bytes, &exif_size)) {
+ Py_RETURN_NONE;
+ }
+ if (strcmp(mode, "RGBA")==0){
+ if (size < width * height * 4){
+ Py_RETURN_NONE;
+ }
+ #if WEBP_ENCODER_ABI_VERSION >= 0x0100
+ if (lossless) {
+ ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output);
+ } else
+ #endif
+ {
+ ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output);
+ }
+ } else if (strcmp(mode, "RGB")==0){
+ if (size < width * height * 3){
+ Py_RETURN_NONE;
+ }
+ #if WEBP_ENCODER_ABI_VERSION >= 0x0100
+ if (lossless) {
+ ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output);
+ } else
+ #endif
+ {
+ ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output);
+ }
+ } else {
+ Py_RETURN_NONE;
+ }
+
+#ifndef HAVE_WEBPMUX
+ if (ret_size > 0) {
+ PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size);
+ free(output);
+ return ret;
+ }
+#else
+ {
+ /* I want to truncate the *_size items that get passed into webp
+ data. Pypy2.1.0 had some issues where the Py_ssize_t items had
+ data in the upper byte. (Not sure why, it shouldn't have been there)
+ */
+ int i_icc_size = (int)icc_size;
+ int i_exif_size = (int)exif_size;
+ WebPData output_data = {0};
+ WebPData image = { output, ret_size };
+ WebPData icc_profile = { icc_bytes, i_icc_size };
+ WebPData exif = { exif_bytes, i_exif_size };
+ WebPMuxError err;
+ int dbg = 0;
+
+ int copy_data = 0; // value 1 indicates given data WILL be copied to the mux
+ // and value 0 indicates data will NOT be copied.
+
+ WebPMux* mux = WebPMuxNew();
+ WebPMuxSetImage(mux, &image, copy_data);
+
+ if (dbg) {
+ /* was getting %ld icc_size == 0, icc_size>0 was true */
+ fprintf(stderr, "icc size %d, %d \n", i_icc_size, i_icc_size > 0);
+ }
+
+ if (i_icc_size > 0) {
+ if (dbg) {
+ fprintf (stderr, "Adding ICC Profile\n");
+ }
+ err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
+ if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) {
+ fprintf(stderr, "Invalid ICC Argument\n");
+ } else if (dbg && err == WEBP_MUX_MEMORY_ERROR) {
+ fprintf(stderr, "ICC Memory Error\n");
+ }
+ }
+
+ if (dbg) {
+ fprintf(stderr, "exif size %d \n", i_exif_size);
+ }
+ if (i_exif_size > 0) {
+ if (dbg){
+ fprintf (stderr, "Adding Exif Data\n");
+ }
+ err = WebPMuxSetChunk(mux, "EXIF", &exif, copy_data);
+ if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) {
+ fprintf(stderr, "Invalid Exif Argument\n");
+ } else if (dbg && err == WEBP_MUX_MEMORY_ERROR) {
+ fprintf(stderr, "Exif Memory Error\n");
+ }
+ }
+
+ WebPMuxAssemble(mux, &output_data);
+ WebPMuxDelete(mux);
+ free(output);
+
+ ret_size = output_data.size;
+ if (ret_size > 0) {
+ PyObject *ret = PyBytes_FromStringAndSize((char*)output_data.bytes, ret_size);
+ WebPDataClear(&output_data);
+ return ret;
+ }
+ }
+#endif
+ Py_RETURN_NONE;
+}
+
+
+PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args)
+{
+ PyBytesObject *webp_string;
+ uint8_t *webp;
+ Py_ssize_t size;
+ PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None;
+ WebPDecoderConfig config;
+ VP8StatusCode vp8_status_code = VP8_STATUS_OK;
+ char* mode = "RGB";
+
+ if (!PyArg_ParseTuple(args, "S", &webp_string)) {
+ Py_RETURN_NONE;
+ }
+
+ if (!WebPInitDecoderConfig(&config)) {
+ Py_RETURN_NONE;
+ }
+
+ PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size);
+
+ vp8_status_code = WebPGetFeatures(webp, size, &config.input);
+ if (vp8_status_code == VP8_STATUS_OK) {
+ // If we don't set it, we don't get alpha.
+ // Initialized to MODE_RGB
+ if (config.input.has_alpha) {
+ config.output.colorspace = MODE_RGBA;
+ mode = "RGBA";
+ }
+
+#ifndef HAVE_WEBPMUX
+ vp8_status_code = WebPDecode(webp, size, &config);
+#else
+ {
+ int copy_data = 0;
+ WebPData data = { webp, size };
+ WebPMuxFrameInfo image;
+ WebPData icc_profile_data = {0};
+ WebPData exif_data = {0};
+
+ WebPMux* mux = WebPMuxCreate(&data, copy_data);
+ WebPMuxGetFrame(mux, 1, &image);
+ webp = (uint8_t*)image.bitstream.bytes;
+ size = image.bitstream.size;
+
+ vp8_status_code = WebPDecode(webp, size, &config);
+
+ WebPMuxGetChunk(mux, "ICCP", &icc_profile_data);
+ if (icc_profile_data.size > 0) {
+ icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size);
+ }
+
+ WebPMuxGetChunk(mux, "EXIF", &exif_data);
+ if (exif_data.size > 0) {
+ exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size);
+ }
+
+ WebPMuxDelete(mux);
+ }
+#endif
+ }
+
+ if (vp8_status_code != VP8_STATUS_OK) {
+ WebPFreeDecBuffer(&config.output);
+ Py_RETURN_NONE;
+ }
+
+ if (config.output.colorspace < MODE_YUV) {
+ bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba,
+ config.output.u.RGBA.size);
+ } else {
+ // Skipping YUV for now. Need Test Images.
+ // UNDONE -- unclear if we'll ever get here if we set mode_rgb*
+ bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y,
+ config.output.u.YUVA.y_size);
+ }
+
+#if PY_VERSION_HEX >= 0x03000000
+ pymode = PyUnicode_FromString(mode);
+#else
+ pymode = PyString_FromString(mode);
+#endif
+ ret = Py_BuildValue("SiiSSS", bytes, config.output.width,
+ config.output.height, pymode, icc_profile, exif);
+ WebPFreeDecBuffer(&config.output);
+ return ret;
+}
+
+// Return the decoder's version number, packed in hexadecimal using 8bits for
+// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
+ return Py_BuildValue("i", WebPGetDecoderVersion());
+}
+
+/*
+ * The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
+ * Files that are valid with 0.3 are reported as being invalid.
+ */
+PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
+ return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103);
+}
+
+static PyMethodDef webpMethods[] =
+{
+ {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"},
+ {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"},
+ {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"},
+ {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"},
+ {NULL, NULL}
+};
+
+void addMuxFlagToModule(PyObject* m) {
+#ifdef HAVE_WEBPMUX
+ PyModule_AddObject(m, "HAVE_WEBPMUX", Py_True);
+#else
+ PyModule_AddObject(m, "HAVE_WEBPMUX", Py_False);
+#endif
+}
+
+
+#if PY_VERSION_HEX >= 0x03000000
+PyMODINIT_FUNC
+PyInit__webp(void) {
+ PyObject* m;
+
+ static PyModuleDef module_def = {
+ PyModuleDef_HEAD_INIT,
+ "_webp", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ webpMethods, /* m_methods */
+ };
+
+ m = PyModule_Create(&module_def);
+ addMuxFlagToModule(m);
+ return m;
+}
+#else
+PyMODINIT_FUNC
+init_webp(void)
+{
+ PyObject* m = Py_InitModule("_webp", webpMethods);
+ addMuxFlagToModule(m);
+}
+#endif
diff --git a/decode.c b/decode.c
index 6ea8d9c3f..f3ac60e51 100644
--- a/decode.c
+++ b/decode.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library.
*
* standard decoder interfaces for the Imaging library
@@ -31,12 +31,8 @@
#include "Python.h"
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
#include "Imaging.h"
+#include "py3.h"
#include "Gif.h"
#include "Lzw.h"
@@ -45,19 +41,20 @@
/* -------------------------------------------------------------------- */
-/* Common */
+/* Common */
/* -------------------------------------------------------------------- */
typedef struct {
PyObject_HEAD
int (*decode)(Imaging im, ImagingCodecState state,
- UINT8* buffer, int bytes);
+ UINT8* buffer, int bytes);
+ int (*cleanup)(ImagingCodecState state);
struct ImagingCodecStateInstance state;
Imaging im;
PyObject* lock;
} ImagingDecoderObject;
-staticforward PyTypeObject ImagingDecoderType;
+static PyTypeObject ImagingDecoderType;
static ImagingDecoderObject*
PyImaging_DecoderNew(int contextsize)
@@ -65,25 +62,26 @@ PyImaging_DecoderNew(int contextsize)
ImagingDecoderObject *decoder;
void *context;
- ImagingDecoderType.ob_type = &PyType_Type;
+ if(PyType_Ready(&ImagingDecoderType) < 0)
+ return NULL;
decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType);
if (decoder == NULL)
- return NULL;
+ return NULL;
/* Clear the decoder state */
memset(&decoder->state, 0, sizeof(decoder->state));
/* Allocate decoder context */
if (contextsize > 0) {
- context = (void*) calloc(1, contextsize);
- if (!context) {
- Py_DECREF(decoder);
- (void) PyErr_NoMemory();
- return NULL;
- }
+ context = (void*) calloc(1, contextsize);
+ if (!context) {
+ Py_DECREF(decoder);
+ (void) PyErr_NoMemory();
+ return NULL;
+ }
} else
- context = 0;
+ context = 0;
/* Initialize decoder context */
decoder->state.context = context;
@@ -92,6 +90,9 @@ PyImaging_DecoderNew(int contextsize)
decoder->lock = NULL;
decoder->im = NULL;
+ /* Initialize the cleanup function pointer */
+ decoder->cleanup = NULL;
+
return decoder;
}
@@ -104,20 +105,34 @@ _dealloc(ImagingDecoderObject* decoder)
PyObject_Del(decoder);
}
-static PyObject*
+static PyObject*
_decode(ImagingDecoderObject* decoder, PyObject* args)
{
UINT8* buffer;
int bufsize, status;
- if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize))
- return NULL;
+ if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize))
+ return NULL;
status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize);
return Py_BuildValue("ii", status, decoder->state.errcode);
}
+static PyObject*
+_decode_cleanup(ImagingDecoderObject* decoder, PyObject* args)
+{
+ int status = 0;
+
+ if (decoder->cleanup){
+ status = decoder->cleanup(&decoder->state);
+ }
+
+ return Py_BuildValue("i", status);
+}
+
+
+
extern Imaging PyImaging_AsImaging(PyObject *op);
static PyObject*
@@ -132,10 +147,10 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
/* FIXME: should publish the ImagingType descriptor */
if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1))
- return NULL;
+ return NULL;
im = PyImaging_AsImaging(op);
if (!im)
- return NULL;
+ return NULL;
decoder->im = im;
@@ -143,30 +158,30 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
/* Setup decoding tile extent */
if (x0 == 0 && x1 == 0) {
- state->xsize = im->xsize;
- state->ysize = im->ysize;
+ state->xsize = im->xsize;
+ state->ysize = im->ysize;
} else {
- state->xoff = x0;
- state->yoff = y0;
- state->xsize = x1 - x0;
- state->ysize = y1 - y0;
+ state->xoff = x0;
+ state->yoff = y0;
+ state->xsize = x1 - x0;
+ state->ysize = y1 - y0;
}
if (state->xsize <= 0 ||
- state->xsize + state->xoff > (int) im->xsize ||
- state->ysize <= 0 ||
- state->ysize + state->yoff > (int) im->ysize) {
- PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image");
- return NULL;
+ state->xsize + state->xoff > (int) im->xsize ||
+ state->ysize <= 0 ||
+ state->ysize + state->yoff > (int) im->ysize) {
+ PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image");
+ return NULL;
}
/* Allocate memory buffer (if bits field is set) */
if (state->bits > 0) {
if (!state->bytes)
state->bytes = (state->bits * state->xsize+7)/8;
- state->buffer = (UINT8*) malloc(state->bytes);
- if (!state->buffer)
- return PyErr_NoMemory();
+ state->buffer = (UINT8*) malloc(state->bytes);
+ if (!state->buffer)
+ return PyErr_NoMemory();
}
/* Keep a reference to the image object, to make sure it doesn't
@@ -181,30 +196,43 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args)
static struct PyMethodDef methods[] = {
{"decode", (PyCFunction)_decode, 1},
+ {"cleanup", (PyCFunction)_decode_cleanup, 1},
{"setimage", (PyCFunction)_setimage, 1},
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_getattr(ImagingDecoderObject* self, char* name)
-{
- return Py_FindMethod(methods, (PyObject*) self, name);
-}
-
-statichere PyTypeObject ImagingDecoderType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "ImagingDecoder", /*tp_name*/
- sizeof(ImagingDecoderObject), /*tp_size*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- (getattrfunc)_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_hash*/
+static PyTypeObject ImagingDecoderType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "ImagingDecoder", /*tp_name*/
+ sizeof(ImagingDecoderObject), /*tp_size*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
/* -------------------------------------------------------------------- */
@@ -218,9 +246,9 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode,
unpack = ImagingFindUnpacker(mode, rawmode, &bits);
if (!unpack) {
- Py_DECREF(decoder);
- PyErr_SetString(PyExc_ValueError, "unknown raw mode");
- return -1;
+ Py_DECREF(decoder);
+ PyErr_SetString(PyExc_ValueError, "unknown raw mode");
+ return -1;
}
decoder->state.shuffle = unpack;
@@ -231,7 +259,7 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode,
/* -------------------------------------------------------------------- */
-/* BIT (packed fields) */
+/* BIT (packed fields) */
/* -------------------------------------------------------------------- */
PyObject*
@@ -247,16 +275,16 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args)
int ystep = 1;
if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill,
&sign, &ystep))
- return NULL;
+ return NULL;
if (strcmp(mode, "F") != 0) {
- PyErr_SetString(PyExc_ValueError, "bad image mode");
+ PyErr_SetString(PyExc_ValueError, "bad image mode");
return NULL;
}
decoder = PyImaging_DecoderNew(sizeof(BITSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
decoder->decode = ImagingBitDecode;
@@ -272,7 +300,7 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* FLI */
+/* FLI */
/* -------------------------------------------------------------------- */
PyObject*
@@ -282,7 +310,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args)
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
decoder->decode = ImagingFliDecode;
@@ -291,7 +319,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* GIF */
+/* GIF */
/* -------------------------------------------------------------------- */
PyObject*
@@ -303,16 +331,16 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args)
int bits = 8;
int interlace = 0;
if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace))
- return NULL;
+ return NULL;
if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) {
- PyErr_SetString(PyExc_ValueError, "bad image mode");
+ PyErr_SetString(PyExc_ValueError, "bad image mode");
return NULL;
}
decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
decoder->decode = ImagingGifDecode;
@@ -324,7 +352,7 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* HEX */
+/* HEX */
/* -------------------------------------------------------------------- */
PyObject*
@@ -335,14 +363,14 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingHexDecode;
@@ -351,7 +379,7 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* LZW */
+/* LZW */
/* -------------------------------------------------------------------- */
PyObject*
@@ -363,14 +391,14 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args)
char* rawmode;
int filter = 0;
if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &filter))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(sizeof(LZWSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingLzwDecode;
@@ -379,9 +407,55 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args)
return (PyObject*) decoder;
}
+/* -------------------------------------------------------------------- */
+/* LibTiff */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBTIFF
+
+#include "TiffDecode.h"
+
+#include
+#ifdef __WIN32__
+#define strcasecmp(s1, s2) stricmp(s1, s2)
+#endif
+
+PyObject*
+PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+ char* mode;
+ char* rawmode;
+ char* compname;
+ int fp;
+
+ if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp))
+ return NULL;
+
+ TRACE(("new tiff decoder %s\n", compname));
+
+ decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ if (! ImagingLibTiffInit(&decoder->state, fp)) {
+ Py_DECREF(decoder);
+ PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
+ return NULL;
+ }
+
+ decoder->decode = ImagingLibTiffDecode;
+
+ return (PyObject*) decoder;
+}
+
+#endif
/* -------------------------------------------------------------------- */
-/* MSP */
+/* MSP */
/* -------------------------------------------------------------------- */
PyObject*
@@ -391,10 +465,10 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args)
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, "1", "1") < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingMspDecode;
@@ -403,7 +477,7 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* PackBits */
+/* PackBits */
/* -------------------------------------------------------------------- */
PyObject*
@@ -414,14 +488,14 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingPackbitsDecode;
@@ -430,7 +504,7 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* PCD */
+/* PCD */
/* -------------------------------------------------------------------- */
PyObject*
@@ -440,11 +514,11 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args)
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
/* Unpack from PhotoYCC to RGB */
if (get_unpacker(decoder, "RGB", "YCC;P") < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingPcdDecode;
@@ -453,7 +527,7 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* PCX */
+/* PCX */
/* -------------------------------------------------------------------- */
PyObject*
@@ -465,14 +539,14 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args)
char* rawmode;
int stride;
if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->state.bytes = stride;
@@ -483,7 +557,7 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* RAW */
+/* RAW */
/* -------------------------------------------------------------------- */
PyObject*
@@ -496,14 +570,14 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args)
int stride = 0;
int ystep = 1;
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(sizeof(RAWSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingRawDecode;
@@ -516,7 +590,7 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* SUN RLE */
+/* SUN RLE */
/* -------------------------------------------------------------------- */
PyObject*
@@ -527,14 +601,14 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingSunRleDecode;
@@ -543,7 +617,7 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* TGA RLE */
+/* TGA RLE */
/* -------------------------------------------------------------------- */
PyObject*
@@ -556,14 +630,14 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args)
int ystep = 1;
int depth = 8;
if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingTgaRleDecode;
@@ -575,7 +649,7 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* XBM */
+/* XBM */
/* -------------------------------------------------------------------- */
PyObject*
@@ -585,10 +659,10 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args)
decoder = PyImaging_DecoderNew(0);
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, "1", "1;R") < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingXbmDecode;
@@ -597,7 +671,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* ZIP */
+/* ZIP */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBZ
@@ -613,14 +687,14 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args)
char* rawmode;
int interlaced = 0;
if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced))
- return NULL;
+ return NULL;
decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingZipDecode;
@@ -632,7 +706,7 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args)
/* -------------------------------------------------------------------- */
-/* JPEG */
+/* JPEG */
/* -------------------------------------------------------------------- */
#ifdef HAVE_LIBJPEG
@@ -640,15 +714,15 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args)
/* We better define this decoder last in this file, so the following
undef's won't mess things up for the Imaging library proper. */
-#undef HAVE_PROTOTYPES
-#undef HAVE_STDDEF_H
-#undef HAVE_STDLIB_H
-#undef UINT8
-#undef UINT16
-#undef UINT32
-#undef INT8
-#undef INT16
-#undef INT32
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDDEF_H
+#undef HAVE_STDLIB_H
+#undef UINT8
+#undef UINT16
+#undef UINT32
+#undef INT8
+#undef INT16
+#undef INT32
#include "Jpeg.h"
@@ -664,19 +738,20 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
int draft = 0;
if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode,
&scale, &draft))
- return NULL;
+ return NULL;
if (!jpegmode)
- jpegmode = "";
+ jpegmode = "";
decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE));
if (decoder == NULL)
- return NULL;
+ return NULL;
if (get_unpacker(decoder, mode, rawmode) < 0)
- return NULL;
+ return NULL;
decoder->decode = ImagingJpegDecode;
+ decoder->cleanup = ImagingJpegDecodeCleanup;
strncpy(((JPEGSTATE*)decoder->state.context)->rawmode, rawmode, 8);
strncpy(((JPEGSTATE*)decoder->state.context)->jpegmode, jpegmode, 8);
diff --git a/display.c b/display.c
index 1a08fadd5..9cc1ae3ad 100644
--- a/display.c
+++ b/display.c
@@ -25,12 +25,8 @@
#include "Python.h"
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
#include "Imaging.h"
+#include "py3.h"
/* -------------------------------------------------------------------- */
/* Windows DIB support */
@@ -44,13 +40,16 @@ typedef struct {
ImagingDIB dib;
} ImagingDisplayObject;
-staticforward PyTypeObject ImagingDisplayType;
+static PyTypeObject ImagingDisplayType;
static ImagingDisplayObject*
_new(const char* mode, int xsize, int ysize)
{
ImagingDisplayObject *display;
+ if (PyType_Ready(&ImagingDisplayType) < 0)
+ return NULL;
+
display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType);
if (display == NULL)
return NULL;
@@ -72,7 +71,7 @@ _delete(ImagingDisplayObject* display)
PyObject_Del(display);
}
-static PyObject*
+static PyObject*
_expose(ImagingDisplayObject* display, PyObject* args)
{
int hdc;
@@ -85,7 +84,7 @@ _expose(ImagingDisplayObject* display, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_draw(ImagingDisplayObject* display, PyObject* args)
{
int hdc;
@@ -129,7 +128,7 @@ _paste(ImagingDisplayObject* display, PyObject* args)
return Py_None;
}
-static PyObject*
+static PyObject*
_query_palette(ImagingDisplayObject* display, PyObject* args)
{
int hdc;
@@ -176,12 +175,18 @@ _releasedc(ImagingDisplayObject* display, PyObject* args)
}
static PyObject*
-_fromstring(ImagingDisplayObject* display, PyObject* args)
+_frombytes(ImagingDisplayObject* display, PyObject* args)
{
char* ptr;
int bytes;
+
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes))
+ return NULL;
+#else
if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes))
- return NULL;
+ return NULL;
+#endif
if (display->dib->ysize * display->dib->linesize != bytes) {
PyErr_SetString(PyExc_ValueError, "wrong size");
@@ -195,12 +200,17 @@ _fromstring(ImagingDisplayObject* display, PyObject* args)
}
static PyObject*
-_tostring(ImagingDisplayObject* display, PyObject* args)
+_tobytes(ImagingDisplayObject* display, PyObject* args)
{
+#if PY_VERSION_HEX >= 0x03000000
+ if (!PyArg_ParseTuple(args, ":tobytes"))
+ return NULL;
+#else
if (!PyArg_ParseTuple(args, ":tostring"))
- return NULL;
+ return NULL;
+#endif
- return PyString_FromStringAndSize(
+ return PyBytes_FromStringAndSize(
display->dib->bits, display->dib->ysize * display->dib->linesize
);
}
@@ -212,42 +222,63 @@ static struct PyMethodDef methods[] = {
{"query_palette", (PyCFunction)_query_palette, 1},
{"getdc", (PyCFunction)_getdc, 1},
{"releasedc", (PyCFunction)_releasedc, 1},
- {"fromstring", (PyCFunction)_fromstring, 1},
- {"tostring", (PyCFunction)_tostring, 1},
+ {"frombytes", (PyCFunction)_frombytes, 1},
+ {"tobytes", (PyCFunction)_tobytes, 1},
+ {"fromstring", (PyCFunction)_frombytes, 1},
+ {"tostring", (PyCFunction)_tobytes, 1},
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_getattr(ImagingDisplayObject* self, char* name)
+static PyObject*
+_getattr_mode(ImagingDisplayObject* self, void* closure)
{
- PyObject* res;
-
- res = Py_FindMethod(methods, (PyObject*) self, name);
- if (res)
- return res;
- PyErr_Clear();
- if (!strcmp(name, "mode"))
return Py_BuildValue("s", self->dib->mode);
- if (!strcmp(name, "size"))
- return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize);
- PyErr_SetString(PyExc_AttributeError, name);
- return NULL;
}
-statichere PyTypeObject ImagingDisplayType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
+static PyObject*
+_getattr_size(ImagingDisplayObject* self, void* closure)
+{
+ return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize);
+}
+
+static struct PyGetSetDef getsetters[] = {
+ { "mode", (getter) _getattr_mode },
+ { "size", (getter) _getattr_size },
+ { NULL }
+};
+
+static PyTypeObject ImagingDisplayType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
"ImagingDisplay", /*tp_name*/
sizeof(ImagingDisplayObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)_delete, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_hash*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ getsetters, /*tp_getset*/
};
PyObject*
@@ -289,20 +320,20 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
BITMAPCOREHEADER core;
HDC screen, screen_copy;
PyObject* buffer;
-
+
/* step 1: create a memory DC large enough to hold the
entire screen */
- screen = CreateDC("DISPLAY", NULL, NULL, NULL);
- screen_copy = CreateCompatibleDC(screen);
+ screen = CreateDC("DISPLAY", NULL, NULL, NULL);
+ screen_copy = CreateCompatibleDC(screen);
width = GetDeviceCaps(screen, HORZRES);
height = GetDeviceCaps(screen, VERTRES);
-
+
bitmap = CreateCompatibleBitmap(screen, width, height);
if (!bitmap)
goto error;
-
+
if (!SelectObject(screen_copy, bitmap))
goto error;
@@ -313,7 +344,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
/* step 3: extract bits from bitmap */
- buffer = PyString_FromStringAndSize(NULL, height * ((width*3 + 3) & -4));
+ buffer = PyBytes_FromStringAndSize(NULL, height * ((width*3 + 3) & -4));
if (!buffer)
return NULL;
@@ -322,7 +353,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
core.bcHeight = height;
core.bcPlanes = 1;
core.bcBitCount = 24;
- if (!GetDIBits(screen_copy, bitmap, 0, height, PyString_AS_STRING(buffer),
+ if (!GetDIBits(screen_copy, bitmap, 0, height, PyBytes_AS_STRING(buffer),
(BITMAPINFO*) &core, DIB_RGB_COLORS))
goto error;
@@ -349,15 +380,15 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam)
RECT inner, outer;
int title_size;
int status;
-
+
/* get window title */
title_size = GetWindowTextLength(hwnd);
if (title_size > 0) {
- title = PyString_FromStringAndSize(NULL, title_size);
+ title = PyUnicode_FromStringAndSize(NULL, title_size);
if (title)
- GetWindowText(hwnd, PyString_AS_STRING(title), title_size+1);
+ GetWindowText(hwnd, PyUnicode_AS_UNICODE(title), title_size+1);
} else
- title = PyString_FromString("");
+ title = PyUnicode_FromString("");
if (!title)
return 0;
@@ -366,7 +397,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam)
GetWindowRect(hwnd, &outer);
item = Py_BuildValue(
- "lN(iiii)(iiii)", (long) hwnd, title,
+ "nN(iiii)(iiii)", (Py_ssize_t) hwnd, title,
inner.left, inner.top, inner.right, inner.bottom,
outer.left, outer.top, outer.right, outer.bottom
);
@@ -379,7 +410,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam)
if (status < 0)
return 0;
-
+
return 1;
}
@@ -387,7 +418,7 @@ PyObject*
PyImaging_ListWindowsWin32(PyObject* self, PyObject* args)
{
PyObject* window_list;
-
+
window_list = PyList_New(0);
if (!window_list)
return NULL;
@@ -413,7 +444,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
int size;
void* data;
PyObject* result;
-
+
int verbose = 0; /* debugging; will be removed in future versions */
if (!PyArg_ParseTuple(args, "|i", &verbose))
return NULL;
@@ -421,7 +452,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
clip = OpenClipboard(NULL);
/* FIXME: check error status */
-
+
if (verbose) {
UINT format = EnumClipboardFormats(0);
char buffer[200];
@@ -512,7 +543,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
size = wcslen(data) * 2;
#endif
- result = PyString_FromStringAndSize(data, size);
+ result = PyBytes_FromStringAndSize(data, size);
GlobalUnlock(handle);
@@ -534,7 +565,7 @@ static void
callback_error(const char* handler)
{
PyObject* sys_stderr;
-
+
sys_stderr = PySys_GetObject("stderr");
if (sys_stderr) {
@@ -691,7 +722,7 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args)
wnd = CreateWindowEx(
0, windowClass.lpszClassName, title,
WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, CW_USEDEFAULT, width, height,
+ CW_USEDEFAULT, CW_USEDEFAULT, width, height,
HWND_DESKTOP, NULL, NULL, NULL
);
@@ -702,15 +733,15 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args)
/* register window callback */
Py_INCREF(callback);
- SetWindowLong(wnd, 0, (LONG) callback);
- SetWindowLong(wnd, sizeof(callback), (LONG) PyThreadState_Get());
+ SetWindowLongPtr(wnd, 0, (LONG_PTR) callback);
+ SetWindowLongPtr(wnd, sizeof(callback), (LONG_PTR) PyThreadState_Get());
Py_BEGIN_ALLOW_THREADS
ShowWindow(wnd, SW_SHOWNORMAL);
SetForegroundWindow(wnd); /* to make sure it's visible */
Py_END_ALLOW_THREADS
- return Py_BuildValue("l", (long) wnd);
+ return Py_BuildValue("n", (Py_ssize_t) wnd);
}
PyObject*
@@ -749,7 +780,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args)
int datasize;
int width, height;
int x0, y0, x1, y1;
- if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize,
+ if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"(ii)(iiii):_load", &data, &datasize,
&width, &height, &x0, &x1, &y0, &y1))
return NULL;
@@ -820,7 +851,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args)
GdiFlush();
- buffer = PyString_FromStringAndSize(ptr, height * ((width*3 + 3) & -4));
+ buffer = PyBytes_FromStringAndSize(ptr, height * ((width*3 + 3) & -4));
error:
DeleteEnhMetaFile(meta);
diff --git a/BUILDME b/docs/BUILDME
similarity index 100%
rename from BUILDME
rename to docs/BUILDME
diff --git a/docs/COPYING b/docs/COPYING
new file mode 100644
index 000000000..fbdf1a8a5
--- /dev/null
+++ b/docs/COPYING
@@ -0,0 +1,25 @@
+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.
\ No newline at end of file
diff --git a/docs/Guardfile b/docs/Guardfile
new file mode 100644
index 000000000..f8f3051ed
--- /dev/null
+++ b/docs/Guardfile
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+from livereload.task import Task
+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/LICENSE b/docs/LICENSE
new file mode 100644
index 000000000..c0f6cde5b
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,10 @@
+The Python Imaging Library (PIL) is
+
+ Copyright © 1997-2011 by Secret Labs AB
+ Copyright © 1995-2011 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.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 000000000..f28f802a1
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,156 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PillowPILfork.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PillowPILfork.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/PillowPILfork"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PillowPILfork"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+livehtml: html
+ livereload $(BUILDDIR)/html -p 33233
diff --git a/docs/PIL.rst b/docs/PIL.rst
new file mode 100644
index 000000000..6726f661f
--- /dev/null
+++ b/docs/PIL.rst
@@ -0,0 +1,168 @@
+PIL Package (autodoc of remaining modules)
+==========================================
+
+Reference for modules whose documentation has not yet been ported or written
+can be found here.
+
+:mod:`BdfFontFile` Module
+-------------------------
+
+.. automodule:: PIL.BdfFontFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ContainerIO` Module
+-------------------------
+
+.. automodule:: PIL.ContainerIO
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ExifTags` Module
+----------------------
+
+.. automodule:: PIL.ExifTags
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`FontFile` Module
+----------------------
+
+.. automodule:: PIL.FontFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GdImageFile` Module
+-------------------------
+
+.. automodule:: PIL.GdImageFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GimpGradientFile` Module
+------------------------------
+
+.. automodule:: PIL.GimpGradientFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GimpPaletteFile` Module
+-----------------------------
+
+.. automodule:: PIL.GimpPaletteFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ImageCms` Module
+----------------------
+
+.. automodule:: PIL.ImageCms
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+.. intentionally skipped documenting this because it's not documented anywhere
+:mod:`ImageDraw2` Module
+------------------------
+
+.. automodule:: PIL.ImageDraw2
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+.. intentionally skipped documenting this because it's deprecated
+:mod:`ImageFileIO` Module
+-------------------------
+
+.. automodule:: PIL.ImageFileIO
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ImageShow` Module
+-----------------------
+
+.. automodule:: PIL.ImageShow
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ImageTransform` Module
+----------------------------
+
+.. automodule:: PIL.ImageTransform
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`JpegPresets` Module
+-------------------------
+
+.. automodule:: PIL.JpegPresets
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`OleFileIO` Module
+-----------------------
+
+.. automodule:: PIL.OleFileIO
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PaletteFile` Module
+-------------------------
+
+.. automodule:: PIL.PaletteFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PcfFontFile` Module
+-------------------------
+
+.. automodule:: PIL.PcfFontFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`TarIO` Module
+-------------------
+
+.. automodule:: PIL.TarIO
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`TiffTags` Module
+----------------------
+
+.. automodule:: PIL.TiffTags
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`WalImageFile` Module
+--------------------------
+
+.. automodule:: PIL.WalImageFile
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`_binary` Module
+---------------------
+
+.. automodule:: PIL._binary
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/_build/.gitignore b/docs/_build/.gitignore
new file mode 100644
index 000000000..b1f9a2ade
--- /dev/null
+++ b/docs/_build/.gitignore
@@ -0,0 +1 @@
+# Empty file, to make the directory available in the repository
diff --git a/docs/_static/.gitignore b/docs/_static/.gitignore
new file mode 100644
index 000000000..b1f9a2ade
--- /dev/null
+++ b/docs/_static/.gitignore
@@ -0,0 +1 @@
+# Empty file, to make the directory available in the repository
diff --git a/docs/_templates/.gitignore b/docs/_templates/.gitignore
new file mode 100644
index 000000000..b1f9a2ade
--- /dev/null
+++ b/docs/_templates/.gitignore
@@ -0,0 +1 @@
+# Empty file, to make the directory available in the repository
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
new file mode 100644
index 000000000..987b6dbb3
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('../'))
+import PIL
+
+### general configuration ###
+
+needs_sphinx = '1.0'
+
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode',
+ 'sphinx.ext.intersphinx']
+intersphinx_mapping = {'http://docs.python.org/2/': None}
+
+source_suffix = '.rst'
+templates_path = ['_templates']
+#source_encoding = 'utf-8-sig'
+master_doc = 'index'
+
+project = u'Pillow (PIL fork)'
+copyright = (u'1997-2011 by Secret Labs AB,'
+ u' 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark')
+
+# The short X.Y version.
+version = PIL.PILLOW_VERSION
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# currently excluding autodoc'd plugs
+exclude_patterns = ['_build', 'plugins.rst']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+### HTML output ###
+
+from better import better_theme_path
+html_theme_path = [better_theme_path]
+html_theme = 'better'
+
+html_title = "Pillow v{release} (PIL fork)".format(release=release)
+html_short_title = "Home"
+html_static_path = ['_static']
+
+html_theme_options = {}
+
+html_sidebars = {
+ '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html',
+ 'searchbox.html'],
+ 'index': ['globaltoc.html', 'sidebarhelp.html', 'searchbox.html'],
+}
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Pillowdoc'
+
+
+### LaTeX output (RtD PDF output as well) ###
+
+latex_elements = {}
+
+latex_documents = [
+ ('index', 'Pillow.tex', u'Pillow (PIL fork) Documentation', u'Author',
+ 'manual'),
+]
+
+
+# skip_api_docs setting will skip PIL.rst if True. Used for working on the
+# guides; makes livereload basically instantaneous.
+def setup(app):
+ app.add_config_value('skip_api_docs', False, True)
+
+skip_api_docs = False
+
+if skip_api_docs:
+ exclude_patterns += ['PIL.rst']
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/appendices.rst b/docs/handbook/appendices.rst
new file mode 100644
index 000000000..f5482ffcd
--- /dev/null
+++ b/docs/handbook/appendices.rst
@@ -0,0 +1,8 @@
+Appendices
+==========
+
+.. toctree::
+ :maxdepth: 2
+
+ image-file-formats
+ writing-your-own-file-decoder
diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst
new file mode 100644
index 000000000..93f964e41
--- /dev/null
+++ b/docs/handbook/concepts.rst
@@ -0,0 +1,109 @@
+Concepts
+========
+
+The Python Imaging Library handles *raster images*; that is, rectangles of
+pixel data.
+
+Bands
+-----
+
+An image can consist of one or more bands of data. The Python Imaging Library
+allows you to store several bands in a single image, provided they all have the
+same dimensions and depth.
+
+To get the number and names of bands in an image, use the
+:py:meth:`~PIL.Image.Image.getbands` method.
+
+Mode
+----
+
+The :term:`mode` of an image defines the type and depth of a pixel in the
+image. The current release supports the following standard modes:
+
+ * ``1`` (1-bit pixels, black and white, stored with one pixel per byte)
+ * ``L`` (8-bit pixels, black and white)
+ * ``P`` (8-bit pixels, mapped to any other mode using a color palette)
+ * ``RGB`` (3x8-bit pixels, true color)
+ * ``RGBA`` (4x8-bit pixels, true color with transparency mask)
+ * ``CMYK`` (4x8-bit pixels, color separation)
+ * ``YCbCr`` (3x8-bit pixels, color video format)
+ * ``I`` (32-bit signed integer pixels)
+ * ``F`` (32-bit floating point pixels)
+
+PIL also provides limited support for a few special modes, including ``LA`` (L
+with alpha), ``RGBX`` (true color with padding) and ``RGBa`` (true color with
+premultiplied alpha). However, PIL doesn’t support user-defined modes; if you
+to handle band combinations that are not listed above, use a sequence of Image
+objects.
+
+You can read the mode of an image through the :py:attr:`~PIL.Image.Image.mode`
+attribute. This is a string containing one of the above values.
+
+Size
+----
+
+You can read the image size through the :py:attr:`~PIL.Image.Image.size`
+attribute. This is a 2-tuple, containing the horizontal and vertical size in
+pixels.
+
+Coordinate System
+-----------------
+
+The Python Imaging Library uses a Cartesian pixel coordinate system, with (0,0)
+in the upper left corner. Note that the coordinates refer to the implied pixel
+corners; the centre of a pixel addressed as (0, 0) actually lies at (0.5, 0.5).
+
+Coordinates are usually passed to the library as 2-tuples (x, y). Rectangles
+are represented as 4-tuples, with the upper left corner given first. For
+example, a rectangle covering all of an 800x600 pixel image is written as (0,
+0, 800, 600).
+
+Palette
+-------
+
+The palette mode (``P``) uses a color palette to define the actual color for
+each pixel.
+
+Info
+----
+
+You can attach auxiliary information to an image using the
+:py:attr:`~PIL.Image.Image.info` attribute. This is a dictionary object.
+
+How such information is handled when loading and saving image files is up to
+the file format handler (see the chapter on :ref:`image-file-formats`). Most
+handlers add properties to the :py:attr:`~PIL.Image.Image.info` attribute when
+loading an image, but ignore it when saving images.
+
+Filters
+-------
+
+For geometry operations that may map multiple input pixels to a single output
+pixel, the Python Imaging Library provides four different resampling *filters*.
+
+``NEAREST``
+ Pick the nearest pixel from the input image. Ignore all other input pixels.
+
+``BILINEAR``
+ Use linear interpolation over a 2x2 environment in the input image. Note
+ that in the current version of PIL, this filter uses a fixed input
+ environment when downsampling.
+
+``BICUBIC``
+ Use cubic interpolation over a 4x4 environment in the input image. Note
+ that in the current version of PIL, this filter uses a fixed input
+ environment when downsampling.
+
+``ANTIALIAS``
+ Calculate the output pixel value using a high-quality resampling filter (a
+ truncated sinc) on all pixels that may contribute to the output value. In
+ the current version of PIL, this filter can only be used with the resize
+ and thumbnail methods.
+
+ .. versionadded:: 1.1.3
+
+Note that in the current version of PIL, the ``ANTIALIAS`` filter is the only
+filter that behaves properly when downsampling (that is, when converting a
+large image to a small one). The ``BILINEAR`` and ``BICUBIC`` filters use a
+fixed input environment, and are best used for scale-preserving geometric
+transforms and upsamping.
diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst
new file mode 100644
index 000000000..21cd615fc
--- /dev/null
+++ b/docs/handbook/image-file-formats.rst
@@ -0,0 +1,617 @@
+.. _image-file-formats:
+
+Image file formats
+==================
+
+The Python Imaging Library supports a wide variety of raster file formats.
+Nearly 30 different file formats can be identified and read by the library.
+Write support is less extensive, but most common interchange and presentation
+formats are supported.
+
+The :py:meth:`~PIL.Image.Image.open` function identifies files from their
+contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method
+looks at the name to determine which format to use, unless the format is given
+explicitly.
+
+Fully supported formats
+-----------------------
+
+BMP
+^^^
+
+PIL reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``,
+or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding
+is not supported.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**compression**
+ Set to ``bmp_rle`` if the file is run-length encoded.
+
+EPS
+^^^
+
+PIL identifies EPS files containing image data, and can read files that contain
+embedded raster images (ImageData descriptors). If Ghostscript is available,
+other EPS files can be read as well. The EPS driver can also write EPS images.
+
+If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load`
+method with the following parameter to affect how Ghostscript renders the EPS
+
+**scale**
+ Affects the scale of the resultant rasterized image. If the EPS suggests
+ that the image be rendered at 100px x 100px, setting this parameter to
+ 2 will make the Ghostscript render a 200px x 200px image instead. The
+ relative position of the bounding box is maintained::
+
+ im = Image.open(...)
+ im.size #(100,100)
+ im.load(scale=2)
+ im.size #(200,200)
+
+GIF
+^^^
+
+PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes
+run-length encoded GIF87a files. Note that GIF files are always read as
+grayscale (``L``) or palette mode (``P``) images.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**background**
+ Default background color (a palette color index).
+
+**duration**
+ Time between frames in an animation (in milliseconds).
+
+**transparency**
+ Transparency color index. This key is omitted if the image is not
+ transparent.
+
+**version**
+ Version (either ``GIF87a`` or ``GIF89a``).
+
+Reading sequences
+~~~~~~~~~~~~~~~~~
+
+The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell`
+methods. You can seek to the next frame (``im.seek(im.tell() + 1``), or rewind
+the file by seeking to the first frame. Random access is not supported.
+
+Reading local images
+~~~~~~~~~~~~~~~~~~~~
+
+The GIF loader creates an image memory the same size as the GIF file’s *logical
+screen size*, and pastes the actual pixel data (the *local image*) into this
+image. If you only want the actual pixel rectangle, you can manipulate the
+:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.tile`
+attributes before loading the file::
+
+ im = Image.open(...)
+
+ if im.tile[0][0] == "gif":
+ # only read the first "local image" from this GIF file
+ tag, (x0, y0, x1, y1), offset, extra = im.tile[0]
+ im.size = (x1 - x0, y1 - y0)
+ im.tile = [(tag, (0, 0) + im.size, offset, extra)]
+
+IM
+^^
+
+IM is a format used by LabEye and other applications based on the IFUNC image
+processing library. The library reads and writes most uncompressed interchange
+versions of this format.
+
+IM is the only format that can store all internal PIL formats.
+
+JPEG
+^^^^
+
+PIL reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or
+``CMYK`` data. It writes standard and progressive JFIF files.
+
+Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by
+converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of
+their original size while loading them. The :py:meth:`~PIL.Image.Image.draft`
+method also configures the JPEG decoder to trade some quality for speed.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**jfif**
+ JFIF application marker found. If the file is not a JFIF file, this key is
+ not present.
+
+**adobe**
+ Adobe application marker found. If the file is not an Adobe JPEG file, this
+ key is not present.
+
+**progression**
+ Indicates that this is a progressive JPEG file.
+
+The :py:meth:`~PIL.Image.Image.save` method supports the following options:
+
+**quality**
+ The image quality, on a scale from 1 (worst) to 95 (best). The default is
+ 75. Values above 95 should be avoided; 100 disables portions of the JPEG
+ compression algorithm, and results in large files with hardly any gain in =
+ image quality.
+
+**optimize**
+ If present, indicates that the encoder should make an extra pass over the
+ image in order to select optimal encoder settings.
+
+**progressive**
+ If present, indicates that this image should be stored as a progressive
+ JPEG file.
+
+.. note::
+
+ To enable JPEG support, you need to build and install the IJG JPEG library
+ before building the Python Imaging Library. See the distribution README for
+ details.
+
+MSP
+^^^
+
+PIL identifies and reads MSP files from Windows 1 and 2. The library writes
+uncompressed (Windows 1) versions of this format.
+
+PCX
+^^^
+
+PIL reads and writes PCX files containing ``1``, ``L``, ``P``, or ``RGB`` data.
+
+PNG
+^^^
+
+PIL identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``,
+``RGB``, or ``RGBA`` data. Interlaced files are supported as of v1.1.7.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties, when appropriate:
+
+**gamma**
+ Gamma, given as a floating point number.
+
+**transparency**
+ Transparency color index. This key is omitted if the image is not a
+ transparent palette image.
+
+The :py:meth:`~PIL.Image.Image.save` method supports the following options:
+
+**optimize**
+ If present, instructs the PNG writer to make the output file as small as
+ possible. This includes extra processing in order to find optimal encoder
+ settings.
+
+**transparency**
+ For ``P``, ``L``, and ``RGB`` images, this option controls what
+ color image to mark as transparent.
+
+**bits (experimental)**
+ For ``P`` images, this option controls how many bits to store. If omitted,
+ the PNG writer uses 8 bits (256 colors).
+
+**dictionary (experimental)**
+ Set the ZLIB encoder dictionary.
+
+.. note::
+
+ To enable PNG support, you need to build and install the ZLIB compression
+ library before building the Python Imaging Library. See the distribution
+ README for details.
+
+PPM
+^^^
+
+PIL reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or ``RGB``
+data.
+
+SPIDER
+^^^^^^
+
+PIL reads and writes SPIDER image files of 32-bit floating point data
+("F;32F").
+
+PIL also reads SPIDER stack files containing sequences of SPIDER images. The
+:py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and
+random access is allowed.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following attributes:
+
+**format**
+ Set to ``SPIDER``
+
+**istack**
+ Set to 1 if the file is an image stack, else 0.
+
+**nimages**
+ Set to the number of images in the stack.
+
+A convenience method, :py:meth:`~PIL.Image.Image.convert2byte`, is provided for
+converting floating point data to byte data (mode ``L``)::
+
+ im = Image.open('image001.spi').convert2byte()
+
+Writing files in SPIDER format
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The extension of SPIDER files may be any 3 alphanumeric characters. Therefore
+the output format must be specified explicitly::
+
+ im.save('newimage.spi', format='SPIDER')
+
+For more information about the SPIDER image processing package, see the
+`SPIDER home page`_ at `Wadsworth Center`_.
+
+.. _SPIDER home page: http://www.wadsworth.org/spider_doc/spider/docs/master.html
+.. _Wadsworth Center: http://www.wadsworth.org/
+
+TIFF
+^^^^
+
+PIL reads and writes TIFF files. It can read both striped and tiled images,
+pixel and plane interleaved multi-band images, and either uncompressed, or
+Packbits, LZW, or JPEG compressed images.
+
+If you have libtiff and its headers installed, PIL can read and write many more
+kinds of compressed TIFF files. If not, PIL will always write uncompressed
+files.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**compression**
+ Compression mode.
+
+**dpi**
+ Image resolution as an (xdpi, ydpi) tuple, where applicable. You can use
+ the :py:attr:`~PIL.Image.Image.tag` attribute to get more detailed
+ information about the image resolution.
+
+ .. versionadded:: 1.1.5
+
+In addition, the :py:attr:`~PIL.Image.Image.tag` attribute contains a
+dictionary of decoded TIFF fields. Values are stored as either strings or
+tuples. Note that only short, long and ASCII tags are correctly unpacked by
+this release.
+
+Saving Tiff Images
+~~~~~~~~~~~~~~~~~~
+
+The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments:
+
+**tiffinfo**
+ A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict
+ object containing tiff tags and values. The TIFF field type is
+ autodetected for Numeric and string values, any other types
+ require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory`
+ object and setting the type in
+ :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with
+ the appropriate numerical value from
+ ``TiffTags.TYPES``.
+
+ .. versionadded:: 2.3.0
+
+**compression**
+ A string containing the desired compression method for the
+ file. (valid only with libtiff installed) Valid compression
+ methods are: ``[None, "tiff_ccitt", "group3", "group4",
+ "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan",
+ "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]``
+
+These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo.
+
+**description**
+
+**software**
+
+**date time**
+
+**artist**
+
+**copyright**
+ Strings
+
+**resolution unit**
+ A string of "inch", "centimeter" or "cm"
+
+**resolution**
+
+**x resolution**
+
+**y resolution**
+
+**dpi**
+ Either a Float, Integer, or 2 tuple of (numerator,
+ denominator). Resolution implies an equal x and y resolution, dpi
+ also implies a unit of inches.
+
+WebP
+^^^^
+
+PIL reads and writes WebP files. The specifics of PIL's capabilities with this
+format are currently undocumented.
+
+The :py:meth:`~PIL.Image.Image.save` method supports the following options:
+
+**lossless**
+ If present, instructs the WEBP writer to use lossless
+ compression.
+
+**quality**
+ Integer, 1-100, Defaults to 80. Sets the quality level for
+ lossy compression.
+
+**icc_procfile**
+ The ICC Profile to include in the saved file. Only supported if
+ the system webp library was built with webpmux support.
+
+**exif**
+ The exif data to include in the saved file. Only supported if
+ the system webp library was built with webpmux support.
+
+XBM
+^^^
+
+PIL reads and writes X bitmap files (mode ``1``).
+
+XV Thumbnails
+^^^^^^^^^^^^^
+
+PIL can read XV thumbnail files.
+
+Read-only formats
+-----------------
+
+CUR
+^^^
+
+CUR is used to store cursors on Windows. The CUR decoder reads the largest
+available cursor. Animated cursors are not supported.
+
+DCX
+^^^
+
+DCX is a container file format for PCX files, defined by Intel. The DCX format
+is commonly used in fax applications. The DCX decoder can read files containing
+``1``, ``L``, ``P``, or ``RGB`` data.
+
+When the file is opened, only the first image is read. You can use
+:py:meth:`~file.seek` or :py:mod:`~PIL.ImageSequence` to read other images.
+
+FLI, FLC
+^^^^^^^^
+
+PIL reads Autodesk FLI and FLC animations.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**duration**
+ The delay (in milliseconds) between each frame.
+
+FPX
+^^^
+
+PIL reads Kodak FlashPix files. In the current version, only the highest
+resolution image is read from the file, and the viewing transform is not taken
+into account.
+
+.. note::
+
+ To enable full FlashPix support, you need to build and install the IJG JPEG
+ library before building the Python Imaging Library. See the distribution
+ README for details.
+
+GBR
+^^^
+
+The GBR decoder reads GIMP brush files.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**description**
+ The brush name.
+
+GD
+^^
+
+PIL reads uncompressed GD files. Note that this file format cannot be
+automatically identified, so you must use :py:func:`PIL.GdImageFile.open` to
+read such a file.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**transparency**
+ Transparency color index. This key is omitted if the image is not
+ transparent.
+
+ICO
+^^^
+
+ICO is used to store icons on Windows. The largest available icon is read.
+
+IMT
+^^^
+
+PIL reads Image Tools images containing ``L`` data.
+
+IPTC/NAA
+^^^^^^^^
+
+PIL provides limited read support for IPTC/NAA newsphoto files.
+
+MCIDAS
+^^^^^^
+
+PIL identifies and reads 8-bit McIdas area files.
+
+MIC (read only)
+
+PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the
+first sprite in the file is loaded. You can use :py:meth:`~file.seek` and
+:py:meth:`~file.tell` to read other sprites from the file.
+
+PCD
+^^^
+
+PIL reads PhotoCD files containing ``RGB`` data. By default, the 768x512
+resolution is read. You can use the :py:meth:`~PIL.Image.Image.draft` method to
+read the lower resolution versions instead, thus effectively resizing the image
+to 384x256 or 192x128. Higher resolutions cannot be read by the Python Imaging
+Library.
+
+PSD
+^^^
+
+PIL identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0.
+
+SGI
+^^^
+
+PIL reads uncompressed ``L``, ``RGB``, and ``RGBA`` files.
+
+TGA
+^^^
+
+PIL reads 24- and 32-bit uncompressed and run-length encoded TGA files.
+
+WAL
+^^^
+
+.. versionadded:: 1.1.4
+
+PIL reads Quake2 WAL texture files.
+
+Note that this file format cannot be automatically identified, so you must use
+the open function in the :py:mod:`~PIL.WalImageFile` module to read files in
+this format.
+
+By default, a Quake2 standard palette is attached to the texture. To override
+the palette, use the putpalette method.
+
+XPM
+^^^
+
+PIL reads X pixmap files (mode ``P``) with 256 colors or less.
+
+The :py:meth:`~PIL.Image.Image.open` method sets the following
+:py:attr:`~PIL.Image.Image.info` properties:
+
+**transparency**
+ Transparency color index. This key is omitted if the image is not
+ transparent.
+
+Write-only formats
+------------------
+
+PALM
+^^^^
+
+PIL provides write-only support for PALM pixmap files.
+
+The format code is ``Palm``, the extension is ``.palm``.
+
+PDF
+^^^
+
+PIL can write PDF (Acrobat) images. Such images are written as binary PDF 1.1
+files, using either JPEG or HEX encoding depending on the image mode (and
+whether JPEG support is available or not).
+
+PIXAR (read only)
+
+PIL provides limited support for PIXAR raster files. The library can identify
+and read “dumped” RGB files.
+
+The format code is ``PIXAR``.
+
+Identify-only formats
+---------------------
+
+BUFR
+^^^^
+
+.. versionadded:: 1.1.3
+
+PIL provides a stub driver for BUFR files.
+
+To add read or write support to your application, use
+:py:func:`PIL.BufrStubImagePlugin.register_handler`.
+
+FITS
+^^^^
+
+.. versionadded:: 1.1.5
+
+PIL provides a stub driver for FITS files.
+
+To add read or write support to your application, use
+:py:func:`PIL.FitsStubImagePlugin.register_handler`.
+
+GRIB
+^^^^
+
+.. versionadded:: 1.1.5
+
+PIL provides a stub driver for GRIB files.
+
+The driver requires the file to start with a GRIB header. If you have files
+with embedded GRIB data, or files with multiple GRIB fields, your application
+has to seek to the header before passing the file handle to PIL.
+
+To add read or write support to your application, use
+:py:func:`PIL.GribStubImagePlugin.register_handler`.
+
+HDF5
+^^^^
+
+.. versionadded:: 1.1.5
+
+PIL provides a stub driver for HDF5 files.
+
+To add read or write support to your application, use
+:py:func:`PIL.Hdf5StubImagePlugin.register_handler`.
+
+MPEG
+^^^^
+
+PIL identifies MPEG files.
+
+WMF
+^^^
+
+PIL can identify placable WMF files.
+
+In PIL 1.1.4 and earlier, the WMF driver provides some limited rendering
+support, but not enough to be useful for any real application.
+
+In PIL 1.1.5 and later, the WMF driver is a stub driver. To add WMF read or
+write support to your application, use
+:py:func:`PIL.WmfImagePlugin.register_handler` to register a WMF handler.
+
+::
+
+ from PIL import Image
+ from PIL import WmfImagePlugin
+
+ class WmfHandler:
+ def open(self, im):
+ ...
+ def load(self, im):
+ ...
+ return image
+ def save(self, im, fp, filename):
+ ...
+
+ wmf_handler = WmfHandler()
+
+ WmfImagePlugin.register_handler(wmf_handler)
+
+ im = Image.open("sample.wmf")
diff --git a/docs/handbook/overview.rst b/docs/handbook/overview.rst
new file mode 100644
index 000000000..f1c26e616
--- /dev/null
+++ b/docs/handbook/overview.rst
@@ -0,0 +1,46 @@
+Overview
+========
+
+The **Python Imaging Library** adds image processing capabilities to your
+Python interpreter.
+
+This library provides extensive file format support, an efficient internal
+representation, and fairly powerful image processing capabilities.
+
+The core image library is designed for fast access to data stored in a few
+basic pixel formats. It should provide a solid foundation for a general image
+processing tool.
+
+Let’s look at a few possible uses of this library.
+
+Image Archives
+--------------
+
+The Python Imaging Library is ideal for for image archival and batch processing
+applications. You can use the library to create thumbnails, convert between
+file formats, print images, etc.
+
+The current version identifies and reads a large number of formats. Write
+support is intentionally restricted to the most commonly used interchange and
+presentation formats.
+
+Image Display
+-------------
+
+The current release includes Tk :py:class:`~PIL.ImageTk.PhotoImage` and
+:py:class:`~PIL.ImageTk.BitmapImage` interfaces, as well as a :py:mod:`Windows
+DIB interface ` that can be used with PythonWin and other
+Windows-based toolkits. Many other GUI toolkits come with some kind of PIL
+support.
+
+For debugging, there’s also a :py:meth:`show` method which saves an image to
+disk, and calls an external display utility.
+
+Image Processing
+----------------
+
+The library contains basic image processing functionality, including point operations, filtering with a set of built-in convolution kernels, and colour space conversions.
+
+The library also supports image resizing, rotation and arbitrary affine transforms.
+
+There’s a histogram method allowing you to pull some statistics out of an image. This can be used for automatic contrast enhancement, and for global statistical analysis.
diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst
new file mode 100644
index 000000000..d7bb98386
--- /dev/null
+++ b/docs/handbook/tutorial.rst
@@ -0,0 +1,539 @@
+Tutorial
+========
+
+Using the Image class
+---------------------
+
+The most important class in the Python Imaging Library is the
+:py:class:`~PIL.Image.Image` class, defined in the module with the same name.
+You can create instances of this class in several ways; either by loading
+images from files, processing other images, or creating images from scratch.
+
+To load an image from a file, use the :py:func:`~PIL.Image.open` function
+in the :py:mod:`~PIL.Image` module::
+
+ >>> from PIL import Image
+ >>> im = Image.open("lena.ppm")
+
+If successful, this function returns an :py:class:`~PIL.Image.Image` object.
+You can now use instance attributes to examine the file contents::
+
+ >>> from __future__ import print_function
+ >>> print(im.format, im.size, im.mode)
+ PPM (512, 512) RGB
+
+The :py:attr:`~PIL.Image.Image.format` attribute identifies the source of an
+image. If the image was not read from a file, it is set to None. The size
+attribute is a 2-tuple containing width and height (in pixels). The
+:py:attr:`~PIL.Image.Image.mode` attribute defines the number and names of the
+bands in the image, and also the pixel type and depth. Common modes are “L”
+(luminance) for greyscale images, “RGB” for true color images, and “CMYK” for
+pre-press images.
+
+If the file cannot be opened, an :py:exc:`IOError` exception is raised.
+
+Once you have an instance of the :py:class:`~PIL.Image.Image` class, you can use
+the methods defined by this class to process and manipulate the image. For
+example, let’s display the image we just loaded::
+
+ >>> im.show()
+
+.. note::
+
+ The standard version of :py:meth:`~PIL.Image.Image.show` is not very
+ efficient, since it saves the image to a temporary file and calls the
+ :command:`xv` utility to display the image. If you don’t have :command:`xv`
+ installed, it won’t even work. When it does work though, it is very handy
+ for debugging and tests.
+
+The following sections provide an overview of the different functions provided in this library.
+
+Reading and writing images
+--------------------------
+
+The Python Imaging Library supports a wide variety of image file formats. To
+read files from disk, use the :py:func:`~PIL.Image.open` function in the
+:py:mod:`~PIL.Image` module. You don’t have to know the file format to open a
+file. The library automatically determines the format based on the contents of
+the file.
+
+To save a file, use the :py:meth:`~PIL.Image.Image.save` method of the
+:py:class:`~PIL.Image.Image` class. When saving files, the name becomes
+important. Unless you specify the format, the library uses the filename
+extension to discover which file storage format to use.
+
+Convert files to JPEG
+^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ from __future__ import print_function
+ import os, sys
+ from PIL import Image
+
+ for infile in sys.argv[1:]:
+ f, e = os.path.splitext(infile)
+ outfile = f + ".jpg"
+ if infile != outfile:
+ try:
+ Image.open(infile).save(outfile)
+ except IOError:
+ print("cannot convert", infile)
+
+A second argument can be supplied to the :py:meth:`~PIL.Image.Image.save`
+method which explicitly specifies a file format. If you use a non-standard
+extension, you must always specify the format this way:
+
+Create JPEG thumbnails
+^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ from __future__ import print_function
+ import os, sys
+ from PIL import Image
+
+ size = (128, 128)
+
+ for infile in sys.argv[1:]:
+ outfile = os.path.splitext(infile)[0] + ".thumbnail"
+ if infile != outfile:
+ try:
+ im = Image.open(infile)
+ im.thumbnail(size)
+ im.save(outfile, "JPEG")
+ except IOError:
+ print("cannot create thumbnail for", infile)
+
+
+It is important to note that the library doesn’t decode or load the raster data
+unless it really has to. When you open a file, the file header is read to
+determine the file format and extract things like mode, size, and other
+properties required to decode the file, but the rest of the file is not
+processed until later.
+
+This means that opening an image file is a fast operation, which is independent
+of the file size and compression type. Here’s a simple script to quickly
+identify a set of image files:
+
+Identify Image Files
+^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ from __future__ import print_function
+ import sys
+ from PIL import Image
+
+ for infile in sys.argv[1:]:
+ try:
+ im = Image.open(infile)
+ print(infile, im.format, "%dx%d" % im.size, im.mode)
+ except IOError:
+ pass
+
+Cutting, pasting, and merging images
+------------------------------------
+
+The :py:class:`~PIL.Image.Image` class contains methods allowing you to
+manipulate regions within an image. To extract a sub-rectangle from an image,
+use the :py:meth:`~PIL.Image.Image.crop` method.
+
+Copying a subrectangle from an image
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ box = (100, 100, 400, 400)
+ region = im.crop(box)
+
+The region is defined by a 4-tuple, where coordinates are (left, upper, right,
+lower). The Python Imaging Library uses a coordinate system with (0, 0) in the
+upper left corner. Also note that coordinates refer to positions between the
+pixels, so the region in the above example is exactly 300x300 pixels.
+
+The region could now be processed in a certain manner and pasted back.
+
+Processing a subrectangle, and pasting it back
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ region = region.transpose(Image.ROTATE_180)
+ im.paste(region, box)
+
+When pasting regions back, the size of the region must match the given region
+exactly. In addition, the region cannot extend outside the image. However, the
+modes of the original image and the region do not need to match. If they don’t,
+the region is automatically converted before being pasted (see the section on
+:ref:`color-transforms` below for details).
+
+Here’s an additional example:
+
+Rolling an image
+^^^^^^^^^^^^^^^^
+
+::
+
+ def roll(image, delta):
+ "Roll an image sideways"
+
+ xsize, ysize = image.size
+
+ delta = delta % xsize
+ if delta == 0: return image
+
+ part1 = image.crop((0, 0, delta, ysize))
+ part2 = image.crop((delta, 0, xsize, ysize))
+ image.paste(part2, (0, 0, xsize-delta, ysize))
+ image.paste(part1, (xsize-delta, 0, xsize, ysize))
+
+ return image
+
+For more advanced tricks, the paste method can also take a transparency mask as
+an optional argument. In this mask, the value 255 indicates that the pasted
+image is opaque in that position (that is, the pasted image should be used as
+is). The value 0 means that the pasted image is completely transparent. Values
+in-between indicate different levels of transparency.
+
+The Python Imaging Library also allows you to work with the individual bands of
+an multi-band image, such as an RGB image. The split method creates a set of
+new images, each containing one band from the original multi-band image. The
+merge function takes a mode and a tuple of images, and combines them into a new
+image. The following sample swaps the three bands of an RGB image:
+
+Splitting and merging bands
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ r, g, b = im.split()
+ im = Image.merge("RGB", (b, g, r))
+
+Note that for a single-band image, :py:meth:`~PIL.Image.Image.split` returns
+the image itself. To work with individual color bands, you may want to convert
+the image to “RGB” first.
+
+Geometrical transforms
+----------------------
+
+The :py:class:`PIL.Image.Image` class contains methods to
+:py:meth:`~PIL.Image.Image.resize` and :py:meth:`~PIL.Image.Image.rotate` an
+image. The former takes a tuple giving the new size, the latter the angle in
+degrees counter-clockwise.
+
+Simple geometry transforms
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ out = im.resize((128, 128))
+ out = im.rotate(45) # degrees counter-clockwise
+
+To rotate the image in 90 degree steps, you can either use the
+:py:meth:`~PIL.Image.Image.rotate` method or the
+:py:meth:`~PIL.Image.Image.transpose` method. The latter can also be used to
+flip an image around its horizontal or vertical axis.
+
+Transposing an image
+^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ out = im.transpose(Image.FLIP_LEFT_RIGHT)
+ out = im.transpose(Image.FLIP_TOP_BOTTOM)
+ out = im.transpose(Image.ROTATE_90)
+ out = im.transpose(Image.ROTATE_180)
+ out = im.transpose(Image.ROTATE_270)
+
+There’s no difference in performance or result between ``transpose(ROTATE)``
+and corresponding :py:meth:`~PIL.Image.Image.rotate` operations.
+
+A more general form of image transformations can be carried out via the
+:py:meth:`~PIL.Image.Image.transform` method.
+
+.. _color-transforms:
+
+Color transforms
+----------------
+
+The Python Imaging Library allows you to convert images between different pixel
+representations using the :py:meth:`~PIL.Image.Image.convert` method.
+
+Converting between modes
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ im = Image.open("lena.ppm").convert("L")
+
+The library supports transformations between each supported mode and the “L”
+and “RGB” modes. To convert between other modes, you may have to use an
+intermediate image (typically an “RGB” image).
+
+Image enhancement
+-----------------
+
+The Python Imaging Library provides a number of methods and modules that can be
+used to enhance images.
+
+Filters
+^^^^^^^
+
+The :py:mod:`~PIL.ImageFilter` module contains a number of pre-defined
+enhancement filters that can be used with the
+:py:meth:`~PIL.Image.Image.filter` method.
+
+Applying filters
+~~~~~~~~~~~~~~~~
+
+::
+
+ from PIL import ImageFilter
+ out = im.filter(ImageFilter.DETAIL)
+
+Point Operations
+^^^^^^^^^^^^^^^^
+
+The :py:meth:`~PIL.Image.Image.point` method can be used to translate the pixel
+values of an image (e.g. image contrast manipulation). In most cases, a
+function object expecting one argument can be passed to the this method. Each
+pixel is processed according to that function:
+
+Applying point transforms
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ # multiply each pixel by 1.2
+ out = im.point(lambda i: i * 1.2)
+
+Using the above technique, you can quickly apply any simple expression to an
+image. You can also combine the :py:meth:`~PIL.Image.Image.point` and
+:py:meth:`~PIL.Image.Image.paste` methods to selectively modify an image:
+
+Processing individual bands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ # split the image into individual bands
+ source = im.split()
+
+ R, G, B = 0, 1, 2
+
+ # select regions where red is less than 100
+ mask = source[R].point(lambda i: i < 100 and 255)
+
+ # process the green band
+ out = source[G].point(lambda i: i * 0.7)
+
+ # paste the processed band back, but only where red was < 100
+ source[G].paste(out, None, mask)
+
+ # build a new multiband image
+ im = Image.merge(im.mode, source)
+
+Note the syntax used to create the mask::
+
+ imout = im.point(lambda i: expression and 255)
+
+Python only evaluates the portion of a logical expression as is necessary to
+determine the outcome, and returns the last value examined as the result of the
+expression. So if the expression above is false (0), Python does not look at
+the second operand, and thus returns 0. Otherwise, it returns 255.
+
+Enhancement
+^^^^^^^^^^^
+
+For more advanced image enhancement, you can use the classes in the
+:py:mod:`~PIL.ImageEnhance` module. Once created from an image, an enhancement
+object can be used to quickly try out different settings.
+
+You can adjust contrast, brightness, color balance and sharpness in this way.
+
+Enhancing images
+~~~~~~~~~~~~~~~~
+
+::
+
+ from PIL import ImageEnhance
+
+ enh = ImageEnhance.Contrast(im)
+ enh.enhance(1.3).show("30% more contrast")
+
+Image sequences
+---------------
+
+The Python Imaging Library contains some basic support for image sequences
+(also called animation formats). Supported sequence formats include FLI/FLC,
+GIF, and a few experimental formats. TIFF files can also contain more than one
+frame.
+
+When you open a sequence file, PIL automatically loads the first frame in the
+sequence. You can use the seek and tell methods to move between different
+frames:
+
+Reading sequences
+^^^^^^^^^^^^^^^^^
+
+::
+
+ from PIL import Image
+
+ im = Image.open("animation.gif")
+ im.seek(1) # skip to the second frame
+
+ try:
+ while 1:
+ im.seek(im.tell()+1)
+ # do something to im
+ except EOFError:
+ pass # end of sequence
+
+As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
+sequence ends.
+
+Note that most drivers in the current version of the library only allow you to
+seek to the next frame (as in the above example). To rewind the file, you may
+have to reopen it.
+
+The following iterator class lets you to use the for-statement to loop over the
+sequence:
+
+A sequence iterator class
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ class ImageSequence:
+ def __init__(self, im):
+ self.im = im
+ def __getitem__(self, ix):
+ try:
+ if ix:
+ self.im.seek(ix)
+ return self.im
+ except EOFError:
+ raise IndexError # end of sequence
+
+ for frame in ImageSequence(im):
+ # ...do something to frame...
+
+
+Postscript printing
+-------------------
+
+The Python Imaging Library includes functions to print images, text and
+graphics on Postscript printers. Here’s a simple example:
+
+Drawing Postscript
+^^^^^^^^^^^^^^^^^^
+
+::
+
+ from PIL import Image
+ from PIL import PSDraw
+
+ im = Image.open("lena.ppm")
+ title = "lena"
+ box = (1*72, 2*72, 7*72, 10*72) # in points
+
+ ps = PSDraw.PSDraw() # default is sys.stdout
+ ps.begin_document(title)
+
+ # draw the image (75 dpi)
+ ps.image(box, im, 75)
+ ps.rectangle(box)
+
+ # draw centered title
+ ps.setfont("HelveticaNarrow-Bold", 36)
+ w, h, b = ps.textsize(title)
+ ps.text((4*72-w/2, 1*72-h), title)
+
+ ps.end_document()
+
+More on reading images
+----------------------
+
+As described earlier, the :py:func:`~PIL.Image.open` function of the
+:py:mod:`~PIL.Image` module is used to open an image file. In most cases, you
+simply pass it the filename as an argument::
+
+ im = Image.open("lena.ppm")
+
+If everything goes well, the result is an :py:class:`PIL.Image.Image` object.
+Otherwise, an :exc:`IOError` exception is raised.
+
+You can use a file-like object instead of the filename. The object must
+implement :py:meth:`~file.read`, :py:meth:`~file.seek` and
+:py:meth:`~file.tell` methods, and be opened in binary mode.
+
+Reading from an open file
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ fp = open("lena.ppm", "rb")
+ im = Image.open(fp)
+
+To read an image from string data, use the :py:class:`~StringIO.StringIO`
+class:
+
+Reading from a string
+^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ import StringIO
+
+ im = Image.open(StringIO.StringIO(buffer))
+
+Note that the library rewinds the file (using ``seek(0)``) before reading the
+image header. In addition, seek will also be used when the image data is read
+(by the load method). If the image file is embedded in a larger file, such as a
+tar file, you can use the :py:class:`~PIL.ContainerIO` or
+:py:class:`~PIL.TarIO` modules to access it.
+
+Reading from a tar archive
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ from PIL import TarIO
+
+ fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
+ im = Image.open(fp)
+
+Controlling the decoder
+-----------------------
+
+Some decoders allow you to manipulate the image while reading it from a file.
+This can often be used to speed up decoding when creating thumbnails (when
+speed is usually more important than quality) and printing to a monochrome
+laser printer (when only a greyscale version of the image is needed).
+
+The :py:meth:`~PIL.Image.Image.draft` method manipulates an opened but not yet
+loaded image so it as closely as possible matches the given mode and size. This
+is done by reconfiguring the image decoder.
+
+Reading in draft mode
+^^^^^^^^^^^^^^^^^^^^^
+
+::
+
+ from __future__ import print_function
+ im = Image.open(file)
+ print("original =", im.mode, im.size)
+
+ im.draft("L", (100, 100))
+ print("draft =", im.mode, im.size)
+
+This prints something like::
+
+ original = RGB (512, 512)
+ draft = L (128, 128)
+
+Note that the resulting image may not exactly match the requested mode and
+size. To make sure that the image is not larger than the given size, use the
+thumbnail method instead.
diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst
new file mode 100644
index 000000000..10833a53e
--- /dev/null
+++ b/docs/handbook/writing-your-own-file-decoder.rst
@@ -0,0 +1,281 @@
+Writing your own file decoder
+=============================
+
+The Python Imaging Library uses a plug-in model which allows you to
+add your own decoders to the library, without any changes to the
+library itself. Such plug-ins usually have names like
+:file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name
+(usually an abbreviation).
+
+.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually.
+
+A decoder plug-in should contain a decoder class, based on the
+:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an
+:py:meth:`_open` method, which reads the file header and sets up at least the
+:py:attr:`~PIL.Image.Image.mode` and :py:attr:`~PIL.Image.Image.size`
+attributes. To be able to load the file, the method must also create a list of
+:py:attr:`tile` descriptors. The class must be explicitly registered, via a
+call to the :py:mod:`~PIL.Image` module.
+
+For performance reasons, it is important that the :py:meth:`_open` method
+quickly rejects files that do not have the appropriate contents.
+
+Example
+-------
+
+The following plug-in supports a simple format, which has a 128-byte header
+consisting of the words “SPAM” followed by the width, height, and pixel size in
+bits. The header fields are separated by spaces. The image data follows
+directly after the header, and can be either bi-level, greyscale, or 24-bit
+true color.
+
+**SpamImagePlugin.py**::
+
+ from PIL import Image, ImageFile
+ import string
+
+ class SpamImageFile(ImageFile.ImageFile):
+
+ format = "SPAM"
+ format_description = "Spam raster image"
+
+ def _open(self):
+
+ # check header
+ header = self.fp.read(128)
+ if header[:4] != "SPAM":
+ raise SyntaxError, "not a SPAM file"
+
+ header = string.split(header)
+
+ # size in pixels (width, height)
+ self.size = int(header[1]), int(header[2])
+
+ # mode setting
+ bits = int(header[3])
+ if bits == 1:
+ self.mode = "1"
+ elif bits == 8:
+ self.mode = "L"
+ elif bits == 24:
+ self.mode = "RGB"
+ else:
+ raise SyntaxError, "unknown number of bits"
+
+ # data descriptor
+ self.tile = [
+ ("raw", (0, 0) + self.size, 128, (self.mode, 0, 1))
+ ]
+
+ Image.register_open("SPAM", SpamImageFile)
+
+ Image.register_extension("SPAM", ".spam")
+ Image.register_extension("SPAM", ".spa") # dos version
+
+The format handler must always set the :py:attr:`~PIL.Image.Image.size` and
+:py:attr:`~PIL.Image.Image.mode` attributes. If these are not set, the file
+cannot be opened. To simplify the decoder, the calling code considers
+exceptions like :py:exc:`SyntaxError`, :py:exc:`KeyError`, and
+:py:exc:`IndexError`, as a failure to identify the file.
+
+Note that the decoder must be explicitly registered using
+:py:func:`PIL.Image.register_open`. Although not required, it is also a good
+idea to register any extensions used by this format.
+
+The :py:attr:`tile` attribute
+-----------------------------
+
+To be able to read the file as well as just identifying it, the :py:attr:`tile`
+attribute must also be set. This attribute consists of a list of tile
+descriptors, where each descriptor specifies how data should be loaded to a
+given region in the image. In most cases, only a single descriptor is used,
+covering the full image.
+
+The tile descriptor is a 4-tuple with the following contents::
+
+ (decoder, region, offset, parameters)
+
+The fields are used as follows:
+
+**decoder**
+ Specifies which decoder to use. The ``raw`` decoder used here supports
+ uncompressed data, in a variety of pixel formats. For more information on
+ this decoder, see the description below.
+
+**region**
+ A 4-tuple specifying where to store data in the image.
+
+**offset**
+ Byte offset from the beginning of the file to image data.
+
+**parameters**
+ Parameters to the decoder. The contents of this field depends on the
+ decoder specified by the first field in the tile descriptor tuple. If the
+ decoder doesn’t need any parameters, use None for this field.
+
+Note that the :py:attr:`tile` attribute contains a list of tile descriptors,
+not just a single descriptor.
+
+The ``raw`` decoder
+
+The ``raw`` decoder is used to read uncompressed data from an image file. It
+can be used with most uncompressed file formats, such as PPM, BMP, uncompressed
+TIFF, and many others. To use the raw decoder with the
+:py:func:`PIL.Image.fromstring` function, use the following syntax::
+
+ image = Image.fromstring(
+ mode, size, data, "raw",
+ raw mode, stride, orientation
+ )
+
+When used in a tile descriptor, the parameter field should look like::
+
+ (raw mode, stride, orientation)
+
+The fields are used as follows:
+
+**raw mode**
+ The pixel layout used in the file, and is used to properly convert data to
+ PIL’s internal layout. For a summary of the available formats, see the
+ table below.
+
+**stride**
+ The distance in bytes between two consecutive lines in the image. If 0, the
+ image is assumed to be packed (no padding between lines). If omitted, the
+ stride defaults to 0.
+
+**orientation**
+
+ Whether the first line in the image is the top line on the screen (1), or
+ the bottom line (-1). If omitted, the orientation defaults to 1.
+
+The **raw mode** field is used to determine how the data should be unpacked to
+match PIL’s internal pixel layout. PIL supports a large set of raw modes; for a
+complete list, see the table in the :py:mod:`Unpack.c` module. The following
+table describes some commonly used **raw modes**:
+
++-----------+-----------------------------------------------------------------+
+| mode | description |
++===========+=================================================================+
+| ``1`` | 1-bit bilevel, stored with the leftmost pixel in the most |
+| | significant bit. 0 means black, 1 means white. |
++-----------+-----------------------------------------------------------------+
+| ``1;I`` | 1-bit inverted bilevel, stored with the leftmost pixel in the |
+| | most significant bit. 0 means white, 1 means black. |
++-----------+-----------------------------------------------------------------+
+| ``1;R`` | 1-bit reversed bilevel, stored with the leftmost pixel in the |
+| | least significant bit. 0 means black, 1 means white. |
++-----------+-----------------------------------------------------------------+
+| ``L`` | 8-bit greyscale. 0 means black, 255 means white. |
++-----------+-----------------------------------------------------------------+
+| ``L;I`` | 8-bit inverted greyscale. 0 means white, 255 means black. |
++-----------+-----------------------------------------------------------------+
+| ``P`` | 8-bit palette-mapped image. |
++-----------+-----------------------------------------------------------------+
+| ``RGB`` | 24-bit true colour, stored as (red, green, blue). |
++-----------+-----------------------------------------------------------------+
+| ``BGR`` | 24-bit true colour, stored as (blue, green, red). |
++-----------+-----------------------------------------------------------------+
+| ``RGBX`` | 24-bit true colour, stored as (blue, green, red, pad). |
++-----------+-----------------------------------------------------------------+
+| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, the |
+| | all green pixels, finally all blue pixels). |
++-----------+-----------------------------------------------------------------+
+
+Note that for the most common cases, the raw mode is simply the same as the mode.
+
+The Python Imaging Library supports many other decoders, including JPEG, PNG,
+and PackBits. For details, see the :file:`decode.c` source file, and the
+standard plug-in implementations provided with the library.
+
+Decoding floating point data
+----------------------------
+
+PIL provides some special mechanisms to allow you to load a wide variety of
+formats into a mode ``F`` (floating point) image memory.
+
+You can use the ``raw`` decoder to read images where data is packed in any
+standard machine data type, using one of the following raw modes:
+
+============ =======================================
+mode description
+============ =======================================
+``F`` 32-bit native floating point.
+``F;8`` 8-bit unsigned integer.
+``F;8S`` 8-bit signed integer.
+``F;16`` 16-bit little endian unsigned integer.
+``F;16S`` 16-bit little endian signed integer.
+``F;16B`` 16-bit big endian unsigned integer.
+``F;16BS`` 16-bit big endian signed integer.
+``F;16N`` 16-bit native unsigned integer.
+``F;16NS`` 16-bit native signed integer.
+``F;32`` 32-bit little endian unsigned integer.
+``F;32S`` 32-bit little endian signed integer.
+``F;32B`` 32-bit big endian unsigned integer.
+``F;32BS`` 32-bit big endian signed integer.
+``F;32N`` 32-bit native unsigned integer.
+``F;32NS`` 32-bit native signed integer.
+``F;32F`` 32-bit little endian floating point.
+``F;32BF`` 32-bit big endian floating point.
+``F;32NF`` 32-bit native floating point.
+``F;64F`` 64-bit little endian floating point.
+``F;64BF`` 64-bit big endian floating point.
+``F;64NF`` 64-bit native floating point.
+============ =======================================
+
+The bit decoder
+---------------
+
+If the raw decoder cannot handle your format, PIL also provides a special “bit”
+decoder that can be used to read various packed formats into a floating point
+image memory.
+
+To use the bit decoder with the fromstring function, use the following syntax::
+
+ image = fromstring(
+ mode, size, data, "bit",
+ bits, pad, fill, sign, orientation
+ )
+
+When used in a tile descriptor, the parameter field should look like::
+
+ (bits, pad, fill, sign, orientation)
+
+The fields are used as follows:
+
+**bits**
+ Number of bits per pixel (2-32). No default.
+
+**pad**
+ Padding between lines, in bits. This is either 0 if there is no padding, or
+ 8 if lines are padded to full bytes. If omitted, the pad value defaults to
+ 8.
+
+**fill**
+ Controls how data are added to, and stored from, the decoder bit buffer.
+
+**fill=0**
+ Add bytes to the LSB end of the decoder buffer; store pixels from the MSB
+ end.
+
+**fill=1**
+ Add bytes to the MSB end of the decoder buffer; store pixels from the MSB
+ end.
+
+**fill=2**
+ Add bytes to the LSB end of the decoder buffer; store pixels from the LSB
+ end.
+
+**fill=3**
+ Add bytes to the MSB end of the decoder buffer; store pixels from the LSB
+ end.
+
+ If omitted, the fill order defaults to 0.
+
+**sign**
+ If non-zero, bit fields are sign extended. If zero or omitted, bit fields
+ are unsigned.
+
+**orientation**
+ Whether the first line in the image is the top line on the screen (1), or
+ the bottom line (-1). If omitted, the orientation defaults to 1.
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 000000000..52a054e22
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,67 @@
+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.
+
+.. image:: https://travis-ci.org/python-imaging/Pillow.png
+ :target: https://travis-ci.org/python-imaging/Pillow
+
+.. 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 `.
+
+If you can't find the information you need, try the old `PIL Handbook`_, but be
+aware that it was last updated for PIL 1.1.5. You can download archives and old
+versions from `PyPI `_. You can get the
+source and contribute at https://github.com/python-imaging/Pillow.
+
+.. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm
+
+.. toctree::
+ :maxdepth: 2
+
+ 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 consider helping us financially. Your assistance would
+be very much appreciated!
+
+.. note:: Contributors please add your name and donation preference here.
+
+======================================= =======================================
+**Developer** **Preference**
+======================================= =======================================
+Alex Clark (fork author) http://gittip.com/aclark4life
+======================================= =======================================
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 000000000..6c6fe3196
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,213 @@
+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, 3.4
+
+.. 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
+
+ * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and
+ above uses liblcms2. Tested with **1.19** and **2.2**.
+
+* **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
+
+In Fedora, the command is::
+
+ $ sudo yum install python-devel
+
+Prerequisites are installed on **Ubuntu 10.04 LTS** with::
+
+ $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \
+ libfreetype6-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 liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev
+
+Prerequisites are installed on **Fedora 20** with::
+
+ $ sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
+ lcms2-devel libwebp-devel tcl-devel tk-devel
+
+
+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/make.bat b/docs/make.bat
new file mode 100644
index 000000000..c943319ad
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,190 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PillowPILfork.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PillowPILfork.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+:end
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/plugins.rst b/docs/plugins.rst
new file mode 100644
index 000000000..b92b500c1
--- /dev/null
+++ b/docs/plugins.rst
@@ -0,0 +1,330 @@
+Plugin reference
+================
+
+:mod:`ArgImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.ArgImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`BmpImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.BmpImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`BufrStubImagePlugin` Module
+---------------------------------
+
+.. automodule:: PIL.BufrStubImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`CurImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.CurImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`DcxImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.DcxImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`EpsImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.EpsImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`FitsStubImagePlugin` Module
+---------------------------------
+
+.. automodule:: PIL.FitsStubImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`FliImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.FliImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`FpxImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.FpxImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GbrImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.GbrImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GifImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.GifImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`GribStubImagePlugin` Module
+---------------------------------
+
+.. automodule:: PIL.GribStubImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`Hdf5StubImagePlugin` Module
+---------------------------------
+
+.. automodule:: PIL.Hdf5StubImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`IcnsImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.IcnsImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`IcoImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.IcoImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ImImagePlugin` Module
+---------------------------
+
+.. automodule:: PIL.ImImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`ImtImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.ImtImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`IptcImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.IptcImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`JpegImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.JpegImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`McIdasImagePlugin` Module
+-------------------------------
+
+.. automodule:: PIL.McIdasImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`MicImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.MicImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`MpegImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.MpegImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`MspImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.MspImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PalmImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.PalmImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PcdImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PcdImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PcxImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PcxImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PdfImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PdfImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PixarImagePlugin` Module
+------------------------------
+
+.. automodule:: PIL.PixarImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PngImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PngImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PpmImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PpmImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`PsdImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.PsdImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`SgiImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.SgiImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`SpiderImagePlugin` Module
+-------------------------------
+
+.. automodule:: PIL.SpiderImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`SunImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.SunImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`TgaImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.TgaImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`TiffImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.TiffImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`WebPImagePlugin` Module
+-----------------------------
+
+.. automodule:: PIL.WebPImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`WmfImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.WmfImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`XVThumbImagePlugin` Module
+--------------------------------
+
+.. automodule:: PIL.XVThumbImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`XbmImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.XbmImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+:mod:`XpmImagePlugin` Module
+----------------------------
+
+.. automodule:: PIL.XpmImagePlugin
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/porting-pil-to-pillow.rst b/docs/porting-pil-to-pillow.rst
new file mode 100644
index 000000000..88a6768e9
--- /dev/null
+++ b/docs/porting-pil-to-pillow.rst
@@ -0,0 +1,23 @@
+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 ``Image`` 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
+
+The image plugin loading mechanism has changed. Pillow no longer
+automatically imports any file in the Python path with a name ending
+in :file:`ImagePlugin.py`. You will need to import your image plugin
+manually.
+
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..e63fd99fe
--- /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 or PyQt5 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/5 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
new file mode 100644
index 000000000..d825a5fcc
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,17 @@
+# requirements for working on docs
+
+# install pillow from master if you're into that, but RtD needs this
+pillow>=2.2.1
+
+Jinja2==2.7.1
+MarkupSafe==0.18
+Pygments==1.6
+Sphinx==1.1.3
+docopt==0.6.1
+docutils==0.11
+wsgiref==0.1.2
+sphinx-better-theme==0.1.5
+
+# livereload not strictly necessary but really useful (make livehtml)
+tornado==3.1.1
+livereload==1.0.1
diff --git a/doctest.py b/doctest.py
deleted file mode 100644
index e8140ecdf..000000000
--- a/doctest.py
+++ /dev/null
@@ -1,1111 +0,0 @@
-# Module doctest version 0.9.6
-# Released to the public domain 16-Jan-2001,
-# by Tim Peters (tim.one@home.com).
-
-# local modifications:
-# 2001-02-13 fl: minor tweaks to make it run under both 1.5.2 and 2.0
-
-# Provided as-is; use at your own risk; no warranty; no promises; enjoy!
-
-"""Module doctest -- a framework for running examples in docstrings.
-
-NORMAL USAGE
-
-In normal use, end each module M with:
-
-def _test():
- import doctest, M # replace M with your module's name
- return doctest.testmod(M) # ditto
-
-if __name__ == "__main__":
- _test()
-
-Then running the module as a script will cause the examples in the
-docstrings to get executed and verified:
-
-python M.py
-
-This won't display anything unless an example fails, in which case the
-failing example(s) and the cause(s) of the failure(s) are printed to stdout
-(why not stderr? because stderr is a lame hack <0.2 wink>), and the final
-line of output is "Test failed.".
-
-Run it with the -v switch instead:
-
-python M.py -v
-
-and a detailed report of all examples tried is printed to stdout, along
-with assorted summaries at the end.
-
-You can force verbose mode by passing "verbose=1" to testmod, or prohibit
-it by passing "verbose=0". In either of those cases, sys.argv is not
-examined by testmod.
-
-In any case, testmod returns a 2-tuple of ints (f, t), where f is the
-number of docstring examples that failed and t is the total number of
-docstring examples attempted.
-
-
-WHICH DOCSTRINGS ARE EXAMINED?
-
-+ M.__doc__.
-
-+ f.__doc__ for all functions f in M.__dict__.values(), except those
- with private names.
-
-+ C.__doc__ for all classes C in M.__dict__.values(), except those with
- private names.
-
-+ If M.__test__ exists and "is true", it must be a dict, and
- each entry maps a (string) name to a function object, class object, or
- string. Function and class object docstrings found from M.__test__
- are searched even if the name is private, and strings are searched
- directly as if they were docstrings. In output, a key K in M.__test__
- appears with name
- .__test__.K
-
-Any classes found are recursively searched similarly, to test docstrings in
-their contained methods and nested classes. Private names reached from M's
-globals are skipped, but all names reached from M.__test__ are searched.
-
-By default, a name is considered to be private if it begins with an
-underscore (like "_my_func") but doesn't both begin and end with (at least)
-two underscores (like "__init__"). You can change the default by passing
-your own "isprivate" function to testmod.
-
-If you want to test docstrings in objects with private names too, stuff
-them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your
-own isprivate function to Tester's constructor, or call the rundoc method
-of a Tester instance).
-
-Warning: imports can cause trouble; e.g., if you do
-
-from XYZ import XYZclass
-
-then XYZclass is a name in M.__dict__ too, and doctest has no way to know
-that XYZclass wasn't *defined* in M. So it may try to execute the examples
-in XYZclass's docstring, and those in turn may require a different set of
-globals to work correctly. I prefer to do "import *"- friendly imports,
-a la
-
-import XYY
-_XYZclass = XYZ.XYZclass
-del XYZ
-
-or (Python 2.0)
-
-from XYZ import XYZclass as _XYZclass
-
-and then the leading underscore stops testmod from going nuts. You may
-prefer the method in the next section.
-
-
-WHAT'S THE EXECUTION CONTEXT?
-
-By default, each time testmod finds a docstring to test, it uses a *copy*
-of M's globals (so that running tests on a module doesn't change the
-module's real globals, and so that one test in M can't leave behind crumbs
-that accidentally allow another test to work). This means examples can
-freely use any names defined at top-level in M. It also means that sloppy
-imports (see above) can cause examples in external docstrings to use
-globals inappropriate for them.
-
-You can force use of your own dict as the execution context by passing
-"globs=your_dict" to testmod instead. Presumably this would be a copy of
-M.__dict__ merged with the globals from other imported modules.
-
-
-WHAT IF I WANT TO TEST A WHOLE PACKAGE?
-
-Piece o' cake, provided the modules do their testing from docstrings.
-Here's the test.py I use for the world's most elaborate Rational/
-floating-base-conversion pkg (which I'll distribute some day):
-
-from Rational import Cvt
-from Rational import Format
-from Rational import machprec
-from Rational import Rat
-from Rational import Round
-from Rational import utils
-
-modules = (Cvt,
- Format,
- machprec,
- Rat,
- Round,
- utils)
-
-def _test():
- import doctest
- import sys
- verbose = "-v" in sys.argv
- for mod in modules:
- doctest.testmod(mod, verbose=verbose, report=0)
- doctest.master.summarize()
-
-if __name__ == "__main__":
- _test()
-
-IOW, it just runs testmod on all the pkg modules. testmod remembers the
-names and outcomes (# of failures, # of tries) for each item it's seen, and
-passing "report=0" prevents it from printing a summary in verbose mode.
-Instead, the summary is delayed until all modules have been tested, and
-then "doctest.master.summarize()" forces the summary at the end.
-
-So this is very nice in practice: each module can be tested individually
-with almost no work beyond writing up docstring examples, and collections
-of modules can be tested too as a unit with no more work than the above.
-
-
-WHAT ABOUT EXCEPTIONS?
-
-No problem, as long as the only output generated by the example is the
-traceback itself. For example:
-
- >>> a = [None]
- >>> a[1]
- Traceback (innermost last):
- File "", line 1, in ?
- IndexError: list index out of range
- >>>
-
-Note that only the exception type and value are compared (specifically,
-only the last line in the traceback).
-
-
-ADVANCED USAGE
-
-doctest.testmod() captures the testing policy I find most useful most
-often. You may want other policies.
-
-testmod() actually creates a local instance of class doctest.Tester, runs
-appropriate methods of that class, and merges the results into global
-Tester instance doctest.master.
-
-You can create your own instances of doctest.Tester, and so build your own
-policies, or even run methods of doctest.master directly. See
-doctest.Tester.__doc__ for details.
-
-
-SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!?
-
-Oh ya. It's easy! In most cases a copy-and-paste of an interactive
-console session works fine -- just make sure the leading whitespace is
-rigidly consistent (you can mix tabs and spaces if you're too lazy to do it
-right, but doctest is not in the business of guessing what you think a tab
-means).
-
- >>> # comments are ignored
- >>> x = 12
- >>> x
- 12
- >>> if x == 13:
- ... print "yes"
- ... else:
- ... print "no"
- ... print "NO"
- ... print "NO!!!"
- ...
- no
- NO
- NO!!!
- >>>
-
-Any expected output must immediately follow the final ">>>" or "..." line
-containing the code, and the expected output (if any) extends to the next
-">>>" or all-whitespace line. That's it.
-
-Bummers:
-
-+ Expected output cannot contain an all-whitespace line, since such a line
- is taken to signal the end of expected output.
-
-+ Output to stdout is captured, but not output to stderr (exception
- tracebacks are captured via a different means).
-
-+ If you continue a line via backslashing in an interactive session, or for
- any other reason use a backslash, you need to double the backslash in the
- docstring version. This is simply because you're in a string, and so the
- backslash must be escaped for it to survive intact. Like:
-
->>> if "yes" == \\
-... "y" + \\
-... "es": # in the source code you'll see the doubled backslashes
-... print 'yes'
-yes
-
-The starting column doesn't matter:
-
->>> assert "Easy!"
- >>> import math
- >>> math.floor(1.9)
- 1.0
-
-and as many leading whitespace characters are stripped from the expected
-output as appeared in the initial ">>>" line that triggered it.
-
-If you execute this very file, the examples above will be found and
-executed, leading to this output in verbose mode:
-
-Running doctest.__doc__
-Trying: a = [None]
-Expecting: nothing
-ok
-Trying: a[1]
-Expecting:
-Traceback (innermost last):
- File "", line 1, in ?
-IndexError: list index out of range
-ok
-Trying: x = 12
-Expecting: nothing
-ok
-Trying: x
-Expecting: 12
-ok
-Trying:
-if x == 13:
- print "yes"
-else:
- print "no"
- print "NO"
- print "NO!!!"
-Expecting:
-no
-NO
-NO!!!
-ok
-... and a bunch more like that, with this summary at the end:
-
-5 items had no tests:
- doctest.Tester.__init__
- doctest.Tester.run__test__
- doctest.Tester.summarize
- doctest.run_docstring_examples
- doctest.testmod
-12 items passed all tests:
- 9 tests in doctest
- 6 tests in doctest.Tester
- 10 tests in doctest.Tester.merge
- 7 tests in doctest.Tester.rundict
- 3 tests in doctest.Tester.rundoc
- 3 tests in doctest.Tester.runstring
- 2 tests in doctest.__test__._TestClass
- 2 tests in doctest.__test__._TestClass.__init__
- 2 tests in doctest.__test__._TestClass.get
- 1 tests in doctest.__test__._TestClass.square
- 2 tests in doctest.__test__.string
- 7 tests in doctest.is_private
-54 tests in 17 items.
-54 passed and 0 failed.
-Test passed.
-"""
-
-# 0,0,1 06-Mar-1999
-# initial version posted
-# 0,0,2 06-Mar-1999
-# loosened parsing:
-# cater to stinkin' tabs
-# don't insist on a blank after PS2 prefix
-# so trailing "... " line from a compound stmt no longer
-# breaks if the file gets whitespace-trimmed
-# better error msgs for inconsistent leading whitespace
-# 0,9,1 08-Mar-1999
-# exposed the Tester class and added client methods
-# plus docstring examples of their use (eww - head-twisting!)
-# fixed logic error in reporting total # of tests & failures
-# added __test__ support to testmod (a pale reflection of Christian
-# Tismer's vision ...)
-# removed the "deep" argument; fiddle __test__ instead
-# simplified endcase logic for extracting tests, and running them.
-# before, if no output was expected but some was produced
-# anyway via an eval'ed result, the discrepancy wasn't caught
-# made TestClass private and used __test__ to get at it
-# many doc updates
-# speed _SpoofOut for long expected outputs
-# 0,9,2 09-Mar-1999
-# throw out comments from examples, enabling use of the much simpler
-# exec compile(... "single") ...
-# for simulating the runtime; that barfs on comment-only lines
-# used the traceback module to do a much better job of reporting
-# exceptions
-# run __doc__ values thru str(), "just in case"
-# privateness of names now determined by an overridable "isprivate"
-# function
-# by default a name now considered to be private iff it begins with
-# an underscore but doesn't both begin & end with two of 'em; so
-# e.g. Class.__init__ etc are searched now -- as they always
-# should have been
-# 0,9,3 18-Mar-1999
-# added .flush stub to _SpoofOut (JPython buglet diagnosed by
-# Hugh Emberson)
-# repaired ridiculous docs about backslashes in examples
-# minor internal changes
-# changed source to Unix line-end conventions
-# moved __test__ logic into new Tester.run__test__ method
-# 0,9,4 27-Mar-1999
-# report item name and line # in failing examples
-# 0,9,5 29-Jun-1999
-# allow straightforward exceptions in examples - thanks to Mark Hammond!
-# 0,9,6 16-Jan-2001
-# fiddling for changes in Python 2.0: some of the embedded docstring
-# examples no longer worked *exactly* as advertised, due to minor
-# language changes, and running doctest on itself pointed that out.
-# Hard to think of a better example of why this is useful .
-
-__version__ = 0, 9, 6
-
-import types
-_FunctionType = types.FunctionType
-_ClassType = types.ClassType
-_ModuleType = types.ModuleType
-_StringType = types.StringType
-del types
-
-import string
-_string_find = string.find
-_string_join = string.join
-_string_split = string.split
-_string_rindex = string.rindex
-del string
-
-import re
-PS1 = ">>>"
-PS2 = "..."
-_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match
-_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match
-_isEmpty = re.compile(r"\s*$").match
-_isComment = re.compile(r"\s*#").match
-del re
-
-__all__ = []
-
-# Extract interactive examples from a string. Return a list of triples,
-# (source, outcome, lineno). "source" is the source code, and ends
-# with a newline iff the source spans more than one line. "outcome" is
-# the expected output if any, else an empty string. When not empty,
-# outcome always ends with a newline. "lineno" is the line number,
-# 0-based wrt the start of the string, of the first source line.
-
-def _extract_examples(s):
- isPS1, isPS2 = _isPS1, _isPS2
- isEmpty, isComment = _isEmpty, _isComment
- examples = []
- lines = _string_split(s, "\n")
- i, n = 0, len(lines)
- while i < n:
- line = lines[i]
- i = i + 1
- m = isPS1(line)
- if m is None:
- continue
- j = m.end(0) # beyond the prompt
- if isEmpty(line, j) or isComment(line, j):
- # a bare prompt or comment -- not interesting
- continue
- lineno = i - 1
- if line[j] != " ":
- raise ValueError("line " + `lineno` + " of docstring lacks "
- "blank after " + PS1 + ": " + line)
- j = j + 1
- blanks = m.group(1)
- nblanks = len(blanks)
- # suck up this and following PS2 lines
- source = []
- while 1:
- source.append(line[j:])
- line = lines[i]
- m = isPS2(line)
- if m:
- if m.group(1) != blanks:
- raise ValueError("inconsistent leading whitespace "
- "in line " + `i` + " of docstring: " + line)
- i = i + 1
- else:
- break
- if len(source) == 1:
- source = source[0]
- else:
- # get rid of useless null line from trailing empty "..."
- if source[-1] == "":
- del source[-1]
- source = _string_join(source, "\n") + "\n"
- # suck up response
- if isPS1(line) or isEmpty(line):
- expect = ""
- else:
- expect = []
- while 1:
- if line[:nblanks] != blanks:
- raise ValueError("inconsistent leading whitespace "
- "in line " + `i` + " of docstring: " + line)
- expect.append(line[nblanks:])
- i = i + 1
- line = lines[i]
- if isPS1(line) or isEmpty(line):
- break
- expect = _string_join(expect, "\n") + "\n"
- examples.append( (source, expect, lineno) )
- return examples
-
-# Capture stdout when running examples.
-
-class _SpoofOut:
- def __init__(self):
- self.clear()
- def write(self, s):
- self.buf.append(s)
- def get(self):
- return _string_join(self.buf, "")
- def clear(self):
- self.buf = []
- def flush(self):
- # JPython calls flush
- pass
-
-# Display some tag-and-msg pairs nicely, keeping the tag and its msg
-# on the same line when that makes sense.
-
-def _tag_out(printer, *tag_msg_pairs):
- for tag, msg in tag_msg_pairs:
- printer(tag + ":")
- msg_has_nl = msg[-1:] == "\n"
- msg_has_two_nl = msg_has_nl and \
- _string_find(msg, "\n") < len(msg) - 1
- if len(tag) + len(msg) < 76 and not msg_has_two_nl:
- printer(" ")
- else:
- printer("\n")
- printer(msg)
- if not msg_has_nl:
- printer("\n")
-
-# Run list of examples, in context globs. "out" can be used to display
-# stuff to "the real" stdout, and fakeout is an instance of _SpoofOut
-# that captures the examples' std output. Return (#failures, #tries).
-
-def _run_examples_inner(out, fakeout, examples, globs, verbose, name):
- import sys, traceback
- OK, BOOM, FAIL = range(3)
- NADA = "nothing"
- stderr = _SpoofOut()
- failures = 0
- for source, want, lineno in examples:
- if verbose:
- _tag_out(out, ("Trying", source),
- ("Expecting", want or NADA))
- fakeout.clear()
- try:
- exec compile(source, "", "single") in globs
- got = fakeout.get()
- state = OK
- except:
- # See whether the exception was expected.
- if _string_find(want, "Traceback (innermost last):\n") == 0 or\
- _string_find(want, "Traceback (most recent call last):\n") == 0:
- # Only compare exception type and value - the rest of
- # the traceback isn't necessary.
- want = _string_split(want, '\n')[-2] + '\n'
- exc_type, exc_val, exc_tb = sys.exc_info()
- got = traceback.format_exception_only(exc_type, exc_val)[0]
- state = OK
- else:
- # unexpected exception
- stderr.clear()
- traceback.print_exc(file=stderr)
- state = BOOM
-
- if state == OK:
- if got == want:
- if verbose:
- out("ok\n")
- continue
- state = FAIL
-
- assert state in (FAIL, BOOM)
- failures = failures + 1
- out("*" * 65 + "\n")
- _tag_out(out, ("Failure in example", source))
- out("from line #" + `lineno` + " of " + name + "\n")
- if state == FAIL:
- _tag_out(out, ("Expected", want or NADA), ("Got", got))
- else:
- assert state == BOOM
- _tag_out(out, ("Exception raised", stderr.get()))
-
- return failures, len(examples)
-
-# Run list of examples, in context globs. Return (#failures, #tries).
-
-def _run_examples(examples, globs, verbose, name):
- import sys
- saveout = sys.stdout
- try:
- sys.stdout = fakeout = _SpoofOut()
- x = _run_examples_inner(saveout.write, fakeout, examples,
- globs, verbose, name)
- finally:
- sys.stdout = saveout
- return x
-
-def run_docstring_examples(f, globs, verbose=0, name="NoName"):
- """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__.
-
- Use dict globs as the globals for execution.
- Return (#failures, #tries).
-
- If optional arg verbose is true, print stuff even if there are no
- failures.
- Use string name in failure msgs.
- """
-
- try:
- doc = f.__doc__
- if not doc:
- # docstring empty or None
- return 0, 0
- # just in case CT invents a doc object that has to be forced
- # to look like a string <0.9 wink>
- doc = str(doc)
- except:
- return 0, 0
-
- e = _extract_examples(doc)
- if not e:
- return 0, 0
- return _run_examples(e, globs, verbose, name)
-
-def is_private(prefix, base):
- """prefix, base -> true iff name prefix + "." + base is "private".
-
- Prefix may be an empty string, and base does not contain a period.
- Prefix is ignored (although functions you write conforming to this
- protocol may make use of it).
- Return true iff base begins with an (at least one) underscore, but
- does not both begin and end with (at least) two underscores.
-
- >>> is_private("a.b", "my_func")
- 0
- >>> is_private("____", "_my_func")
- 1
- >>> is_private("someclass", "__init__")
- 0
- >>> is_private("sometypo", "__init_")
- 1
- >>> is_private("x.y.z", "_")
- 1
- >>> is_private("_x.y.z", "__")
- 0
- >>> is_private("", "") # senseless but consistent
- 0
- """
-
- return base[:1] == "_" and not base[:2] == "__" == base[-2:]
-
-class Tester:
- """Class Tester -- runs docstring examples and accumulates stats.
-
-In normal use, function doctest.testmod() hides all this from you,
-so use that if you can. Create your own instances of Tester to do
-fancier things.
-
-Methods:
- runstring(s, name)
- Search string s for examples to run; use name for logging.
- Return (#failures, #tries).
-
- rundoc(object, name=None)
- Search object.__doc__ for examples to run; use name (or
- object.__name__) for logging. Return (#failures, #tries).
-
- rundict(d, name)
- Search for examples in docstrings in all of d.values(); use name
- for logging. Return (#failures, #tries).
-
- run__test__(d, name)
- Treat dict d like module.__test__. Return (#failures, #tries).
-
- summarize(verbose=None)
- Display summary of testing results, to stdout. Return
- (#failures, #tries).
-
- merge(other)
- Merge in the test results from Tester instance "other".
-
->>> from doctest import Tester
->>> t = Tester(globs={'x': 42}, verbose=0)
->>> t.runstring(r'''
-... >>> x = x * 2
-... >>> print x
-... 42
-... ''', 'XYZ')
-*****************************************************************
-Failure in example: print x
-from line #2 of XYZ
-Expected: 42
-Got: 84
-(1, 2)
->>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2')
-(0, 2)
->>> t.summarize()
-1 items had failures:
- 1 of 2 in XYZ
-***Test Failed*** 1 failures.
-(1, 4)
->>> t.summarize(verbose=1)
-1 items passed all tests:
- 2 tests in example2
-1 items had failures:
- 1 of 2 in XYZ
-4 tests in 2 items.
-3 passed and 1 failed.
-***Test Failed*** 1 failures.
-(1, 4)
->>>
-"""
-
- def __init__(self, mod=None, globs=None, verbose=None,
- isprivate=None):
- """mod=None, globs=None, verbose=None, isprivate=None
-
-See doctest.__doc__ for an overview.
-
-Optional keyword arg "mod" is a module, whose globals are used for
-executing examples. If not specified, globs must be specified.
-
-Optional keyword arg "globs" gives a dict to be used as the globals
-when executing examples; if not specified, use the globals from
-module mod.
-
-In either case, a copy of the dict is used for each docstring
-examined.
-
-Optional keyword arg "verbose" prints lots of stuff if true, only
-failures if false; by default, it's true iff "-v" is in sys.argv.
-
-Optional keyword arg "isprivate" specifies a function used to determine
-whether a name is private. The default function is doctest.is_private;
-see its docs for details.
-"""
-
- if mod is None and globs is None:
- raise TypeError("Tester.__init__: must specify mod or globs")
- if mod is not None and type(mod) is not _ModuleType:
- raise TypeError("Tester.__init__: mod must be a module; " +
- `mod`)
- if globs is None:
- globs = mod.__dict__
- self.globs = globs
-
- if verbose is None:
- import sys
- verbose = "-v" in sys.argv
- self.verbose = verbose
-
- if isprivate is None:
- isprivate = is_private
- self.isprivate = isprivate
-
- self.name2ft = {} # map name to (#failures, #trials) pair
-
- def runstring(self, s, name):
- """
- s, name -> search string s for examples to run, logging as name.
-
- Use string name as the key for logging the outcome.
- Return (#failures, #examples).
-
- >>> t = Tester(globs={}, verbose=1)
- >>> test = r'''
- ... # just an example
- ... >>> x = 1 + 2
- ... >>> x
- ... 3
- ... '''
- >>> t.runstring(test, "Example")
- Running string Example
- Trying: x = 1 + 2
- Expecting: nothing
- ok
- Trying: x
- Expecting: 3
- ok
- 0 of 2 examples failed in string Example
- (0, 2)
- """
-
- if self.verbose:
- print "Running string", name
- f = t = 0
- e = _extract_examples(s)
- if e:
- f, t = _run_examples(e, self.globs.copy(), self.verbose, name)
- if self.verbose:
- print f, "of", t, "examples failed in string", name
- self.__record_outcome(name, f, t)
- return f, t
-
- def rundoc(self, object, name=None):
- """
- object, name=None -> search object.__doc__ for examples to run.
-
- Use optional string name as the key for logging the outcome;
- by default use object.__name__.
- Return (#failures, #examples).
- If object is a class object, search recursively for method
- docstrings too.
- object.__doc__ is examined regardless of name, but if object is
- a class, whether private names reached from object are searched
- depends on the constructor's "isprivate" argument.
-
- >>> t = Tester(globs={}, verbose=0)
- >>> def _f():
- ... '''Trivial docstring example.
- ... >>> assert 2 == 2
- ... '''
- ... return 32
- ...
- >>> t.rundoc(_f) # expect 0 failures in 1 example
- (0, 1)
- """
-
- if name is None:
- try:
- name = object.__name__
- except AttributeError:
- raise ValueError("Tester.rundoc: name must be given "
- "when object.__name__ doesn't exist; " + `object`)
- if self.verbose:
- print "Running", name + ".__doc__"
- f, t = run_docstring_examples(object, self.globs.copy(),
- self.verbose, name)
- if self.verbose:
- print f, "of", t, "examples failed in", name + ".__doc__"
- self.__record_outcome(name, f, t)
- if type(object) is _ClassType:
- f2, t2 = self.rundict(object.__dict__, name)
- f = f + f2
- t = t + t2
- return f, t
-
- def rundict(self, d, name):
- """
- d. name -> search for docstring examples in all of d.values().
-
- For k, v in d.items() such that v is a function or class,
- do self.rundoc(v, name + "." + k). Whether this includes
- objects with private names depends on the constructor's
- "isprivate" argument.
- Return aggregate (#failures, #examples).
-
- >>> def _f():
- ... '''>>> assert 1 == 1
- ... '''
- >>> def g():
- ... '''>>> assert 2 != 1
- ... '''
- >>> d = {"_f": _f, "g": g}
- >>> t = Tester(globs={}, verbose=0)
- >>> t.rundict(d, "rundict_test") # _f is skipped
- (0, 1)
- >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0)
- >>> t.rundict(d, "rundict_test_pvt") # both are searched
- (0, 2)
- """
-
- if not hasattr(d, "items"):
- raise TypeError("Tester.rundict: d must support .items(); " +
- `d`)
- f = t = 0
- for thisname, value in d.items():
- if type(value) in (_FunctionType, _ClassType):
- f2, t2 = self.__runone(value, name + "." + thisname)
- f = f + f2
- t = t + t2
- return f, t
-
- def run__test__(self, d, name):
- """d, name -> Treat dict d like module.__test__.
-
- Return (#failures, #tries).
- See testmod.__doc__ for details.
- """
-
- failures = tries = 0
- prefix = name + "."
- savepvt = self.isprivate
- try:
- self.isprivate = lambda *args: 0
- for k, v in d.items():
- thisname = prefix + k
- if type(v) is _StringType:
- f, t = self.runstring(v, thisname)
- elif type(v) in (_FunctionType, _ClassType):
- f, t = self.rundoc(v, thisname)
- else:
- raise TypeError("Tester.run__test__: values in "
- "dict must be strings, functions "
- "or classes; " + `v`)
- failures = failures + f
- tries = tries + t
- finally:
- self.isprivate = savepvt
- return failures, tries
-
- def summarize(self, verbose=None):
- """
- verbose=None -> summarize results, return (#failures, #tests).
-
- Print summary of test results to stdout.
- Optional arg 'verbose' controls how wordy this is. By
- default, use the verbose setting established by the
- constructor.
- """
-
- if verbose is None:
- verbose = self.verbose
- notests = []
- passed = []
- failed = []
- totalt = totalf = 0
- for x in self.name2ft.items():
- name, (f, t) = x
- assert f <= t
- totalt = totalt + t
- totalf = totalf + f
- if t == 0:
- notests.append(name)
- elif f == 0:
- passed.append( (name, t) )
- else:
- failed.append(x)
- if verbose:
- if notests:
- print len(notests), "items had no tests:"
- notests.sort()
- for thing in notests:
- print " ", thing
- if passed:
- print len(passed), "items passed all tests:"
- passed.sort()
- for thing, count in passed:
- print " %3d tests in %s" % (count, thing)
- if failed:
- print len(failed), "items had failures:"
- failed.sort()
- for thing, (f, t) in failed:
- print " %3d of %3d in %s" % (f, t, thing)
- if verbose:
- print totalt, "tests in", len(self.name2ft), "items."
- print totalt - totalf, "passed and", totalf, "failed."
- if totalf:
- print "***Test Failed***", totalf, "failures."
- elif verbose:
- print "Test passed."
- return totalf, totalt
-
- def merge(self, other):
- """
- other -> merge in test results from the other Tester instance.
-
- If self and other both have a test result for something
- with the same name, the (#failures, #tests) results are
- summed, and a warning is printed to stdout.
-
- >>> from doctest import Tester
- >>> t1 = Tester(globs={}, verbose=0)
- >>> t1.runstring('''
- ... >>> x = 12
- ... >>> print x
- ... 12
- ... ''', "t1example")
- (0, 2)
- >>>
- >>> t2 = Tester(globs={}, verbose=0)
- >>> t2.runstring('''
- ... >>> x = 13
- ... >>> print x
- ... 13
- ... ''', "t2example")
- (0, 2)
- >>> common = ">>> assert 1 + 2 == 3\\n"
- >>> t1.runstring(common, "common")
- (0, 1)
- >>> t2.runstring(common, "common")
- (0, 1)
- >>> t1.merge(t2)
- *** Tester.merge: 'common' in both testers; summing outcomes.
- >>> t1.summarize(1)
- 3 items passed all tests:
- 2 tests in common
- 2 tests in t1example
- 2 tests in t2example
- 6 tests in 3 items.
- 6 passed and 0 failed.
- Test passed.
- (0, 6)
- >>>
- """
-
- d = self.name2ft
- for name, (f, t) in other.name2ft.items():
- if d.has_key(name):
- print "*** Tester.merge: '" + name + "' in both" \
- " testers; summing outcomes."
- f2, t2 = d[name]
- f = f + f2
- t = t + t2
- d[name] = f, t
-
- def __record_outcome(self, name, f, t):
- if self.name2ft.has_key(name):
- print "*** Warning: '" + name + "' was tested before;", \
- "summing outcomes."
- f2, t2 = self.name2ft[name]
- f = f + f2
- t = t + t2
- self.name2ft[name] = f, t
-
- def __runone(self, target, name):
- if "." in name:
- i = _string_rindex(name, ".")
- prefix, base = name[:i], name[i+1:]
- else:
- prefix, base = "", base
- if self.isprivate(prefix, base):
- return 0, 0
- return self.rundoc(target, name)
-
-master = None
-
-def testmod(m, name=None, globs=None, verbose=None, isprivate=None,
- report=1):
- """m, name=None, globs=None, verbose=None, isprivate=None, report=1
-
- Test examples in docstrings in functions and classes reachable from
- module m, starting with m.__doc__. Private names are skipped.
-
- Also test examples reachable from dict m.__test__ if it exists and is
- not None. m.__dict__ maps names to functions, classes and strings;
- function and class docstrings are tested even if the name is private;
- strings are tested directly, as if they were docstrings.
-
- Return (#failures, #tests).
-
- See doctest.__doc__ for an overview.
-
- Optional keyword arg "name" gives the name of the module; by default
- use m.__name__.
-
- Optional keyword arg "globs" gives a dict to be used as the globals
- when executing examples; by default, use m.__dict__. A copy of this
- dict is actually used for each docstring, so that each docstring's
- examples start with a clean slate.
-
- Optional keyword arg "verbose" prints lots of stuff if true, prints
- only failures if false; by default, it's true iff "-v" is in sys.argv.
-
- Optional keyword arg "isprivate" specifies a function used to
- determine whether a name is private. The default function is
- doctest.is_private; see its docs for details.
-
- Optional keyword arg "report" prints a summary at the end when true,
- else prints nothing at the end. In verbose mode, the summary is
- detailed, else very brief (in fact, empty if all tests passed).
-
- Advanced tomfoolery: testmod runs methods of a local instance of
- class doctest.Tester, then merges the results into (or creates)
- global Tester instance doctest.master. Methods of doctest.master
- can be called directly too, if you want to do something unusual.
- Passing report=0 to testmod is especially useful then, to delay
- displaying a summary. Invoke doctest.master.summarize(verbose)
- when you're done fiddling.
- """
-
- global master
-
- if type(m) is not _ModuleType:
- raise TypeError("testmod: module required; " + `m`)
- if name is None:
- name = m.__name__
- tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate)
- failures, tries = tester.rundoc(m, name)
- f, t = tester.rundict(m.__dict__, name)
- failures = failures + f
- tries = tries + t
- if hasattr(m, "__test__"):
- testdict = m.__test__
- if testdict:
- if not hasattr(testdict, "items"):
- raise TypeError("testmod: module.__test__ must support "
- ".items(); " + `testdict`)
- f, t = tester.run__test__(testdict, name + ".__test__")
- failures = failures + f
- tries = tries + t
- if report:
- tester.summarize()
- if master is None:
- master = tester
- else:
- master.merge(tester)
- return failures, tries
-
-class _TestClass:
- """
- A pointless class, for sanity-checking of docstring testing.
-
- Methods:
- square()
- get()
-
- >>> _TestClass(13).get() + _TestClass(-12).get()
- 1
- >>> hex(_TestClass(13).square().get())
- '0xa9'
- """
-
- def __init__(self, val):
- """val -> _TestClass object with associated value val.
-
- >>> t = _TestClass(123)
- >>> print t.get()
- 123
- """
-
- self.val = val
-
- def square(self):
- """square() -> square TestClass's associated value
-
- >>> _TestClass(13).square().get()
- 169
- """
-
- self.val = self.val ** 2
- return self
-
- def get(self):
- """get() -> return TestClass's associated value.
-
- >>> x = _TestClass(-42)
- >>> print x.get()
- -42
- """
-
- return self.val
-
-__test__ = {"_TestClass": _TestClass,
- "string": r"""
- Example of a string object, searched as-is.
- >>> x = 1; y = 2
- >>> x + y, x * y
- (3, 2)
- """
- }
-
-def _test():
- import doctest
- return doctest.testmod(doctest)
-
-if __name__ == "__main__":
- _test()
diff --git a/encode.c b/encode.c
index 2ae13ad72..8be44d8ec 100644
--- a/encode.c
+++ b/encode.c
@@ -15,7 +15,7 @@
* 1999-02-07 fl Added PCX encoder
*
* Copyright (c) 1997-2001 by Secret Labs AB
- * Copyright (c) 1996-1997 by Fredrik Lundh
+ * Copyright (c) 1996-1997 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
*/
@@ -24,12 +24,8 @@
#include "Python.h"
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
#include "Imaging.h"
+#include "py3.h"
#include "Gif.h"
#ifdef HAVE_UNISTD_H
@@ -49,7 +45,7 @@ typedef struct {
PyObject* lock;
} ImagingEncoderObject;
-staticforward PyTypeObject ImagingEncoderType;
+static PyTypeObject ImagingEncoderType;
static ImagingEncoderObject*
PyImaging_EncoderNew(int contextsize)
@@ -57,7 +53,8 @@ PyImaging_EncoderNew(int contextsize)
ImagingEncoderObject *encoder;
void *context;
- ImagingEncoderType.ob_type = &PyType_Type;
+ if(!PyType_Ready(&ImagingEncoderType) < 0)
+ return NULL;
encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType);
if (encoder == NULL)
@@ -96,7 +93,7 @@ _dealloc(ImagingEncoderObject* encoder)
PyObject_Del(encoder);
}
-static PyObject*
+static PyObject*
_encode(ImagingEncoderObject* encoder, PyObject* args)
{
PyObject* buf;
@@ -110,15 +107,15 @@ _encode(ImagingEncoderObject* encoder, PyObject* args)
if (!PyArg_ParseTuple(args, "|i", &bufsize))
return NULL;
- buf = PyString_FromStringAndSize(NULL, bufsize);
+ buf = PyBytes_FromStringAndSize(NULL, bufsize);
if (!buf)
return NULL;
status = encoder->encode(encoder->im, &encoder->state,
- (UINT8*) PyString_AsString(buf), bufsize);
+ (UINT8*) PyBytes_AsString(buf), bufsize);
/* adjust string length to avoid slicing in encoder */
- if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0)
+ if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0)
return NULL;
result = Py_BuildValue("iiO", status, encoder->state.errcode, buf);
@@ -128,7 +125,7 @@ _encode(ImagingEncoderObject* encoder, PyObject* args)
return result;
}
-static PyObject*
+static PyObject*
_encode_to_file(ImagingEncoderObject* encoder, PyObject* args)
{
UINT8* buf;
@@ -241,26 +238,38 @@ static struct PyMethodDef methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_getattr(ImagingEncoderObject* self, char* name)
-{
- return Py_FindMethod(methods, (PyObject*) self, name);
-}
-
-statichere PyTypeObject ImagingEncoderType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
+static PyTypeObject ImagingEncoderType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
"ImagingEncoder", /*tp_name*/
sizeof(ImagingEncoderObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_hash*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
/* -------------------------------------------------------------------- */
@@ -436,11 +445,27 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
int optimize = 0;
+ int compress_level = -1;
+ int compress_type = -1;
char* dictionary = NULL;
int dictionary_size = 0;
- if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize,
- &dictionary, &dictionary_size))
- return NULL;
+ if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode,
+ &optimize,
+ &compress_level, &compress_type,
+ &dictionary, &dictionary_size))
+ return NULL;
+
+ /* Copy to avoid referencing Python's memory, but there's no mechanism to
+ free this memory later, so this function (and several others here)
+ leaks. */
+ if (dictionary && dictionary_size > 0) {
+ char* p = malloc(dictionary_size);
+ if (!p)
+ return PyErr_NoMemory();
+ memcpy(p, dictionary, dictionary_size);
+ dictionary = p;
+ } else
+ dictionary = NULL;
encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
if (encoder == NULL)
@@ -456,6 +481,8 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE;
((ZIPSTATE*)encoder->state.context)->optimize = optimize;
+ ((ZIPSTATE*)encoder->state.context)->compress_level = compress_level;
+ ((ZIPSTATE*)encoder->state.context)->compress_type = compress_type;
((ZIPSTATE*)encoder->state.context)->dictionary = dictionary;
((ZIPSTATE*)encoder->state.context)->dictionary_size = dictionary_size;
@@ -485,6 +512,69 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
#include "Jpeg.h"
+static unsigned int** get_qtables_arrays(PyObject* qtables) {
+ PyObject* tables;
+ PyObject* table;
+ PyObject* table_data;
+ int i, j, num_tables;
+ unsigned int **qarrays;
+
+ if ((qtables == NULL) || (qtables == Py_None)) {
+ return NULL;
+ }
+
+ if (!PySequence_Check(qtables)) {
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
+ return NULL;
+ }
+
+ tables = PySequence_Fast(qtables, "expected a sequence");
+ num_tables = PySequence_Size(qtables);
+ if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) {
+ PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4.");
+ return NULL;
+ }
+ qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*));
+ if (!qarrays) {
+ Py_DECREF(tables);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (i = 0; i < num_tables; i++) {
+ table = PySequence_Fast_GET_ITEM(tables, i);
+ if (!PySequence_Check(table)) {
+ Py_DECREF(tables);
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
+ return NULL;
+ }
+ if (PySequence_Size(table) != DCTSIZE2) {
+ Py_DECREF(tables);
+ PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
+ return NULL;
+ }
+ table_data = PySequence_Fast(table, "expected a sequence");
+ qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int));
+ if (!qarrays[i]) {
+ Py_DECREF(tables);
+ PyErr_NoMemory();
+ return NULL;
+ }
+ for (j = 0; j < DCTSIZE2; j++) {
+ qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
+ }
+ }
+
+ Py_DECREF(tables);
+
+ if (PyErr_Occurred()) {
+ PyMem_Free(qarrays);
+ qarrays = NULL;
+ }
+
+ return qarrays;
+}
+
+
PyObject*
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
{
@@ -499,10 +589,18 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
int xdpi = 0, ydpi = 0;
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
- char* extra = NULL; int extra_size;
- if (!PyArg_ParseTuple(args, "ss|iiiiiiiis#", &mode, &rawmode, &quality,
- &progressive, &smooth, &optimize, &streamtype,
- &xdpi, &ydpi, &subsampling, &extra, &extra_size))
+ PyObject* qtables=NULL;
+ unsigned int **qarrays = NULL;
+ char* extra = NULL;
+ int extra_size;
+ char* rawExif = NULL;
+ int rawExifLen = 0;
+
+ if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
+ &mode, &rawmode, &quality,
+ &progressive, &smooth, &optimize, &streamtype,
+ &xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size,
+ &rawExif, &rawExifLen))
return NULL;
encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE));
@@ -512,6 +610,8 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
if (get_packer(encoder, mode, rawmode) < 0)
return NULL;
+ qarrays = get_qtables_arrays(qtables);
+
if (extra && extra_size > 0) {
char* p = malloc(extra_size);
if (!p)
@@ -521,9 +621,19 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
} else
extra = NULL;
+ if (rawExif && rawExifLen > 0) {
+ char* pp = malloc(rawExifLen);
+ if (!pp)
+ return PyErr_NoMemory();
+ memcpy(pp, rawExif, rawExifLen);
+ rawExif = pp;
+ } else
+ rawExif = NULL;
+
encoder->encode = ImagingJpegEncode;
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality;
+ ((JPEGENCODERSTATE*)encoder->state.context)->qtables = qarrays;
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling;
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive;
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth;
@@ -533,8 +643,153 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi;
((JPEGENCODERSTATE*)encoder->state.context)->extra = extra;
((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size;
+ ((JPEGENCODERSTATE*)encoder->state.context)->rawExif = rawExif;
+ ((JPEGENCODERSTATE*)encoder->state.context)->rawExifLen = rawExifLen;
return (PyObject*) encoder;
}
#endif
+
+/* -------------------------------------------------------------------- */
+/* LibTiff */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBTIFF
+
+#include "TiffDecode.h"
+
+#include
+#ifdef __WIN32__
+#define strcasecmp(s1, s2) stricmp(s1, s2)
+#endif
+
+PyObject*
+PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char* mode;
+ char* rawmode;
+ char* compname;
+ char* filename;
+ int fp;
+
+ PyObject *dir;
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ int status;
+
+ Py_ssize_t d_size;
+ PyObject *keys, *values;
+
+
+ if (! PyArg_ParseTuple(args, "sssisO", &mode, &rawmode, &compname, &fp, &filename, &dir)) {
+ return NULL;
+ }
+
+ if (!PyDict_Check(dir)) {
+ PyErr_SetString(PyExc_ValueError, "Invalid Dictionary");
+ return NULL;
+ } else {
+ d_size = PyDict_Size(dir);
+ TRACE(("dict size: %d\n", (int)d_size));
+ keys = PyDict_Keys(dir);
+ values = PyDict_Values(dir);
+ for (pos=0;posstate, filename, fp)) {
+ Py_DECREF(encoder);
+ PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
+ return NULL;
+ }
+
+ // While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t
+ // while (PyDict_Next(dir, &pos, &key, &value)) {
+ for (pos=0;posstate,
+ (ttag_t) PyInt_AsLong(key),
+ PyInt_AsLong(value));
+ } else if(PyBytes_Check(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;
+ int *intav;
+ TRACE(("Setting from List: %d \n", (int)PyInt_AsLong(key)));
+ len = (int)PyList_Size(value);
+ if (len) {
+ if (PyInt_Check(PyList_GetItem(value,0))) {
+ TRACE((" %d elements, setting as ints \n", len));
+ intav = malloc(sizeof(int)*len);
+ if (intav) {
+ for (i=0;istate,
+ (ttag_t) PyInt_AsLong(key),
+ intav);
+ free(intav);
+ }
+ } else {
+ TRACE((" %d elements, setting as floats \n", len));
+ floatav = malloc(sizeof(float)*len);
+ if (floatav) {
+ for (i=0;istate,
+ (ttag_t) PyInt_AsLong(key),
+ floatav);
+ free(floatav);
+ }
+ }
+ }
+ } else if (PyFloat_Check(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 \n",
+ (int)PyInt_AsLong(key),
+ PyBytes_AsString(PyObject_Str(value))));
+ }
+ if (!status) {
+ TRACE(("Error setting Field\n"));
+ Py_DECREF(encoder);
+ PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary");
+ return NULL;
+ }
+ }
+
+ encoder->encode = ImagingLibTiffEncode;
+
+ return (PyObject*) encoder;
+}
+
+#endif
+
diff --git a/libImaging/Access.c b/libImaging/Access.c
index 5ebc9b6f3..70eb1af4c 100644
--- a/libImaging/Access.c
+++ b/libImaging/Access.c
@@ -17,7 +17,7 @@
#define ACCESS_TABLE_HASH 30197
static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE];
-
+
static inline UINT32
hash(const char* mode)
{
@@ -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/AlphaComposite.c b/libImaging/AlphaComposite.c
new file mode 100644
index 000000000..9433fae53
--- /dev/null
+++ b/libImaging/AlphaComposite.c
@@ -0,0 +1,98 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * Alpha composite imSrc over imDst.
+ * http://en.wikipedia.org/wiki/Alpha_compositing
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+typedef struct
+{
+ UINT8 r;
+ UINT8 g;
+ UINT8 b;
+ UINT8 a;
+} rgba8;
+
+
+
+Imaging
+ImagingAlphaComposite(Imaging imDst, Imaging imSrc)
+{
+ Imaging imOut;
+ int x, y;
+
+ /* Check arguments */
+ if (!imDst || !imSrc ||
+ strcmp(imDst->mode, "RGBA") ||
+ imDst->type != IMAGING_TYPE_UINT8 ||
+ imDst->bands != 4)
+ return ImagingError_ModeError();
+
+ if (strcmp(imDst->mode, imSrc->mode) ||
+ imDst->type != imSrc->type ||
+ imDst->bands != imSrc->bands ||
+ imDst->xsize != imSrc->xsize ||
+ imDst->ysize != imSrc->ysize)
+ return ImagingError_Mismatch();
+
+ imOut = ImagingNew(imDst->mode, imDst->xsize, imDst->ysize);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, imDst);
+
+ for (y = 0; y < imDst->ysize; y++) {
+
+ rgba8* dst = (rgba8*) imDst->image[y];
+ rgba8* src = (rgba8*) imSrc->image[y];
+ rgba8* out = (rgba8*) imOut->image[y];
+
+ for (x = 0; x < imDst->xsize; x ++) {
+
+ if (src->a == 0) {
+ // Copy 4 bytes at once.
+ *out = *dst;
+ } else {
+ // Integer implementation with increased precision.
+ // Each variable has extra meaningful bits.
+ // Divisions are rounded.
+
+ // This code uses trick from Paste.c:
+ // (a + (2 << (n-1)) - 1) / ((2 << n)-1)
+ // almost equivalent to:
+ // tmp = a + (2 << (n-1)), ((tmp >> n) + tmp) >> n
+
+ UINT32 tmpr, tmpg, tmpb;
+ UINT16 blend = dst->a * (255 - src->a);
+ UINT16 outa255 = src->a * 255 + blend;
+ // There we use 7 bits for precision.
+ // We could use more, but we go beyond 32 bits.
+ UINT16 coef1 = src->a * 255 * 255 * 128 / outa255;
+ UINT16 coef2 = 255 * 128 - coef1;
+
+ #define SHIFTFORDIV255(a)\
+ ((((a) >> 8) + a) >> 8)
+
+ tmpr = src->r * coef1 + dst->r * coef2 + (0x80 << 7);
+ out->r = SHIFTFORDIV255(tmpr) >> 7;
+ tmpg = src->g * coef1 + dst->g * coef2 + (0x80 << 7);
+ out->g = SHIFTFORDIV255(tmpg) >> 7;
+ tmpb = src->b * coef1 + dst->b * coef2 + (0x80 << 7);
+ out->b = SHIFTFORDIV255(tmpb) >> 7;
+ out->a = SHIFTFORDIV255(outa255 + 0x80);
+ }
+
+ dst++; src++; out++;
+ }
+
+ }
+
+ return imOut;
+}
diff --git a/libImaging/Antialias.c b/libImaging/Antialias.c
index 53dfa3522..d413fbb6a 100644
--- a/libImaging/Antialias.c
+++ b/libImaging/Antialias.c
@@ -2,7 +2,7 @@
* The Python Imaging Library
* $Id$
*
- * pilopen antialiasing support
+ * pilopen antialiasing support
*
* history:
* 2002-03-09 fl Created (for PIL 1.1.3)
@@ -133,7 +133,7 @@ ImagingStretch(Imaging imOut, Imaging imIn, int filter)
filterscale = 1.0;
support = 0.5;
}
-
+
support = support * filterscale;
/* coefficient buffer (with rounding safety margin) */
diff --git a/libImaging/Bands.c b/libImaging/Bands.c
index fdfc6ae95..cc8d634dd 100644
--- a/libImaging/Bands.c
+++ b/libImaging/Bands.c
@@ -1,7 +1,7 @@
-/*
+/*
* The Python Imaging Library
* $Id$
- *
+ *
* stuff to extract and paste back individual bands
*
* history:
diff --git a/libImaging/BitDecode.c b/libImaging/BitDecode.c
index 572926f00..a78183542 100644
--- a/libImaging/BitDecode.c
+++ b/libImaging/BitDecode.c
@@ -75,7 +75,7 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
bitstate->bitbuffer = (bitstate->bitbuffer << 8) | byte;
bitstate->bitcount += 8;
-
+
while (bitstate->bitcount >= bitstate->bits) {
/* get a pixel from the bit buffer */
@@ -127,7 +127,7 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
return -1;
}
state->x = 0;
- /* reset bit buffer */
+ /* reset bit buffer */
if (bitstate->pad > 0)
bitstate->bitcount = 0;
}
diff --git a/libImaging/Blend.c b/libImaging/Blend.c
index 0861c8ef4..885a1bb82 100644
--- a/libImaging/Blend.c
+++ b/libImaging/Blend.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/Chops.c b/libImaging/Chops.c
index e5993195a..73d961231 100644
--- a/libImaging/Chops.c
+++ b/libImaging/Chops.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/Convert.c b/libImaging/Convert.c
index 25299aa82..631263b31 100644
--- a/libImaging/Convert.c
+++ b/libImaging/Convert.c
@@ -1,7 +1,7 @@
-/*
+/*
* The Python Imaging Library
* $Id$
- *
+ *
* convert images
*
* history:
@@ -39,8 +39,8 @@
#define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v))
/* like (a * b + 127) / 255), but much faster on most platforms */
-#define MULDIV255(a, b, tmp)\
- (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
+#define MULDIV255(a, b, tmp)\
+ (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
#define L(rgb)\
@@ -55,7 +55,7 @@ bit2l(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++)
- *out++ = (*in++ != 0) ? 255 : 0;
+ *out++ = (*in++ != 0) ? 255 : 0;
}
static void
@@ -64,10 +64,10 @@ bit2rgb(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = (*in++ != 0) ? 255 : 0;
- *out++ = v;
- *out++ = v;
- *out++ = v;
- *out++ = 255;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
}
}
@@ -76,10 +76,10 @@ bit2cmyk(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++) {
- *out++ = 0;
- *out++ = 0;
- *out++ = 0;
- *out++ = (*in++ != 0) ? 0 : 255;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = (*in++ != 0) ? 0 : 255;
}
}
@@ -88,7 +88,7 @@ bit2ycbcr(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++) {
- *out++ = (*in++ != 0) ? 255 : 0;
+ *out++ = (*in++ != 0) ? 255 : 0;
*out++ = 128;
*out++ = 128;
*out++ = 255;
@@ -104,7 +104,7 @@ l2bit(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++)
- *out++ = (*in++ >= 128) ? 255 : 0;
+ *out++ = (*in++ >= 128) ? 255 : 0;
}
static void
@@ -113,10 +113,10 @@ l2la(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = *in++;
- *out++ = v;
- *out++ = v;
- *out++ = v;
- *out++ = 255;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
}
}
@@ -126,10 +126,10 @@ l2rgb(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++) {
UINT8 v = *in++;
- *out++ = v;
- *out++ = v;
- *out++ = v;
- *out++ = 255;
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = 255;
}
}
@@ -138,7 +138,7 @@ la2l(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4)
- *out++ = in[0];
+ *out++ = in[0];
}
static void
@@ -147,10 +147,10 @@ la2rgb(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++, in += 4) {
UINT8 v = in[0];
- *out++ = v;
- *out++ = v;
- *out++ = v;
- *out++ = in[3];
+ *out++ = v;
+ *out++ = v;
+ *out++ = v;
+ *out++ = in[3];
}
}
@@ -159,8 +159,8 @@ rgb2bit(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4)
- /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
- *out++ = (L(in) >= 128000) ? 255 : 0;
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ *out++ = (L(in) >= 128000) ? 255 : 0;
}
static void
@@ -168,8 +168,8 @@ rgb2l(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4)
- /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
- *out++ = L(in) / 1000;
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ *out++ = L(in) / 1000;
}
static void
@@ -177,8 +177,8 @@ rgb2la(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
- /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
- out[0] = out[1] = out[2] = L(in) / 1000;
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ out[0] = out[1] = out[2] = L(in) / 1000;
out[3] = 255;
}
}
@@ -189,7 +189,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize)
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++, in += 4)
- *out++ = L(in) / 1000;
+ *out++ = L(in) / 1000;
}
static void
@@ -198,7 +198,7 @@ rgb2f(UINT8* out_, const UINT8* in, int xsize)
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++, in += 4)
- *out++ = (float) L(in) / 1000.0F;
+ *out++ = (float) L(in) / 1000.0F;
}
static void
@@ -207,7 +207,7 @@ rgb2bgr15(UINT8* out_, const UINT8* in, int xsize)
int x;
UINT16* out = (UINT16*) out_;
for (x = 0; x < xsize; x++, in += 4)
- *out++ =
+ *out++ =
((((UINT16)in[0])<<7)&0x7c00) +
((((UINT16)in[1])<<2)&0x03e0) +
((((UINT16)in[2])>>3)&0x001f);
@@ -219,7 +219,7 @@ rgb2bgr16(UINT8* out_, const UINT8* in, int xsize)
int x;
UINT16* out = (UINT16*) out_;
for (x = 0; x < xsize; x++, in += 4)
- *out++ =
+ *out++ =
((((UINT16)in[0])<<8)&0xf800) +
((((UINT16)in[1])<<3)&0x07e0) +
((((UINT16)in[2])>>3)&0x001f);
@@ -230,9 +230,9 @@ rgb2bgr24(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4) {
- *out++ = in[2];
- *out++ = in[1];
- *out++ = in[0];
+ *out++ = in[2];
+ *out++ = in[1];
+ *out++ = in[0];
}
}
@@ -257,8 +257,8 @@ rgba2la(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4, out += 4) {
- /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
- out[0] = out[1] = out[2] = L(in) / 1000;
+ /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+ out[0] = out[1] = out[2] = L(in) / 1000;
out[3] = in[3];
}
}
@@ -289,6 +289,57 @@ rgba2rgba(UINT8* out, const UINT8* in, int xsize)
}
}
+/* RGBa -> RGBA conversion to remove premultiplication
+ Needed for correct transforms/resizing on RGBA images */
+static void
+rgba2rgbA(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ unsigned int alpha;
+ for (x = 0; x < xsize; x++, in+=4) {
+ alpha = in[3];
+ if (alpha) {
+ *out++ = CLIP((255 * in[0]) / alpha);
+ *out++ = CLIP((255 * in[1]) / alpha);
+ *out++ = CLIP((255 * in[2]) / alpha);
+ } else {
+ *out++ = in[0];
+ *out++ = in[1];
+ *out++ = in[2];
+ }
+ *out++ = in[3];
+
+ }
+}
+
+/*
+ * Conversion of RGB + single transparent color to RGBA,
+ * where any pixel that matches the color will have the
+ * alpha channel set to 0
+ */
+
+static void
+rgbT2rgba(UINT8* out, int xsize, int r, int g, int b)
+{
+#ifdef WORDS_BIGENDIAN
+ UINT32 trns = ((r & 0xff)<<24) | ((g & 0xff)<<16) | ((b & 0xff)<<8) | 0xff;
+ UINT32 repl = trns & 0xffffff00;
+#else
+ UINT32 trns = (0xff <<24) | ((b & 0xff)<<16) | ((g & 0xff)<<8) | (r & 0xff);
+ UINT32 repl = trns & 0x00ffffff;
+#endif
+
+ UINT32* tmp = (UINT32 *)out;
+ int i;
+
+ for (i=0; i < xsize; i++ ,tmp++) {
+ if (tmp[0]==trns) {
+ tmp[0]=repl;
+ }
+ }
+}
+
+
/* ---------------- */
/* CMYK conversions */
/* ---------------- */
@@ -310,7 +361,7 @@ rgb2cmyk(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++) {
- /* Note: no undercolour removal */
+ /* Note: no undercolour removal */
*out++ = ~(*in++);
*out++ = ~(*in++);
*out++ = ~(*in++);
@@ -324,9 +375,9 @@ cmyk2rgb(UINT8* out, const UINT8* in, int xsize)
int x;
for (x = 0; x < xsize; x++, in += 4) {
*out++ = CLIP(255 - (in[0] + in[3]));
- *out++ = CLIP(255 - (in[1] + in[3]));
- *out++ = CLIP(255 - (in[2] + in[3]));
- *out++ = 255;
+ *out++ = CLIP(255 - (in[1] + in[3]));
+ *out++ = CLIP(255 - (in[2] + in[3]));
+ *out++ = 255;
}
}
@@ -340,7 +391,7 @@ bit2i(UINT8* out_, const UINT8* in, int xsize)
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (*in++ != 0) ? 255 : 0;
+ *out++ = (*in++ != 0) ? 255 : 0;
}
static void
@@ -349,7 +400,7 @@ l2i(UINT8* out_, const UINT8* in, int xsize)
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (INT32) *in++;
+ *out++ = (INT32) *in++;
}
static void
@@ -374,7 +425,7 @@ i2f(UINT8* out_, const UINT8* in_, int xsize)
INT32* in = (INT32*) in_;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (FLOAT32) *in++;
+ *out++ = (FLOAT32) *in++;
}
/* ------------- */
@@ -387,7 +438,7 @@ bit2f(UINT8* out_, const UINT8* in, int xsize)
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (*in++ != 0) ? 255.0F : 0.0F;
+ *out++ = (*in++ != 0) ? 255.0F : 0.0F;
}
static void
@@ -396,7 +447,7 @@ l2f(UINT8* out_, const UINT8* in, int xsize)
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (FLOAT32) *in++;
+ *out++ = (FLOAT32) *in++;
}
static void
@@ -421,7 +472,7 @@ f2i(UINT8* out_, const UINT8* in_, int xsize)
FLOAT32* in = (FLOAT32*) in_;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (INT32) *in++;
+ *out++ = (INT32) *in++;
}
/* ----------------- */
@@ -435,10 +486,10 @@ l2ycbcr(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++) {
- *out++ = *in++;
- *out++ = 128;
- *out++ = 128;
- *out++ = 255;
+ *out++ = *in++;
+ *out++ = 128;
+ *out++ = 128;
+ *out++ = 255;
}
}
@@ -447,7 +498,7 @@ ycbcr2l(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 4)
- *out++ = in[0];
+ *out++ = in[0];
}
/* ------------------------- */
@@ -498,6 +549,25 @@ I16B_I(UINT8* out_, const UINT8* in, int xsize)
*out++ = in[1] + ((int) in[0] << 8);
}
+static void
+I16L_F(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = (FLOAT32) (in[0] + ((int) in[1] << 8));
+}
+
+
+static void
+I16B_F(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = (FLOAT32) (in[1] + ((int) in[0] << 8));
+}
+
static void
L_I16L(UINT8* out, const UINT8* in, int xsize)
{
@@ -600,6 +670,8 @@ static struct {
{ "RGBA", "CMYK", rgb2cmyk },
{ "RGBA", "YCbCr", ImagingConvertRGB2YCbCr },
+ { "RGBa", "RGBA", rgba2rgbA },
+
{ "RGBX", "1", rgb2bit },
{ "RGBX", "L", rgb2l },
{ "RGBA", "I", rgb2i },
@@ -630,6 +702,10 @@ static struct {
{ "L", "I;16B", L_I16B },
{ "I;16B", "L", I16B_L },
+ { "I;16", "F", I16L_F },
+ { "I;16L", "F", I16L_F },
+ { "I;16B", "F", I16B_F },
+
{ NULL }
};
@@ -645,7 +721,7 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
int x;
/* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++)
- *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0;
+ *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0;
}
static void
@@ -654,7 +730,7 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
int x;
/* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++)
- *out++ = L(&palette[in[x]*4]) / 1000;
+ *out++ = L(&palette[in[x]*4]) / 1000;
}
static void
@@ -663,7 +739,7 @@ pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
int x;
/* FIXME: precalculate greyscale palette? */
for (x = 0; x < xsize; x++, in += 2) {
- *out++ = L(&palette[in[0]*4]) / 1000;
+ *out++ = L(&palette[in[0]*4]) / 1000;
*out++ = in[1];
}
}
@@ -674,7 +750,7 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = L(&palette[in[x]*4]) / 1000;
+ *out++ = L(&palette[in[x]*4]) / 1000;
}
static void
@@ -683,7 +759,7 @@ p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette)
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++)
- *out++ = (float) L(&palette[in[x]*4]) / 1000.0F;
+ *out++ = (float) L(&palette[in[x]*4]) / 1000.0F;
}
static void
@@ -691,11 +767,11 @@ p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++) {
- const UINT8* rgb = &palette[*in++ * 4];
- *out++ = rgb[0];
- *out++ = rgb[1];
- *out++ = rgb[2];
- *out++ = 255;
+ const UINT8* rgb = &palette[*in++ * 4];
+ *out++ = rgb[0];
+ *out++ = rgb[1];
+ *out++ = rgb[2];
+ *out++ = 255;
}
}
@@ -704,11 +780,11 @@ p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++) {
- const UINT8* rgba = &palette[*in++ * 4];
- *out++ = rgba[0];
- *out++ = rgba[1];
- *out++ = rgba[2];
- *out++ = rgba[3];
+ const UINT8* rgba = &palette[*in++ * 4];
+ *out++ = rgba[0];
+ *out++ = rgba[1];
+ *out++ = rgba[2];
+ *out++ = rgba[3];
}
}
@@ -717,11 +793,11 @@ pa2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
{
int x;
for (x = 0; x < xsize; x++, in += 4) {
- const UINT8* rgb = &palette[in[0] * 4];
- *out++ = rgb[0];
- *out++ = rgb[1];
- *out++ = rgb[2];
- *out++ = in[3];
+ const UINT8* rgb = &palette[in[0] * 4];
+ *out++ = rgb[0];
+ *out++ = rgb[1];
+ *out++ = rgb[2];
+ *out++ = in[3];
}
}
@@ -750,32 +826,32 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
/* Map palette image to L, RGB, RGBA, or CMYK */
if (!imIn->palette)
- return (Imaging) ImagingError_ValueError("no palette");
+ return (Imaging) ImagingError_ValueError("no palette");
alpha = !strcmp(imIn->mode, "PA");
if (strcmp(mode, "1") == 0)
- convert = p2bit;
+ convert = p2bit;
else if (strcmp(mode, "L") == 0)
- convert = p2l;
+ convert = p2l;
else if (strcmp(mode, "LA") == 0)
- convert = (alpha) ? pa2la : p2l;
+ convert = (alpha) ? pa2la : p2l;
else if (strcmp(mode, "I") == 0)
- convert = p2i;
+ convert = p2i;
else if (strcmp(mode, "F") == 0)
- convert = p2f;
+ convert = p2f;
else if (strcmp(mode, "RGB") == 0)
- convert = p2rgb;
+ convert = p2rgb;
else if (strcmp(mode, "RGBA") == 0)
- convert = (alpha) ? pa2rgba : p2rgba;
+ convert = (alpha) ? pa2rgba : p2rgba;
else if (strcmp(mode, "RGBX") == 0)
- convert = p2rgba;
+ convert = p2rgba;
else if (strcmp(mode, "CMYK") == 0)
- convert = p2cmyk;
+ convert = p2cmyk;
else if (strcmp(mode, "YCbCr") == 0)
- convert = p2ycbcr;
+ convert = p2ycbcr;
else
- return (Imaging) ImagingError_ValueError("conversion not supported");
+ return (Imaging) ImagingError_ValueError("conversion not supported");
imOut = ImagingNew2(mode, imOut, imIn);
if (!imOut)
@@ -783,13 +859,16 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode)
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++)
- (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
- imIn->xsize, imIn->palette->palette);
+ (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize, imIn->palette->palette);
ImagingSectionLeave(&cookie);
return imOut;
}
+#if defined(_MSC_VER) && (_MSC_VER == 1600)
+#pragma optimize("", off)
+#endif
static Imaging
topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
{
@@ -799,23 +878,23 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
/* Map L or RGB/RGBX/RGBA to palette image */
if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0)
- return (Imaging) ImagingError_ValueError("conversion not supported");
+ return (Imaging) ImagingError_ValueError("conversion not supported");
if (palette == NULL) {
/* FIXME: make user configurable */
if (imIn->bands == 1)
- palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */
+ palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */
else
- palette = ImagingPaletteNewBrowser(); /* Standard colour cube */
+ palette = ImagingPaletteNewBrowser(); /* Standard colour cube */
}
if (!palette)
- return (Imaging) ImagingError_ValueError("no palette");
+ return (Imaging) ImagingError_ValueError("no palette");
imOut = ImagingNew2("P", imOut, imIn);
if (!imOut) {
if (palette != inpalette)
- ImagingPaletteDelete(palette);
+ ImagingPaletteDelete(palette);
return NULL;
}
@@ -823,24 +902,24 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
imOut->palette = ImagingPaletteDuplicate(palette);
if (imIn->bands == 1) {
- /* greyscale image */
+ /* greyscale image */
- /* Greyscale palette: copy data as is */
+ /* Greyscale palette: copy data as is */
ImagingSectionEnter(&cookie);
- for (y = 0; y < imIn->ysize; y++)
- memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
+ for (y = 0; y < imIn->ysize; y++)
+ memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
ImagingSectionLeave(&cookie);
} else {
- /* colour image */
+ /* colour image */
- /* Create mapping cache */
- if (ImagingPaletteCachePrepare(palette) < 0) {
- ImagingDelete(imOut);
- if (palette != inpalette)
- ImagingPaletteDelete(palette);
- return NULL;
- }
+ /* Create mapping cache */
+ if (ImagingPaletteCachePrepare(palette) < 0) {
+ ImagingDelete(imOut);
+ if (palette != inpalette)
+ ImagingPaletteDelete(palette);
+ return NULL;
+ }
if (dither) {
/* floyd-steinberg dither */
@@ -929,8 +1008,8 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
ImagingSectionLeave(&cookie);
}
- if (inpalette != palette)
- ImagingPaletteCacheDelete(palette);
+ if (inpalette != palette)
+ ImagingPaletteCacheDelete(palette);
}
if (inpalette != palette)
@@ -948,7 +1027,7 @@ tobilevel(Imaging imOut, Imaging imIn, int dither)
/* Map L or RGB to dithered 1 image */
if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0)
- return (Imaging) ImagingError_ValueError("conversion not supported");
+ return (Imaging) ImagingError_ValueError("conversion not supported");
imOut = ImagingNew2("1", imOut, imIn);
if (!imOut)
@@ -1022,7 +1101,9 @@ tobilevel(Imaging imOut, Imaging imIn, int dither)
return imOut;
}
-
+#if defined(_MSC_VER) && (_MSC_VER == 1600)
+#pragma optimize("", on)
+#endif
static Imaging
convert(Imaging imOut, Imaging imIn, const char *mode,
@@ -1033,29 +1114,29 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
int y;
if (!imIn)
- return (Imaging) ImagingError_ModeError();
+ return (Imaging) ImagingError_ModeError();
if (!mode) {
- /* Map palette image to full depth */
- if (!imIn->palette)
- return (Imaging) ImagingError_ModeError();
- mode = imIn->palette->mode;
+ /* Map palette image to full depth */
+ if (!imIn->palette)
+ return (Imaging) ImagingError_ModeError();
+ mode = imIn->palette->mode;
} else
- /* Same mode? */
- if (!strcmp(imIn->mode, mode))
- return ImagingCopy2(imOut, imIn);
+ /* Same mode? */
+ if (!strcmp(imIn->mode, mode))
+ return ImagingCopy2(imOut, imIn);
/* test for special conversions */
if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0)
- return frompalette(imOut, imIn, mode);
-
+ return frompalette(imOut, imIn, mode);
+
if (strcmp(mode, "P") == 0)
- return topalette(imOut, imIn, palette, dither);
+ return topalette(imOut, imIn, palette, dither);
if (dither && strcmp(mode, "1") == 0)
- return tobilevel(imOut, imIn, dither);
+ return tobilevel(imOut, imIn, dither);
/* standard conversion machinery */
@@ -1063,15 +1144,15 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
convert = NULL;
for (y = 0; converters[y].from; y++)
- if (!strcmp(imIn->mode, converters[y].from) &&
- !strcmp(mode, converters[y].to)) {
- convert = converters[y].convert;
- break;
- }
+ if (!strcmp(imIn->mode, converters[y].from) &&
+ !strcmp(mode, converters[y].to)) {
+ convert = converters[y].convert;
+ break;
+ }
if (!convert)
#ifdef notdef
- return (Imaging) ImagingError_ValueError("conversion not supported");
+ return (Imaging) ImagingError_ValueError("conversion not supported");
#else
{
static char buf[256];
@@ -1087,8 +1168,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode,
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++)
- (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
- imIn->xsize);
+ (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize);
ImagingSectionLeave(&cookie);
return imOut;
@@ -1107,6 +1188,60 @@ ImagingConvert2(Imaging imOut, Imaging imIn)
return convert(imOut, imIn, imOut->mode, NULL, 0);
}
+
+Imaging
+ImagingConvertTransparent(Imaging imIn, const char *mode,
+ int r, int g, int b)
+{
+ ImagingSectionCookie cookie;
+ ImagingShuffler convert;
+ Imaging imOut = NULL;
+ int y;
+
+ if (!imIn){
+ return (Imaging) ImagingError_ModeError();
+ }
+
+ if (!((strcmp(imIn->mode, "RGB") == 0 ||
+ strcmp(imIn->mode, "L") == 0)
+ && strcmp(mode, "RGBA") == 0))
+#ifdef notdef
+ {
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+ }
+#else
+ {
+ static char buf[256];
+ /* FIXME: may overflow if mode is too large */
+ sprintf(buf, "conversion from %s to %s not supported in convert_transparent", imIn->mode, mode);
+ return (Imaging) ImagingError_ValueError(buf);
+ }
+#endif
+
+ if (strcmp(imIn->mode, "RGB") == 0) {
+ convert = rgb2rgba;
+ } else {
+ convert = l2rgb;
+ g = b = r;
+ }
+
+ imOut = ImagingNew2(mode, imOut, imIn);
+ if (!imOut){
+ return NULL;
+ }
+
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize);
+ rgbT2rgba((UINT8*) imOut->image[y], imIn->xsize, r, g, b);
+ }
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+
+}
+
Imaging
ImagingConvertInPlace(Imaging imIn, const char* mode)
{
@@ -1121,10 +1256,10 @@ ImagingConvertInPlace(Imaging imIn, const char* mode)
convert = bit2l;
else
return ImagingError_ModeError();
-
+
ImagingSectionEnter(&cookie);
for (y = 0; y < imIn->ysize; y++)
- (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y],
+ (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y],
imIn->xsize);
ImagingSectionLeave(&cookie);
diff --git a/libImaging/ConvertYCbCr.c b/libImaging/ConvertYCbCr.c
index 40cd01780..6ce549111 100644
--- a/libImaging/ConvertYCbCr.c
+++ b/libImaging/ConvertYCbCr.c
@@ -352,7 +352,7 @@ ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels)
y = (Y_R[r] + Y_G[g] + Y_B[b]) >> SCALE;
cb = ((Cb_R[r] + Cb_G[g] + Cb_B[b]) >> SCALE) + 128;
cr = ((Cr_R[r] + Cr_G[g] + Cr_B[b]) >> SCALE) + 128;
-
+
out[0] = (UINT8) y;
out[1] = (UINT8) cb;
out[2] = (UINT8) cr;
diff --git a/libImaging/Copy.c b/libImaging/Copy.c
index 4a1fc2489..b5b0b0aea 100644
--- a/libImaging/Copy.c
+++ b/libImaging/Copy.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/Crop.c b/libImaging/Crop.c
index 5acacd4c4..0db67e8e9 100644
--- a/libImaging/Crop.c
+++ b/libImaging/Crop.c
@@ -26,7 +26,7 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1)
int xsize, ysize;
int dx0, dy0, dx1, dy1;
INT32 zero = 0;
-
+
if (!imIn)
return (Imaging) ImagingError_ModeError();
diff --git a/libImaging/Dib.c b/libImaging/Dib.c
index 36dc24ef3..6db8c076e 100644
--- a/libImaging/Dib.c
+++ b/libImaging/Dib.c
@@ -126,7 +126,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize)
/* Bind a palette to it as well (only required for 8-bit DIBs) */
if (dib->pixelsize == 1) {
for (i = 0; i < 256; i++) {
- palette[i].rgbRed =
+ palette[i].rgbRed =
palette[i].rgbGreen =
palette[i].rgbBlue = i;
palette[i].rgbReserved = 0;
@@ -181,7 +181,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize)
i++;
}
for (r = 1; r < 22-1; r++) {
- /* Black and white are already provided by the cube. */
+ /* Black and white are already provided by the cube. */
pal->palPalEntry[i].peRed =
pal->palPalEntry[i].peGreen =
pal->palPalEntry[i].peBlue = r * 255 / (22-1);
diff --git a/libImaging/Draw.c b/libImaging/Draw.c
index bdf17e18a..f13ba4df0 100644
--- a/libImaging/Draw.c
+++ b/libImaging/Draw.c
@@ -436,7 +436,7 @@ polygon8(Imaging im, int n, Edge *e, int ink, int eofill)
for (;ymin <= ymax; ymin++) {
y = ymin+0.5F;
- for (i = j = 0; i < n; i++)
+ for (i = j = 0; i < n; i++)
if (y >= e[i].ymin && y <= e[i].ymax) {
if (e[i].d == 0)
hline8(im, e[i].xmin, ymin, e[i].xmax, ink);
@@ -590,7 +590,7 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1)
e->ymin = y0, e->ymax = y1;
else
e->ymin = y1, e->ymax = y0;
-
+
if (y0 == y1) {
e->d = 0;
e->dx = 0.0;
@@ -777,7 +777,7 @@ ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_,
draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink);
}
-
+
return 0;
}
@@ -861,7 +861,7 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1,
}
free(e);
-
+
} else {
for (i = start; i <= end; i++) {
@@ -1017,7 +1017,7 @@ ImagingOutlineLine(ImagingOutline outline, float x1, float y1)
outline->x = x1;
outline->y = y1;
-
+
return 0;
}
@@ -1061,13 +1061,13 @@ ImagingOutlineCurve(ImagingOutline outline, float x1, float y1,
outline->x = xo;
outline->y = yo;
-
+
return 0;
}
int
ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy,
- float x3, float y3)
+ float x3, float y3)
{
/* add bezier curve based on three control points (as
in the Flash file format) */
@@ -1095,13 +1095,13 @@ ImagingOutlineTransform(ImagingOutline outline, double a[6])
int i, n;
int x0, y0, x1, y1;
int X0, Y0, X1, Y1;
-
+
double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
eIn = outline->edges;
n = outline->count;
-
+
/* FIXME: ugly! */
outline->edges = NULL;
outline->count = outline->size = 0;
@@ -1113,12 +1113,12 @@ ImagingOutlineTransform(ImagingOutline outline, double a[6])
ImagingError_MemoryError();
return -1;
}
-
+
for (i = 0; i < n; i++) {
-
+
x0 = eIn->x0;
y0 = eIn->y0;
-
+
/* FIXME: ouch! */
if (eIn->x0 == eIn->xmin)
x1 = eIn->xmax;
diff --git a/libImaging/Effects.c b/libImaging/Effects.c
index 1b964ac40..db6e72989 100644
--- a/libImaging/Effects.c
+++ b/libImaging/Effects.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/EpsEncode.c b/libImaging/EpsEncode.c
index 704d5a656..45fab0a6e 100644
--- a/libImaging/EpsEncode.c
+++ b/libImaging/EpsEncode.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library.
* $Id$
*
@@ -76,5 +76,5 @@ ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
}
return ptr - buf;
-
+
}
diff --git a/libImaging/Except.c b/libImaging/Except.c
index 635435d57..515d85d1f 100644
--- a/libImaging/Except.c
+++ b/libImaging/Except.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/File.c b/libImaging/File.c
index 6f454d1ca..ac9ec3be1 100644
--- a/libImaging/File.c
+++ b/libImaging/File.c
@@ -57,7 +57,7 @@ ImagingOpenPPM(const char* infile)
x = y = max = 0;
- while (i < 3) {
+ while (i < 3) {
/* Ignore optional comment fields */
while (c == '\n') {
diff --git a/libImaging/Fill.c b/libImaging/Fill.c
index ce00e6c15..1e8dbf9d8 100644
--- a/libImaging/Fill.c
+++ b/libImaging/Fill.c
@@ -24,6 +24,7 @@ Imaging
ImagingFill(Imaging im, const void* colour)
{
int x, y;
+ ImagingSectionCookie cookie;
if (im->type == IMAGING_TYPE_SPECIAL) {
/* use generic API */
@@ -40,6 +41,7 @@ ImagingFill(Imaging im, const void* colour)
}
} else {
INT32 c = 0L;
+ ImagingSectionEnter(&cookie);
memcpy(&c, colour, im->pixelsize);
if (im->image32 && c != 0L) {
for (y = 0; y < im->ysize; y++)
@@ -50,6 +52,7 @@ ImagingFill(Imaging im, const void* colour)
for (y = 0; y < im->ysize; y++)
memset(im->image[y], cc, im->linesize);
}
+ ImagingSectionLeave(&cookie);
}
return im;
diff --git a/libImaging/Filter.c b/libImaging/Filter.c
index 45c9bf060..9079fbf88 100644
--- a/libImaging/Filter.c
+++ b/libImaging/Filter.c
@@ -31,7 +31,7 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode)
{
Imaging imOut;
int x, y;
-
+
if (xmargin < 0 && ymargin < 0)
return (Imaging) ImagingError_ValueError("bad kernel size");
diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c
index 41f535b56..0f59ee0d5 100644
--- a/libImaging/Geometry.c
+++ b/libImaging/Geometry.c
@@ -583,7 +583,7 @@ getfilter(Imaging im, int filterid)
Imaging
ImagingTransform(
- Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
ImagingTransformMap transform, void* transform_data,
ImagingTransformFilter filter, void* filter_data,
int fill)
diff --git a/libImaging/GetBBox.c b/libImaging/GetBBox.c
index d1722fe14..3cfa42c48 100644
--- a/libImaging/GetBBox.c
+++ b/libImaging/GetBBox.c
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
@@ -224,7 +224,7 @@ getcolors32(Imaging im, int maxcolors, int* size)
33554432,9, 67108864,71, 134217728,39, 268435456,9, 536870912,5,
1073741824,83, 0
};
-
+
code_size = code_poly = code_mask = 0;
for (i = 0; SIZES[i]; i += 2) {
diff --git a/libImaging/GifDecode.c b/libImaging/GifDecode.c
index 6fb05b564..1b9206b29 100644
--- a/libImaging/GifDecode.c
+++ b/libImaging/GifDecode.c
@@ -75,7 +75,7 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes)
state->errcode = IMAGING_CODEC_CONFIG;
return -1;
}
-
+
/* Clear code */
context->clear = 1 << context->bits;
diff --git a/libImaging/GifEncode.c b/libImaging/GifEncode.c
index f4d07598f..4ada55496 100644
--- a/libImaging/GifEncode.c
+++ b/libImaging/GifEncode.c
@@ -44,7 +44,7 @@ emit(GIFENCODERSTATE *context, int byte)
/* no room in the current block (or no current block);
allocate a new one */
-
+
/* add current block to end of flush queue */
if (context->block) {
block = context->flush;
diff --git a/libImaging/Histo.c b/libImaging/Histo.c
index 513c84475..0bfc8dfe9 100644
--- a/libImaging/Histo.c
+++ b/libImaging/Histo.c
@@ -41,7 +41,7 @@ ImagingHistogramNew(Imaging im)
/* Create histogram descriptor */
h = calloc(1, sizeof(struct ImagingHistogramInstance));
- strcpy(h->mode, im->mode);
+ strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH);
h->bands = im->bands;
h->histogram = calloc(im->pixelsize, 256 * sizeof(long));
diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h
index 2effd3870..23c660488 100644
--- a/libImaging/ImDib.h
+++ b/libImaging/ImDib.h
@@ -14,6 +14,10 @@
#if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__)
/* already defined in basetsd.h */
+#undef INT8
+#undef UINT8
+#undef INT16
+#undef UINT16
#undef INT32
#undef INT64
#undef UINT32
diff --git a/libImaging/ImPlatform.h b/libImaging/ImPlatform.h
index 4ffd6fdef..7fc5cbdca 100644
--- a/libImaging/ImPlatform.h
+++ b/libImaging/ImPlatform.h
@@ -1,4 +1,4 @@
-/*
+/*
* The Python Imaging Library
* $Id$
*
diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h
index 4609376ad..b45dcbe23 100644
--- a/libImaging/Imaging.h
+++ b/libImaging/Imaging.h
@@ -1,7 +1,7 @@
/*
* The Python Imaging Library
* $Id$
- *
+ *
* declarations for the imaging core library
*
* Copyright (c) 1997-2005 by Secret Labs AB
@@ -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
@@ -72,10 +73,12 @@ typedef struct ImagingPaletteInstance* ImagingPalette;
#define IMAGING_TYPE_FLOAT32 2
#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */
+#define IMAGING_MODE_LENGTH 6+1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */
+
struct ImagingMemoryInstance {
/* Format */
- char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */
+ char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */
int type; /* Data type (IMAGING_TYPE_*) */
int depth; /* Depth (ignored in this version) */
int bands; /* Number of bands (1, 2, 3, or 4) */
@@ -127,7 +130,7 @@ struct ImagingAccessInstance {
struct ImagingHistogramInstance {
/* Format */
- char mode[4+1]; /* Band names (of corresponding source image) */
+ char mode[IMAGING_MODE_LENGTH]; /* Band names (of corresponding source image) */
int bands; /* Number of bands (1, 3, or 4) */
/* Data */
@@ -139,7 +142,7 @@ struct ImagingHistogramInstance {
struct ImagingPaletteInstance {
/* Format */
- char mode[4+1]; /* Band names */
+ char mode[IMAGING_MODE_LENGTH]; /* Band names */
/* Data */
UINT8 palette[1024];/* Palette data (same format as image data) */
@@ -239,11 +242,13 @@ typedef int (*ImagingTransformFilter)(void* out, Imaging im,
/* Image Manipulation Methods */
/* -------------------------- */
+extern Imaging ImagingAlphaComposite(Imaging imIn1, Imaging imIn2);
extern Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha);
extern Imaging ImagingCopy(Imaging im);
extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither);
extern Imaging ImagingConvertInPlace(Imaging im, const char* mode);
extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]);
+extern Imaging ImagingConvertTransparent(Imaging im, const char *mode, int r, int g, int b);
extern Imaging ImagingCrop(Imaging im, int x0, int y0, int x1, int y1);
extern Imaging ImagingExpand(Imaging im, int x, int y, int mode);
extern Imaging ImagingFill(Imaging im, const void* ink);
@@ -288,16 +293,16 @@ extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn);
extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn);
extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter);
extern Imaging ImagingTransformPerspective(
- Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[8], int filter, int fill);
extern Imaging ImagingTransformAffine(
- Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[6], int filter, int fill);
extern Imaging ImagingTransformQuad(
- Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
double a[8], int filter, int fill);
extern Imaging ImagingTransform(
- Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
ImagingTransformMap transform, void* transform_data,
ImagingTransformFilter filter, void* filter_data,
int fill);
@@ -370,7 +375,7 @@ extern int ImagingOutlineLine(ImagingOutline outline, float x, float y);
extern int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1,
float x2, float y2, float x3, float y3);
extern int ImagingOutlineTransform(ImagingOutline outline, double a[6]);
-
+
extern int ImagingOutlineClose(ImagingOutline outline);
/* Special effects */
@@ -414,11 +419,19 @@ extern int ImagingHexDecode(Imaging im, ImagingCodecState state,
#ifdef HAVE_LIBJPEG
extern int ImagingJpegDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
+extern int ImagingJpegDecodeCleanup(ImagingCodecState state);
+
extern int ImagingJpegEncode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
#endif
extern int ImagingLzwDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
+#ifdef HAVE_LIBTIFF
+extern int ImagingLibTiffDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingLibTiffEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+#endif
#ifdef HAVE_LIBMPEG
extern int ImagingMpegDecode(Imaging im, ImagingCodecState state,
UINT8* buffer, int bytes);
diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h
index d39165f3c..0b8c5cf9a 100644
--- a/libImaging/Jpeg.h
+++ b/libImaging/Jpeg.h
@@ -88,6 +88,9 @@ typedef struct {
/* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */
int subsampling;
+ /* Custom quantization tables () */
+ unsigned int **qtables;
+
/* Extra data (to be injected after header) */
char* extra; int extra_size;
@@ -101,4 +104,7 @@ typedef struct {
int extra_offset;
+ int rawExifLen; /* EXIF data length */
+ char* rawExif; /* EXIF buffer pointer */
+
} JPEGENCODERSTATE;
diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c
index 11b2767ba..6ebdb8f93 100644
--- a/libImaging/JpegDecode.c
+++ b/libImaging/JpegDecode.c
@@ -15,8 +15,8 @@
* 2000-10-12 fl Suppress warnings
* 2000-12-04 fl Suppress errors beyond end of image data
*
- * Copyright (c) 1998-2000 Secret Labs AB
- * Copyright (c) 1996-2000 Fredrik Lundh
+ * Copyright (c) 1998-2000 Secret Labs AB
+ * Copyright (c) 1996-2000 Fredrik Lundh
*
* See the README file for details on usage and redistribution.
*/
@@ -24,11 +24,11 @@
#include "Imaging.h"
-#ifdef HAVE_LIBJPEG
+#ifdef HAVE_LIBJPEG
-#undef HAVE_PROTOTYPES
-#undef HAVE_STDLIB_H
-#undef HAVE_STDDEF_H
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef HAVE_STDDEF_H
#undef UINT8
#undef UINT16
#undef UINT32
@@ -39,7 +39,7 @@
/* -------------------------------------------------------------------- */
-/* Suspending input handler */
+/* Suspending input handler */
/* -------------------------------------------------------------------- */
METHODDEF(void)
@@ -61,16 +61,16 @@ skip_input_data(j_decompress_ptr cinfo, long num_bytes)
JPEGSOURCE* source = (JPEGSOURCE*) cinfo->src;
if (num_bytes > (long) source->pub.bytes_in_buffer) {
- /* We need to skip more data than we have in the buffer.
- This will force the JPEG library to suspend decoding. */
- source->skip = num_bytes - source->pub.bytes_in_buffer;
- source->pub.next_input_byte += source->pub.bytes_in_buffer;
- source->pub.bytes_in_buffer = 0;
+ /* We need to skip more data than we have in the buffer.
+ This will force the JPEG library to suspend decoding. */
+ source->skip = num_bytes - source->pub.bytes_in_buffer;
+ source->pub.next_input_byte += source->pub.bytes_in_buffer;
+ source->pub.bytes_in_buffer = 0;
} else {
- /* Skip portion of the buffer */
- source->pub.bytes_in_buffer -= num_bytes;
- source->pub.next_input_byte += num_bytes;
- source->skip = 0;
+ /* Skip portion of the buffer */
+ source->pub.bytes_in_buffer -= num_bytes;
+ source->pub.next_input_byte += num_bytes;
+ source->skip = 0;
}
}
@@ -93,7 +93,7 @@ jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source)
/* -------------------------------------------------------------------- */
-/* Error handler */
+/* Error handler */
/* -------------------------------------------------------------------- */
METHODDEF(void)
@@ -111,7 +111,7 @@ output(j_common_ptr cinfo)
}
/* -------------------------------------------------------------------- */
-/* Decoder */
+/* Decoder */
/* -------------------------------------------------------------------- */
int
@@ -121,23 +121,23 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
int ok;
if (setjmp(context->error.setjmp_buffer)) {
- /* JPEG error handler */
- jpeg_destroy_decompress(&context->cinfo);
- state->errcode = IMAGING_CODEC_BROKEN;
- return -1;
+ /* JPEG error handler */
+ jpeg_destroy_decompress(&context->cinfo);
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
}
if (!state->state) {
- /* Setup decompression context */
- context->cinfo.err = jpeg_std_error(&context->error.pub);
- context->error.pub.error_exit = error;
- context->error.pub.output_message = output;
- jpeg_create_decompress(&context->cinfo);
- jpeg_buffer_src(&context->cinfo, &context->source);
+ /* Setup decompression context */
+ context->cinfo.err = jpeg_std_error(&context->error.pub);
+ context->error.pub.error_exit = error;
+ context->error.pub.output_message = output;
+ jpeg_create_decompress(&context->cinfo);
+ jpeg_buffer_src(&context->cinfo, &context->source);
- /* Ready to decode */
- state->state = 1;
+ /* Ready to decode */
+ state->state = 1;
}
@@ -146,122 +146,137 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
context->source.pub.bytes_in_buffer = bytes;
if (context->source.skip > 0) {
- skip_input_data(&context->cinfo, context->source.skip);
- if (context->source.skip > 0)
- return context->source.pub.next_input_byte - buf;
+ skip_input_data(&context->cinfo, context->source.skip);
+ if (context->source.skip > 0)
+ return context->source.pub.next_input_byte - buf;
}
switch (state->state) {
case 1:
- /* Read JPEG header, until we find an image body. */
- do {
-
- /* Note that we cannot return unless we have decoded
- as much data as possible. */
- ok = jpeg_read_header(&context->cinfo, FALSE);
+ /* Read JPEG header, until we find an image body. */
+ do {
- } while (ok == JPEG_HEADER_TABLES_ONLY);
+ /* Note that we cannot return unless we have decoded
+ as much data as possible. */
+ ok = jpeg_read_header(&context->cinfo, FALSE);
- if (ok == JPEG_SUSPENDED)
- break;
+ } while (ok == JPEG_HEADER_TABLES_ONLY);
- /* Decoder settings */
+ if (ok == JPEG_SUSPENDED)
+ break;
- /* jpegmode indicates whats in the file; if not set, we'll
- trust the decoder */
- if (strcmp(context->jpegmode, "L") == 0)
- context->cinfo.jpeg_color_space = JCS_GRAYSCALE;
- else if (strcmp(context->jpegmode, "RGB") == 0)
- context->cinfo.jpeg_color_space = JCS_RGB;
- else if (strcmp(context->jpegmode, "CMYK") == 0)
- context->cinfo.jpeg_color_space = JCS_CMYK;
- else if (strcmp(context->jpegmode, "YCbCr") == 0)
- context->cinfo.jpeg_color_space = JCS_YCbCr;
- else if (strcmp(context->jpegmode, "YCbCrK") == 0) {
- context->cinfo.jpeg_color_space = JCS_YCCK;
- }
+ /* Decoder settings */
- /* rawmode indicates what we want from the decoder. if not
- set, conversions are disabled */
- if (strcmp(context->rawmode, "L") == 0)
- context->cinfo.out_color_space = JCS_GRAYSCALE;
- else if (strcmp(context->rawmode, "RGB") == 0)
- context->cinfo.out_color_space = JCS_RGB;
- else if (strcmp(context->rawmode, "CMYK") == 0 ||
+ /* jpegmode indicates whats in the file; if not set, we'll
+ trust the decoder */
+ if (strcmp(context->jpegmode, "L") == 0)
+ context->cinfo.jpeg_color_space = JCS_GRAYSCALE;
+ else if (strcmp(context->jpegmode, "RGB") == 0)
+ context->cinfo.jpeg_color_space = JCS_RGB;
+ else if (strcmp(context->jpegmode, "CMYK") == 0)
+ context->cinfo.jpeg_color_space = JCS_CMYK;
+ else if (strcmp(context->jpegmode, "YCbCr") == 0)
+ context->cinfo.jpeg_color_space = JCS_YCbCr;
+ else if (strcmp(context->jpegmode, "YCbCrK") == 0) {
+ context->cinfo.jpeg_color_space = JCS_YCCK;
+ }
+
+ /* rawmode indicates what we want from the decoder. if not
+ set, conversions are disabled */
+ if (strcmp(context->rawmode, "L") == 0)
+ context->cinfo.out_color_space = JCS_GRAYSCALE;
+ else if (strcmp(context->rawmode, "RGB") == 0)
+ context->cinfo.out_color_space = JCS_RGB;
+ else if (strcmp(context->rawmode, "CMYK") == 0 ||
strcmp(context->rawmode, "CMYK;I") == 0)
- context->cinfo.out_color_space = JCS_CMYK;
- else if (strcmp(context->rawmode, "YCbCr") == 0)
- context->cinfo.out_color_space = JCS_YCbCr;
- else if (strcmp(context->rawmode, "YCbCrK") == 0)
- context->cinfo.out_color_space = JCS_YCCK;
- else {
- /* Disable decoder conversions */
- context->cinfo.jpeg_color_space = JCS_UNKNOWN;
- context->cinfo.out_color_space = JCS_UNKNOWN;
- }
+ context->cinfo.out_color_space = JCS_CMYK;
+ else if (strcmp(context->rawmode, "YCbCr") == 0)
+ context->cinfo.out_color_space = JCS_YCbCr;
+ else if (strcmp(context->rawmode, "YCbCrK") == 0)
+ context->cinfo.out_color_space = JCS_YCCK;
+ else {
+ /* Disable decoder conversions */
+ context->cinfo.jpeg_color_space = JCS_UNKNOWN;
+ context->cinfo.out_color_space = JCS_UNKNOWN;
+ }
- if (context->scale > 1) {
- context->cinfo.scale_num = 1;
- context->cinfo.scale_denom = context->scale;
- }
- if (context->draft) {
- context->cinfo.do_fancy_upsampling = FALSE;
- context->cinfo.dct_method = JDCT_FASTEST;
- }
+ if (context->scale > 1) {
+ context->cinfo.scale_num = 1;
+ context->cinfo.scale_denom = context->scale;
+ }
+ if (context->draft) {
+ context->cinfo.do_fancy_upsampling = FALSE;
+ context->cinfo.dct_method = JDCT_FASTEST;
+ }
state->state++;
- /* fall through */
+ /* fall through */
case 2:
/* Set things up for decompression (this processes the entire
file if necessary to return data line by line) */
- if (!jpeg_start_decompress(&context->cinfo))
+ if (!jpeg_start_decompress(&context->cinfo))
break;
-
- state->state++;
- /* fall through */
+
+ state->state++;
+ /* fall through */
case 3:
- /* Decompress a single line of data */
- ok = 1;
- while (state->y < state->ysize) {
- ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1);
- if (ok != 1)
- break;
- state->shuffle((UINT8*) im->image[state->y + state->yoff] +
- state->xoff * im->pixelsize, state->buffer,
- state->xsize);
- state->y++;
- }
- if (ok != 1)
- break;
- state->state++;
- /* fall through */
+ /* Decompress a single line of data */
+ ok = 1;
+ while (state->y < state->ysize) {
+ ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1);
+ if (ok != 1)
+ break;
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+ state->y++;
+ }
+ if (ok != 1)
+ break;
+ state->state++;
+ /* fall through */
case 4:
- /* Finish decompression */
- if (!jpeg_finish_decompress(&context->cinfo)) {
+ /* Finish decompression */
+ if (!jpeg_finish_decompress(&context->cinfo)) {
/* FIXME: add strictness mode test */
if (state->y < state->ysize)
break;
}
- /* Clean up */
- jpeg_destroy_decompress(&context->cinfo);
- /* if (jerr.pub.num_warnings) return BROKEN; */
- return -1;
+ /* Clean up */
+ jpeg_destroy_decompress(&context->cinfo);
+ /* if (jerr.pub.num_warnings) return BROKEN; */
+ return -1;
}
/* Return number of bytes consumed */
- return context->source.pub.next_input_byte - buf;
+ return context->source.pub.next_input_byte - buf;
}
+/* -------------------------------------------------------------------- */
+/* Cleanup */
+/* -------------------------------------------------------------------- */
+
+int ImagingJpegDecodeCleanup(ImagingCodecState state){
+ /* called to fee the decompression engine when the decode terminates
+ due to a corrupt or truncated image
+ */
+ JPEGSTATE* context = (JPEGSTATE*) state->context;
+
+ /* Clean up */
+ jpeg_destroy_decompress(&context->cinfo);
+ return -1;
+}
+
#endif
diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c
index 1e2191a69..711b201e0 100644
--- a/libImaging/JpegEncode.c
+++ b/libImaging/JpegEncode.c
@@ -22,11 +22,11 @@
#include "Imaging.h"
-#ifdef HAVE_LIBJPEG
+#ifdef HAVE_LIBJPEG
-#undef HAVE_PROTOTYPES
-#undef HAVE_STDLIB_H
-#undef HAVE_STDDEF_H
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef HAVE_STDDEF_H
#undef UINT8
#undef UINT16
#undef UINT32
@@ -36,7 +36,7 @@
#include "Jpeg.h"
/* -------------------------------------------------------------------- */
-/* Suspending output handler */
+/* Suspending output handler */
/* -------------------------------------------------------------------- */
METHODDEF(void)
@@ -64,16 +64,16 @@ jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination)
/* -------------------------------------------------------------------- */
-/* Error handler */
+/* Error handler */
/* -------------------------------------------------------------------- */
METHODDEF(void)
error(j_common_ptr cinfo)
{
- JPEGERROR* error;
- error = (JPEGERROR*) cinfo->err;
- (*cinfo->err->output_message) (cinfo);
- longjmp(error->setjmp_buffer, 1);
+ JPEGERROR* error;
+ error = (JPEGERROR*) cinfo->err;
+ (*cinfo->err->output_message) (cinfo);
+ longjmp(error->setjmp_buffer, 1);
}
@@ -143,48 +143,62 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* Compressor configuration */
jpeg_set_defaults(&context->cinfo);
- if (context->quality > 0)
+
+ /* Use custom quantization tables */
+ if (context->qtables) {
+ int i;
+ int quality = 100;
+ if (context->quality > 0) {
+ quality = context->quality;
+ }
+ for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) {
+ // TODO: Should add support for none baseline
+ jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
+ quality, TRUE);
+ }
+ } else if (context->quality > 0) {
jpeg_set_quality(&context->cinfo, context->quality, 1);
-
+ }
+
/* Set subsampling options */
switch (context->subsampling)
- {
- case 0: /* 1x1 1x1 1x1 (4:4:4) : None */
+ {
+ case 0: /* 1x1 1x1 1x1 (4:4:4) : None */
{
- context->cinfo.comp_info[0].h_samp_factor = 1;
- context->cinfo.comp_info[0].v_samp_factor = 1;
- context->cinfo.comp_info[1].h_samp_factor = 1;
- context->cinfo.comp_info[1].v_samp_factor = 1;
- context->cinfo.comp_info[2].h_samp_factor = 1;
- context->cinfo.comp_info[2].v_samp_factor = 1;
- break;
+ context->cinfo.comp_info[0].h_samp_factor = 1;
+ context->cinfo.comp_info[0].v_samp_factor = 1;
+ context->cinfo.comp_info[1].h_samp_factor = 1;
+ context->cinfo.comp_info[1].v_samp_factor = 1;
+ context->cinfo.comp_info[2].h_samp_factor = 1;
+ context->cinfo.comp_info[2].v_samp_factor = 1;
+ break;
}
- case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */
+ case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */
{
- context->cinfo.comp_info[0].h_samp_factor = 2;
- context->cinfo.comp_info[0].v_samp_factor = 1;
- context->cinfo.comp_info[1].h_samp_factor = 1;
- context->cinfo.comp_info[1].v_samp_factor = 1;
- context->cinfo.comp_info[2].h_samp_factor = 1;
- context->cinfo.comp_info[2].v_samp_factor = 1;
- break;
+ context->cinfo.comp_info[0].h_samp_factor = 2;
+ context->cinfo.comp_info[0].v_samp_factor = 1;
+ context->cinfo.comp_info[1].h_samp_factor = 1;
+ context->cinfo.comp_info[1].v_samp_factor = 1;
+ context->cinfo.comp_info[2].h_samp_factor = 1;
+ context->cinfo.comp_info[2].v_samp_factor = 1;
+ break;
}
- case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */
+ case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */
{
- context->cinfo.comp_info[0].h_samp_factor = 2;
- context->cinfo.comp_info[0].v_samp_factor = 2;
- context->cinfo.comp_info[1].h_samp_factor = 1;
- context->cinfo.comp_info[1].v_samp_factor = 1;
- context->cinfo.comp_info[2].h_samp_factor = 1;
- context->cinfo.comp_info[2].v_samp_factor = 1;
- break;
+ context->cinfo.comp_info[0].h_samp_factor = 2;
+ context->cinfo.comp_info[0].v_samp_factor = 2;
+ context->cinfo.comp_info[1].h_samp_factor = 1;
+ context->cinfo.comp_info[1].v_samp_factor = 1;
+ context->cinfo.comp_info[2].h_samp_factor = 1;
+ context->cinfo.comp_info[2].v_samp_factor = 1;
+ break;
}
- default:
+ default:
{
- /* Use the lib's default */
- break;
+ /* Use the lib's default */
+ break;
}
- }
+ }
if (context->progressive)
jpeg_simple_progression(&context->cinfo);
context->cinfo.smoothing_factor = context->smooth;
@@ -215,6 +229,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* fall through */
case 2:
+ // check for exif len + 'APP1' header bytes
+ if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){
+ break;
+ }
+ //add exif header
+ if (context->rawExifLen > 0){
+ jpeg_write_marker(&context->cinfo, JPEG_APP0+1,
+ (unsigned char*)context->rawExif, context->rawExifLen);
+ }
+
+ state->state++;
+ /* fall through */
+ case 3:
if (context->extra) {
/* copy extra buffer to output buffer */
@@ -231,9 +258,12 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
else
break;
} else
- state->state++;
+ state->state++;
- case 3:
+ case 4:
+ if (1024 > context->destination.pub.free_in_buffer){
+ break;
+ }
ok = 1;
while (state->y < state->ysize) {
@@ -251,7 +281,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
state->state++;
/* fall through */
- case 4:
+ case 5:
/* Finish compression */
if (context->destination.pub.free_in_buffer < 100)
diff --git a/libImaging/ModeFilter.c b/libImaging/ModeFilter.c
index ad11d87d4..b1fc7e8e6 100644
--- a/libImaging/ModeFilter.c
+++ b/libImaging/ModeFilter.c
@@ -69,7 +69,7 @@ ImagingModeFilter(Imaging im, int size)
out[x] = IMAGING_PIXEL_L(im, x, y);
}
-
+
}
ImagingCopyInfo(imOut, im);
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/Palette.c b/libImaging/Palette.c
index bd4f4f1d6..9b0dd57c4 100644
--- a/libImaging/Palette.c
+++ b/libImaging/Palette.c
@@ -37,7 +37,7 @@ ImagingPaletteNew(const char* mode)
if (!palette)
return (ImagingPalette) ImagingError_MemoryError();
- strcpy(palette->mode, mode);
+ strncpy(palette->mode, mode, IMAGING_MODE_LENGTH);
/* Initialize to ramp */
for (i = 0; i < 256; i++) {
diff --git a/libImaging/Paste.c b/libImaging/Paste.c
index b89dd6404..d27b10fdb 100644
--- a/libImaging/Paste.c
+++ b/libImaging/Paste.c
@@ -7,7 +7,7 @@
* history:
* 96-03-27 fl Created
* 96-07-16 fl Support "1", "L" and "RGBA" masks
- * 96-08-16 fl Merged with opaque paste
+ * 96-08-16 fl Merged with opaque paste
* 97-01-17 fl Faster blending, added support for RGBa images
* 97-08-27 fl Faster masking for 32-bit images
* 98-02-02 fl Fixed MULDIV255 macro for gcc
@@ -209,7 +209,7 @@ paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask,
}
}
}
-
+
int
ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask,
int dx0, int dy0, int dx1, int dy1)
@@ -310,7 +310,7 @@ fill(Imaging imOut, const void* ink_, int dx, int dy,
xsize *= pixelsize;
for (y = 0; y < ysize; y++)
memset(imOut->image[y+dy]+dx, ink8, xsize);
-
+
} else {
for (y = 0; y < ysize; y++) {
diff --git a/libImaging/PcdDecode.c b/libImaging/PcdDecode.c
index b6898e301..fb6adc688 100644
--- a/libImaging/PcdDecode.c
+++ b/libImaging/PcdDecode.c
@@ -37,7 +37,7 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
for (;;) {
- /* We need data for two full lines before we can do anything */
+ /* We need data for two full lines before we can do anything */
if (bytes < chunk)
return ptr - buf;
diff --git a/libImaging/PcxDecode.c b/libImaging/PcxDecode.c
index ab82b23ad..04c86cb35 100644
--- a/libImaging/PcxDecode.c
+++ b/libImaging/PcxDecode.c
@@ -36,7 +36,7 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
return ptr - buf;
n = ptr[0] & 0x3F;
-
+
while (n > 0) {
if (state->x >= state->bytes) {
state->errcode = IMAGING_CODEC_OVERRUN;
diff --git a/libImaging/Point.c b/libImaging/Point.c
index 2593dca57..53d797e58 100644
--- a/libImaging/Point.c
+++ b/libImaging/Point.c
@@ -207,8 +207,8 @@ ImagingPointTransform(Imaging imIn, double scale, double offset)
Imaging imOut;
int x, y;
- if (!imIn || (strcmp(imIn->mode, "I") != 0 &&
- strcmp(imIn->mode, "I;16") != 0 &&
+ if (!imIn || (strcmp(imIn->mode, "I") != 0 &&
+ strcmp(imIn->mode, "I;16") != 0 &&
strcmp(imIn->mode, "F") != 0))
return (Imaging) ImagingError_ModeError();
diff --git a/libImaging/Quant.c b/libImaging/Quant.c
index 1b76a6a0f..5b8a8d994 100644
--- a/libImaging/Quant.c
+++ b/libImaging/Quant.c
@@ -11,7 +11,7 @@
* 2005-02-07 fl Limit number of colors to 256
*
* Written by Toby J Sargeant .
- *
+ *
* Copyright (c) 1998 by Toby J Sargeant
* Copyright (c) 1998-2004 by Secret Labs AB. All rights reserved.
*
@@ -25,16 +25,15 @@
#include
#include
-#include "Quant.h"
-
-#include "QuantDefines.h"
+#include "QuantTypes.h"
+#include "QuantOctree.h"
#include "QuantHash.h"
#include "QuantHeap.h"
#define NO_OUTPUT
typedef struct {
- unsigned long scale;
+ uint32_t scale;
} PixelHashData;
typedef struct _PixelList {
@@ -49,7 +48,7 @@ typedef struct _BoxNode {
PixelList *head[3],*tail[3];
int axis;
int volume;
- unsigned long pixelCount;
+ uint32_t pixelCount;
} BoxNode;
#define _SQR(x) ((x)*(x))
@@ -75,104 +74,92 @@ typedef struct _BoxNode {
((q)->c.g=(p)->c.g>>(s)), \
((q)->c.b=(p)->c.b>>(s))
-static unsigned long
-unshifted_pixel_hash(const HashTable h, const void *p)
+static uint32_t
+unshifted_pixel_hash(const HashTable *h, const Pixel pixel)
{
- Pixel *pixel=(Pixel *)&p;
- unsigned long hash=PIXEL_HASH(pixel->c.r,
- pixel->c.g,
- pixel->c.b);
- return hash;
+ return PIXEL_HASH(pixel.c.r, pixel.c.g, pixel.c.b);
}
static int
-unshifted_pixel_cmp(const HashTable h, const void *a, const void *b)
+unshifted_pixel_cmp(const HashTable *h, const Pixel pixel1, const Pixel pixel2)
{
- Pixel *pixel1=(Pixel *)&a;
- Pixel *pixel2=(Pixel *)&b;
- if (pixel1->c.r==pixel2->c.r) {
- if (pixel1->c.g==pixel2->c.g) {
- if (pixel1->c.b==pixel2->c.b) {
+ if (pixel1.c.r==pixel2.c.r) {
+ if (pixel1.c.g==pixel2.c.g) {
+ if (pixel1.c.b==pixel2.c.b) {
return 0;
} else {
- return (int)(pixel1->c.b)-(int)(pixel2->c.b);
+ return (int)(pixel1.c.b)-(int)(pixel2.c.b);
}
} else {
- return (int)(pixel1->c.g)-(int)(pixel2->c.g);
+ return (int)(pixel1.c.g)-(int)(pixel2.c.g);
}
} else {
- return (int)(pixel1->c.r)-(int)(pixel2->c.r);
+ return (int)(pixel1.c.r)-(int)(pixel2.c.r);
}
}
-static unsigned long
-pixel_hash(const HashTable h,const void *p)
+static uint32_t
+pixel_hash(const HashTable *h,const Pixel pixel)
{
PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
- Pixel *pixel=(Pixel *)&p;
- unsigned long hash=PIXEL_HASH(pixel->c.r>>d->scale,
- pixel->c.g>>d->scale,
- pixel->c.b>>d->scale);
- return hash;
+ return PIXEL_HASH(pixel.c.r>>d->scale, pixel.c.g>>d->scale, pixel.c.b>>d->scale);
}
static int
-pixel_cmp(const HashTable h,const void *a,const void *b)
+pixel_cmp(const HashTable *h,const Pixel pixel1, const Pixel pixel2)
{
PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
- Pixel *pixel1=(Pixel *)&a;
- Pixel *pixel2=(Pixel *)&b;
- unsigned long A,B;
- A=PIXEL_HASH(pixel1->c.r>>d->scale,
- pixel1->c.g>>d->scale,
- pixel1->c.b>>d->scale);
- B=PIXEL_HASH(pixel2->c.r>>d->scale,
- pixel2->c.g>>d->scale,
- pixel2->c.b>>d->scale);
+ uint32_t A,B;
+ A=PIXEL_HASH(pixel1.c.r>>d->scale, pixel1.c.g>>d->scale, pixel1.c.b>>d->scale);
+ B=PIXEL_HASH(pixel2.c.r>>d->scale, pixel2.c.g>>d->scale, pixel2.c.b>>d->scale);
return (A==B)?0:((Ascale=0;
+#ifndef NO_OUTPUT
timer=timer3=clock();
+#endif
for (i=0;iscale++;
#ifndef NO_OUTPUT
printf ("rehashing - new scale: %d\n",(int)d->scale);
-#endif
timer2=clock();
- hashtable_rehash_compute(hash,rehash_collide);
- timer2=clock()-timer2;
-#ifndef NO_OUTPUT
- printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC);
#endif
+ hashtable_rehash_compute(hash,rehash_collide);
+#ifndef NO_OUTPUT
+ timer2=clock()-timer2;
+ printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC);
timer+=timer2;
+#endif
}
}
#ifndef NO_OUTPUT
@@ -200,7 +187,7 @@ create_pixel_hash(Pixel *pixelData,unsigned long nPixels)
}
static void
-destroy_pixel_hash(HashTable hash)
+destroy_pixel_hash(HashTable *hash)
{
PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash);
if (d) free(d);
@@ -236,21 +223,19 @@ compute_box_volume(BoxNode *b)
}
static void
-hash_to_list(HashTable h, const void *key, const void *val, void *u)
+hash_to_list(const HashTable *h, const Pixel pixel, const uint32_t count, void *u)
{
PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
PixelList **pl=(PixelList **)u;
PixelList *p;
- Pixel *pixel=(Pixel *)&key;
int i;
Pixel q;
- int count=*(int *)&val;
- PIXEL_SCALE(pixel,&q,d->scale);
+ PIXEL_SCALE(&pixel,&q,d->scale);
p=malloc(sizeof(PixelList));
if (!p) return;
-
+
p->flag=0;
p->p=q;
p->count=count;
@@ -326,7 +311,7 @@ test_sorted(PixelList *pl[3])
#endif
static int
-box_heap_cmp(const Heap h, const void *A, const void *B)
+box_heap_cmp(const Heap *h, const void *A, const void *B)
{
BoxNode *a=(BoxNode *)A;
BoxNode *b=(BoxNode *)B;
@@ -340,11 +325,11 @@ splitlists(PixelList *h[3],
PixelList *t[3],
PixelList *nh[2][3],
PixelList *nt[2][3],
- unsigned long nCount[2],
+ uint32_t nCount[2],
int axis,
- unsigned long pixelCount)
+ uint32_t pixelCount)
{
- unsigned long left;
+ uint32_t left;
PixelList *l,*r,*c,*n;
int i;
@@ -475,7 +460,7 @@ split(BoxNode *node)
int i;
PixelList *heads[2][3];
PixelList *tails[2][3];
- unsigned long newCounts[2];
+ uint32_t newCounts[2];
BoxNode *left,*right;
rh=node->head[0]->p.c.r;
@@ -499,7 +484,7 @@ split(BoxNode *node)
#ifdef TEST_SPLIT
printf ("along axis %d\n",axis+1);
#endif
-
+
#ifdef TEST_SPLIT
{
PixelList *_prevTest,*_nextTest;
@@ -617,13 +602,13 @@ split(BoxNode *node)
static BoxNode *
median_cut(PixelList *hl[3],
- unsigned long imPixelCount,
+ uint32_t imPixelCount,
int nPixels)
{
PixelList *tl[3];
int i;
BoxNode *root;
- Heap h;
+ Heap* h;
BoxNode *thisNode;
h=ImagingQuantHeapNew(box_heap_cmp);
@@ -700,7 +685,7 @@ checkContained(BoxNode *n,Pixel *pp)
#endif
static int
-annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box)
+annotate_hash_table(BoxNode *n,HashTable *h,uint32_t *box)
{
PixelList *p;
PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
@@ -716,7 +701,7 @@ annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box)
}
for (p=n->head[0];p;p=p->next[0]) {
PIXEL_UNSCALE(&(p->p),&q,d->scale);
- if (!hashtable_insert(h,(void *)q.v,(void *)*box)) {
+ if (!hashtable_insert(h,q,*box)) {
#ifndef NO_OUTPUT
printf ("hashtable insert failed\n");
#endif
@@ -730,20 +715,20 @@ annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box)
static int
_sort_ulong_ptr_keys(const void *a, const void *b)
{
- unsigned long A=**(unsigned long **)a;
- unsigned long B=**(unsigned long **)b;
+ uint32_t A=**(uint32_t **)a;
+ uint32_t B=**(uint32_t **)b;
return (A==B)?0:((A=nPaletteEntries) {
@@ -1090,35 +1075,35 @@ compute_palette_from_quantized_pixels(
static int
k_means(Pixel *pixelData,
- unsigned long nPixels,
+ uint32_t nPixels,
Pixel *paletteData,
- unsigned long nPaletteEntries,
- unsigned long *qp,
+ uint32_t nPaletteEntries,
+ uint32_t *qp,
int threshold)
{
- unsigned long *avg[3];
- unsigned long *count;
- unsigned long i;
- unsigned long *avgDist;
- unsigned long **avgDistSortKey;
+ uint32_t *avg[3];
+ uint32_t *count;
+ uint32_t i;
+ uint32_t *avgDist;
+ uint32_t **avgDistSortKey;
int changes;
int built=0;
-
- if (!(count=malloc(sizeof(unsigned long)*nPaletteEntries))) {
+
+ if (!(count=malloc(sizeof(uint32_t)*nPaletteEntries))) {
return 0;
}
for(i=0;i<3;i++) {
avg[i]=NULL;
}
for(i=0;i<3;i++) {
- if (!(avg[i]=malloc(sizeof(unsigned long)*nPaletteEntries))) {
+ if (!(avg[i]=malloc(sizeof(uint32_t)*nPaletteEntries))) {
goto error_1;
}
}
- avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries);
+ avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries);
if (!avgDist) { goto error_1; }
- avgDistSortKey=malloc(sizeof(unsigned long *)*nPaletteEntries*nPaletteEntries);
+ avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries);
if (!avgDistSortKey) { goto error_2; }
#ifndef NO_OUTPUT
@@ -1171,26 +1156,26 @@ error_1:
int
quantize(Pixel *pixelData,
- unsigned long nPixels,
- unsigned long nQuantPixels,
+ uint32_t nPixels,
+ uint32_t nQuantPixels,
Pixel **palette,
- unsigned long *paletteLength,
- unsigned long **quantizedPixels,
+ uint32_t *paletteLength,
+ uint32_t **quantizedPixels,
int kmeans)
{
PixelList *hl[3];
- HashTable h;
+ HashTable *h;
BoxNode *root;
- unsigned long i;
- unsigned long *qp;
- unsigned long nPaletteEntries;
-
- unsigned long *avgDist;
- unsigned long **avgDistSortKey;
+ uint32_t i;
+ uint32_t *qp;
+ uint32_t nPaletteEntries;
+
+ uint32_t *avgDist;
+ uint32_t **avgDistSortKey;
Pixel *p;
-
+
#ifndef NO_OUTPUT
- unsigned long timer,timer2;
+ uint32_t timer,timer2;
#endif
#ifndef NO_OUTPUT
@@ -1265,13 +1250,13 @@ quantize(Pixel *pixelData,
free_box_tree(root);
root=NULL;
- qp=malloc(sizeof(unsigned long)*nPixels);
+ qp=malloc(sizeof(uint32_t)*nPixels);
if (!qp) { goto error_4; }
- avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries);
+ avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries);
if (!avgDist) { goto error_5; }
- avgDistSortKey=malloc(sizeof(unsigned long *)*nPaletteEntries*nPaletteEntries);
+ avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries);
if (!avgDistSortKey) { goto error_6; }
if (!build_distance_tables(avgDist,avgDistSortKey,p,nPaletteEntries)) {
@@ -1285,12 +1270,12 @@ quantize(Pixel *pixelData,
#ifdef TEST_NEAREST_NEIGHBOUR
#include
{
- unsigned long bestmatch,bestdist,dist;
- HashTable h2;
+ uint32_t bestmatch,bestdist,dist;
+ HashTable *h2;
printf ("nearest neighbour search (full search)..."); fflush(stdout); timer=clock();
h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
for (i=0;inew),pixel);
+ uint32_t oldDist=*dist;
+ uint32_t newDist;
+ newDist=_DISTSQR(&(data->new),&pixel);
if (data->secondPixel || newDistdata->furthestDistance) {
data->furthestDistance=oldDist;
- data->furthest.v=pixel->v;
+ data->furthest.v=pixel.v;
}
}
int
quantize2(Pixel *pixelData,
- unsigned long nPixels,
- unsigned long nQuantPixels,
+ uint32_t nPixels,
+ uint32_t nQuantPixels,
Pixel **palette,
- unsigned long *paletteLength,
- unsigned long **quantizedPixels,
+ uint32_t *paletteLength,
+ uint32_t **quantizedPixels,
int kmeans)
{
- HashTable h;
- unsigned long i;
- unsigned long mean[3];
+ HashTable *h;
+ uint32_t i;
+ uint32_t mean[3];
Pixel *p;
DistanceData data;
- unsigned long *qp;
- unsigned long *avgDist;
- unsigned long **avgDistSortKey;
+ uint32_t *qp;
+ uint32_t *avgDist;
+ uint32_t **avgDistSortKey;
p=malloc(sizeof(Pixel)*nQuantPixels);
if (!p) return 0;
mean[0]=mean[1]=mean[2]=0;
h=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp);
for (i=0;imode, "L") != 0 && strcmp(im->mode, "P") != 0 &&
- strcmp(im->mode, "RGB"))
+ strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0)
return ImagingError_ModeError();
+ /* only octree supports RGBA */
+ if (!strcmp(im->mode, "RGBA") && mode != 2)
+ return ImagingError_ModeError();
+
p = malloc(sizeof(Pixel) * im->xsize * im->ysize);
if (!p)
return ImagingError_MemoryError();
@@ -1529,7 +1519,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
p[i].c.b = pp[v*4+2];
}
- } else if (!strcmp(im->mode, "RGB")) {
+ } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) {
/* true colour */
for (i = y = 0; y < im->ysize; y++)
@@ -1541,6 +1531,8 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
return (Imaging) ImagingError_ValueError("internal error");
}
+ ImagingSectionEnter(&cookie);
+
switch (mode) {
case 0:
/* median cut */
@@ -1566,16 +1558,31 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
kmeans
);
break;
+ case 2:
+ if (!strcmp(im->mode, "RGBA")) {
+ withAlpha = 1;
+ }
+ result = quantize_octree(
+ p,
+ im->xsize*im->ysize,
+ colors,
+ &palette,
+ &paletteLength,
+ &newData,
+ withAlpha
+ );
+ break;
default:
result = 0;
break;
}
free(p);
+ ImagingSectionLeave(&cookie);
if (result) {
-
imOut = ImagingNew("P", im->xsize, im->ysize);
+ ImagingSectionEnter(&cookie);
for (i = y = 0; y < im->ysize; y++)
for (x=0; x < im->xsize; x++)
@@ -1589,7 +1596,11 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
*pp++ = palette[i].c.r;
*pp++ = palette[i].c.g;
*pp++ = palette[i].c.b;
- *pp++ = 255;
+ if (withAlpha) {
+ *pp++ = palette[i].c.a;
+ } else {
+ *pp++ = 255;
+ }
}
for (; i < 256; i++) {
*pp++ = 0;
@@ -1598,7 +1609,12 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans)
*pp++ = 255;
}
+ if (withAlpha) {
+ strcpy(imOut->palette->mode, "RGBA");
+ }
+
free(palette);
+ ImagingSectionLeave(&cookie);
return imOut;
diff --git a/libImaging/Quant.h b/libImaging/Quant.h
deleted file mode 100644
index 0de485a1a..000000000
--- a/libImaging/Quant.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * The Python Imaging Library
- * $Id$
- *
- * image quantizer
- *
- * Written by Toby J Sargeant .
- *
- * See the README file for information on usage and redistribution.
- */
-
-#ifndef __QUANT_H__
-#define __QUANT_H__
-
-typedef union {
- struct {
- unsigned char r,g,b,a;
- } c;
- struct {
- unsigned char v[4];
- } a;
- unsigned long v;
-} Pixel;
-
-int quantize(Pixel *,
- unsigned long,
- unsigned long,
- Pixel **,
- unsigned long *,
- unsigned long **,
- int);
-
-int quantize2(Pixel *,
- unsigned long,
- unsigned long,
- Pixel **,
- unsigned long *,
- unsigned long **,
- int);
-#endif
diff --git a/libImaging/QuantDefines.h b/libImaging/QuantDefines.h
deleted file mode 100644
index 5b080104e..000000000
--- a/libImaging/QuantDefines.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * The Python Imaging Library
- * $Id$
- *
- * image quantizer
- *
- * Written by Toby J Sargeant .
- *
- * See the README file for information on usage and redistribution.
- */
-
-#ifndef __DEFINES_H__
-#define __DEFINES_H__
-
-#if 0
-
-void *newMalloc(size_t,const char *,const char *,int);
-void newFree(void *,const char *,const char *,int);
-void print_malloc_stats();
-#define malloc(x) newMalloc(x,__FILE__,__FUNCTION__,__LINE__)
-#define free(x) newFree(x,__FILE__,__FUNCTION__,__LINE__)
-
-#endif
-
-#endif
diff --git a/libImaging/QuantHash.c b/libImaging/QuantHash.c
index e6438402d..a1f99ed3a 100644
--- a/libImaging/QuantHash.c
+++ b/libImaging/QuantHash.c
@@ -22,35 +22,35 @@
#include
#include "QuantHash.h"
-#include "QuantDefines.h"
-typedef struct _IntHashNode {
- struct _IntHashNode *next;
- void *key,*value;
-} IntHashNode;
+typedef struct _HashNode {
+ struct _HashNode *next;
+ HashKey_t key;
+ HashVal_t value;
+} HashNode;
-typedef struct _IntHashTable {
- IntHashNode **table;
- unsigned long length;
- unsigned long count;
+struct _HashTable {
+ HashNode **table;
+ uint32_t length;
+ uint32_t count;
HashFunc hashFunc;
HashCmpFunc cmpFunc;
- DestroyFunc keyDestroyFunc;
- DestroyFunc valDestroyFunc;
+ KeyDestroyFunc keyDestroyFunc;
+ ValDestroyFunc valDestroyFunc;
void *userData;
-} IntHashTable;
+};
#define MIN_LENGTH 11
#define RESIZE_FACTOR 3
-static int _hashtable_insert_node(IntHashTable *,IntHashNode *,int,int,CollisionFunc);
+static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc);
#if 0
-static int _hashtable_test(IntHashTable *);
+static int _hashtable_test(HashTable *);
#endif
-HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) {
- IntHashTable *h;
- h=malloc(sizeof(IntHashTable));
+HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) {
+ HashTable *h;
+ h=malloc(sizeof(HashTable));
if (!h) { return NULL; }
h->hashFunc=hf;
h->cmpFunc=cf;
@@ -59,25 +59,24 @@ HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) {
h->length=MIN_LENGTH;
h->count=0;
h->userData=NULL;
- h->table=malloc(sizeof(IntHashNode *)*h->length);
+ h->table=malloc(sizeof(HashNode *)*h->length);
if (!h->table) { free(h); return NULL; }
- memset (h->table,0,sizeof(IntHashNode *)*h->length);
- return (HashTable)h;
+ memset (h->table,0,sizeof(HashNode *)*h->length);
+ return h;
}
-static void _hashtable_destroy(HashTable H,const void *key,const void *val,void *u) {
- IntHashTable *h=(IntHashTable *)H;
- if (h->keyDestroyFunc&&key) {
- h->keyDestroyFunc((HashTable)h,(void *)key);
+static void _hashtable_destroy(const HashTable *h,const HashKey_t key,const HashVal_t val,void *u) {
+ if (h->keyDestroyFunc) {
+ h->keyDestroyFunc(h,key);
}
- if (h->valDestroyFunc&&val) {
- h->valDestroyFunc((HashTable)h,(void *)val);
+ if (h->valDestroyFunc) {
+ h->valDestroyFunc(h,val);
}
}
-static unsigned long _findPrime(unsigned long start,int dir) {
+static uint32_t _findPrime(uint32_t start,int dir) {
static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0};
- unsigned long t;
+ uint32_t t;
while (start>1) {
if (!unit[start&0x0f]) {
start+=dir;
@@ -94,22 +93,20 @@ static unsigned long _findPrime(unsigned long start,int dir) {
return start;
}
-static void _hashtable_rehash(IntHashTable *h,
- CollisionFunc cf,
- unsigned long newSize) {
- IntHashNode **oldTable=h->table;
- unsigned long i;
- IntHashNode *n,*nn;
- unsigned long oldSize;
+static void _hashtable_rehash(HashTable *h,CollisionFunc cf,uint32_t newSize) {
+ HashNode **oldTable=h->table;
+ uint32_t i;
+ HashNode *n,*nn;
+ uint32_t oldSize;
oldSize=h->length;
- h->table=malloc(sizeof(IntHashNode *)*newSize);
+ h->table=malloc(sizeof(HashNode *)*newSize);
if (!h->table) {
h->table=oldTable;
return;
}
h->length=newSize;
h->count=0;
- memset (h->table,0,sizeof(IntHashNode *)*h->length);
+ memset (h->table,0,sizeof(HashNode *)*h->length);
for (i=0;inext;
@@ -119,9 +116,9 @@ static void _hashtable_rehash(IntHashTable *h,
free(oldTable);
}
-static void _hashtable_resize(IntHashTable *h) {
- unsigned long newSize;
- unsigned long oldSize;
+static void _hashtable_resize(HashTable *h) {
+ uint32_t newSize;
+ uint32_t oldSize;
oldSize=h->length;
newSize=oldSize;
if (h->count*RESIZE_FACTORlength) {
@@ -136,13 +133,13 @@ static void _hashtable_resize(IntHashTable *h) {
}
#if 0
-static int _hashtable_test(IntHashTable *h) {
- unsigned long i;
+static int _hashtable_test(HashTable *h) {
+ uint32_t i;
int j;
- IntHashNode *n;
+ HashNode *n;
for (i=0;ilength;i++) {
for (n=h->table[i];n&&n->next;n=n->next) {
- j=h->cmpFunc((HashTable)h,n->key,n->next->key);
+ j=h->cmpFunc(h,n->key,n->next->key);
printf ("%c",j?(j<0?'-':'+'):'=');
}
printf ("\n");
@@ -151,26 +148,26 @@ static int _hashtable_test(IntHashTable *h) {
}
#endif
-static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,int update,CollisionFunc cf) {
- unsigned long hash=h->hashFunc((HashTable)h,node->key)%h->length;
- IntHashNode **n,*nv;
+static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int update,CollisionFunc cf) {
+ uint32_t hash=h->hashFunc(h,node->key)%h->length;
+ HashNode **n,*nv;
int i;
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
- i=h->cmpFunc((HashTable)h,nv->key,node->key);
+ i=h->cmpFunc(h,nv->key,node->key);
if (!i) {
if (cf) {
nv->key=node->key;
- cf((HashTable)h,&(nv->key),&(nv->value),node->key,node->value);
+ cf(h,&(nv->key),&(nv->value),node->key,node->value);
free(node);
return 1;
} else {
if (h->valDestroyFunc) {
- h->valDestroyFunc((HashTable)h,nv->value);
+ h->valDestroyFunc(h,nv->value);
}
if (h->keyDestroyFunc) {
- h->keyDestroyFunc((HashTable)h,nv->key);
+ h->keyDestroyFunc(h,nv->key);
}
nv->key=node->key;
nv->value=node->value;
@@ -192,17 +189,17 @@ static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,i
}
}
-static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int update) {
- IntHashNode **n,*nv;
- IntHashNode *t;
+static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize,int update) {
+ HashNode **n,*nv;
+ HashNode *t;
int i;
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
-
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
- i=h->cmpFunc((HashTable)h,nv->key,key);
+ i=h->cmpFunc(h,nv->key,key);
if (!i) {
- if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,nv->value); }
+ if (h->valDestroyFunc) { h->valDestroyFunc(h,nv->value); }
nv->value=val;
return 1;
} else if (i>0) {
@@ -210,7 +207,7 @@ static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int
}
}
if (!update) {
- t=malloc(sizeof(IntHashNode));
+ t=malloc(sizeof(HashNode));
if (!t) return 0;
t->next=*n;
*n=t;
@@ -224,15 +221,15 @@ static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int
}
}
-static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,void *newVal,int resize) {
- IntHashNode **n,*nv;
- IntHashNode *t;
+static int _hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *retVal,HashVal_t newVal,int resize) {
+ HashNode **n,*nv;
+ HashNode *t;
int i;
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
-
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
- i=h->cmpFunc((HashTable)h,nv->key,key);
+ i=h->cmpFunc(h,nv->key,key);
if (!i) {
*retVal=nv->value;
return 1;
@@ -240,7 +237,7 @@ static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,v
break;
}
}
- t=malloc(sizeof(IntHashNode));
+ t=malloc(sizeof(HashNode));
if (!t) return 0;
t->next=*n;
*n=t;
@@ -252,26 +249,25 @@ static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,v
return 1;
}
-int hashtable_insert_or_update_computed(HashTable H,
- void *key,
+int hashtable_insert_or_update_computed(HashTable *h,
+ HashKey_t key,
ComputeFunc newFunc,
ComputeFunc existsFunc) {
- IntHashTable *h=(IntHashTable *)H;
- IntHashNode **n,*nv;
- IntHashNode *t;
+ HashNode **n,*nv;
+ HashNode *t;
int i;
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
-
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+
for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
nv=*n;
- i=h->cmpFunc((HashTable)h,nv->key,key);
+ i=h->cmpFunc(h,nv->key,key);
if (!i) {
- void *old=nv->value;
+ HashVal_t old=nv->value;
if (existsFunc) {
- existsFunc(H,nv->key,&(nv->value));
+ existsFunc(h,nv->key,&(nv->value));
if (nv->value!=old) {
if (h->valDestroyFunc) {
- h->valDestroyFunc((HashTable)h,old);
+ h->valDestroyFunc(h,old);
}
}
} else {
@@ -282,13 +278,13 @@ int hashtable_insert_or_update_computed(HashTable H,
break;
}
}
- t=malloc(sizeof(IntHashNode));
+ t=malloc(sizeof(HashNode));
if (!t) return 0;
t->key=key;
t->next=*n;
*n=t;
if (newFunc) {
- newFunc(H,t->key,&(t->value));
+ newFunc(h,t->key,&(t->value));
} else {
free(t);
return 0;
@@ -298,52 +294,47 @@ int hashtable_insert_or_update_computed(HashTable H,
return 1;
}
-int hashtable_update(HashTable H,void *key,void *val) {
- IntHashTable *h=(IntHashTable *)H;
+int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val) {
return _hashtable_insert(h,key,val,1,0);
}
-int hashtable_insert(HashTable H,void *key,void *val) {
- IntHashTable *h=(IntHashTable *)H;
+int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val) {
return _hashtable_insert(h,key,val,1,0);
}
-void hashtable_foreach_update(HashTable H,IteratorUpdateFunc i,void *u) {
- IntHashTable *h=(IntHashTable *)H;
- IntHashNode *n;
- unsigned long x;
+void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u) {
+ HashNode *n;
+ uint32_t x;
if (h->table) {
for (x=0;xlength;x++) {
for (n=h->table[x];n;n=n->next) {
- i((HashTable)h,n->key,(void **)&(n->value),u);
+ i(h,n->key,&(n->value),u);
}
}
}
}
-void hashtable_foreach(HashTable H,IteratorFunc i,void *u) {
- IntHashTable *h=(IntHashTable *)H;
- IntHashNode *n;
- unsigned long x;
+void hashtable_foreach(HashTable *h,IteratorFunc i,void *u) {
+ HashNode *n;
+ uint32_t x;
if (h->table) {
for (x=0;xlength;x++) {
for (n=h->table[x];n;n=n->next) {
- i((HashTable)h,n->key,n->value,u);
+ i(h,n->key,n->value,u);
}
}
}
}
-void hashtable_free(HashTable H) {
- IntHashTable *h=(IntHashTable *)H;
- IntHashNode *n,*nn;
- unsigned long i;
+void hashtable_free(HashTable *h) {
+ HashNode *n,*nn;
+ uint32_t i;
if (h->table) {
if (h->keyDestroyFunc || h->keyDestroyFunc) {
- hashtable_foreach(H,_hashtable_destroy,NULL);
+ hashtable_foreach(h,_hashtable_destroy,NULL);
}
for (i=0;ilength;i++) {
for (n=h->table[i];n;n=nn) {
@@ -356,31 +347,29 @@ void hashtable_free(HashTable H) {
free(h);
}
-DestroyFunc hashtable_set_value_destroy_func(HashTable H,DestroyFunc d) {
- IntHashTable *h=(IntHashTable *)H;
- DestroyFunc r=h->valDestroyFunc;
+ValDestroyFunc hashtable_set_value_destroy_func(HashTable *h,ValDestroyFunc d) {
+ ValDestroyFunc r=h->valDestroyFunc;
h->valDestroyFunc=d;
return r;
}
-DestroyFunc hashtable_set_key_destroy_func(HashTable H,DestroyFunc d) {
- IntHashTable *h=(IntHashTable *)H;
- DestroyFunc r=h->keyDestroyFunc;
+KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *h,KeyDestroyFunc d) {
+ KeyDestroyFunc r=h->keyDestroyFunc;
h->keyDestroyFunc=d;
return r;
}
-static int _hashtable_remove(IntHashTable *h,
- const void *key,
- void **keyRet,
- void **valRet,
+static int _hashtable_remove(HashTable *h,
+ const HashKey_t key,
+ HashKey_t *keyRet,
+ HashVal_t *valRet,
int resize) {
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
- IntHashNode *n,*p;
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+ HashNode *n,*p;
int i;
-
+
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
- i=h->cmpFunc((HashTable)h,n->key,key);
+ i=h->cmpFunc(h,n->key,key);
if (!i) {
if (p) p=n->next; else h->table[hash]=n->next;
*keyRet=n->key;
@@ -395,17 +384,17 @@ static int _hashtable_remove(IntHashTable *h,
return 0;
}
-static int _hashtable_delete(IntHashTable *h,const void *key,int resize) {
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
- IntHashNode *n,*p;
+static int _hashtable_delete(HashTable *h,const HashKey_t key,int resize) {
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+ HashNode *n,*p;
int i;
-
+
for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
- i=h->cmpFunc((HashTable)h,n->key,key);
+ i=h->cmpFunc(h,n->key,key);
if (!i) {
if (p) p=n->next; else h->table[hash]=n->next;
- if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,n->value); }
- if (h->keyDestroyFunc) { h->keyDestroyFunc((HashTable)h,n->key); }
+ if (h->valDestroyFunc) { h->valDestroyFunc(h,n->value); }
+ if (h->keyDestroyFunc) { h->keyDestroyFunc(h,n->key); }
free(n);
h->count++;
return 1;
@@ -416,39 +405,33 @@ static int _hashtable_delete(IntHashTable *h,const void *key,int resize) {
return 0;
}
-int hashtable_remove(HashTable H,const void *key,void **keyRet,void **valRet) {
- IntHashTable *h=(IntHashTable *)H;
+int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet) {
return _hashtable_remove(h,key,keyRet,valRet,1);
}
-int hashtable_delete(HashTable H,const void *key) {
- IntHashTable *h=(IntHashTable *)H;
+int hashtable_delete(HashTable *h,const HashKey_t key) {
return _hashtable_delete(h,key,1);
}
-void hashtable_rehash_compute(HashTable H,CollisionFunc cf) {
- IntHashTable *h=(IntHashTable *)H;
+void hashtable_rehash_compute(HashTable *h,CollisionFunc cf) {
_hashtable_rehash(h,cf,h->length);
}
-void hashtable_rehash(HashTable H) {
- IntHashTable *h=(IntHashTable *)H;
+void hashtable_rehash(HashTable *h) {
_hashtable_rehash(h,NULL,h->length);
}
-int hashtable_lookup_or_insert(HashTable H,void *key,void **valp,void *val) {
- IntHashTable *h=(IntHashTable *)H;
+int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val) {
return _hashtable_lookup_or_insert(h,key,valp,val,1);
}
-int hashtable_lookup(const HashTable H,const void *key,void **valp) {
- IntHashTable *h=(IntHashTable *)H;
- unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
- IntHashNode *n;
+int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp) {
+ uint32_t hash=h->hashFunc(h,key)%h->length;
+ HashNode *n;
int i;
-
+
for (n=h->table[hash];n;n=n->next) {
- i=h->cmpFunc((HashTable)h,n->key,key);
+ i=h->cmpFunc(h,n->key,key);
if (!i) {
*valp=n->value;
return 1;
@@ -459,18 +442,15 @@ int hashtable_lookup(const HashTable H,const void *key,void **valp) {
return 0;
}
-unsigned long hashtable_get_count(const HashTable H) {
- IntHashTable *h=(IntHashTable *)H;
+uint32_t hashtable_get_count(const HashTable *h) {
return h->count;
}
-void *hashtable_get_user_data(const HashTable H) {
- IntHashTable *h=(IntHashTable *)H;
+void *hashtable_get_user_data(const HashTable *h) {
return h->userData;
}
-void *hashtable_set_user_data(HashTable H,void *data) {
- IntHashTable *h=(IntHashTable *)H;
+void *hashtable_set_user_data(HashTable *h,void *data) {
void *r=h->userData;
h->userData=data;
return r;
diff --git a/libImaging/QuantHash.h b/libImaging/QuantHash.h
index b98b56eaa..028b4af89 100644
--- a/libImaging/QuantHash.h
+++ b/libImaging/QuantHash.h
@@ -9,28 +9,41 @@
* See the README file for information on usage and redistribution.
*/
-#ifndef __HASH_H__
-#define __HASH_H__
+#ifndef __QUANTHASH_H__
+#define __QUANTHASH_H__
#include "QuantTypes.h"
-HashTable hashtable_new(HashFunc,HashCmpFunc);
-void hashtable_free(HashTable);
-void hashtable_foreach(HashTable,IteratorFunc,void *);
-void hashtable_foreach_update(HashTable,IteratorUpdateFunc,void *);
-int hashtable_insert(HashTable,void *,void *);
-int hashtable_update(HashTable,void *,void *);
-int hashtable_lookup(const HashTable,const void *,void **);
-int hashtable_lookup_or_insert(HashTable,void *,void **,void *);
-int hashtable_insert_or_update_computed(HashTable,void *,ComputeFunc,ComputeFunc);
-int hashtable_delete(HashTable,const void *);
-int hashtable_remove(HashTable,const void *,void **,void **);
-void *hashtable_set_user_data(HashTable,void *);
-void *hashtable_get_user_data(const HashTable);
-DestroyFunc hashtable_set_key_destroy_func(HashTable,DestroyFunc);
-DestroyFunc hashtable_set_value_destroy_func(HashTable,DestroyFunc);
-unsigned long hashtable_get_count(const HashTable);
-void hashtable_rehash(HashTable);
-void hashtable_rehash_compute(HashTable,CollisionFunc);
+typedef struct _HashTable HashTable;
+typedef Pixel HashKey_t;
+typedef uint32_t HashVal_t;
-#endif
+typedef uint32_t (*HashFunc)(const HashTable *,const HashKey_t);
+typedef int (*HashCmpFunc)(const HashTable *,const HashKey_t,const HashKey_t);
+typedef void (*IteratorFunc)(const HashTable *,const HashKey_t,const HashVal_t,void *);
+typedef void (*IteratorUpdateFunc)(const HashTable *,const HashKey_t,HashVal_t *,void *);
+typedef void (*KeyDestroyFunc)(const HashTable *,HashKey_t);
+typedef void (*ValDestroyFunc)(const HashTable *,HashVal_t);
+typedef void (*ComputeFunc)(const HashTable *,const HashKey_t,HashVal_t *);
+typedef void (*CollisionFunc)(const HashTable *,HashKey_t *,HashVal_t *,HashKey_t,HashVal_t);
+
+HashTable * hashtable_new(HashFunc hf,HashCmpFunc cf);
+void hashtable_free(HashTable *h);
+void hashtable_foreach(HashTable *h,IteratorFunc i,void *u);
+void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u);
+int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val);
+int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val);
+int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp);
+int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val);
+int hashtable_insert_or_update_computed(HashTable *h,HashKey_t key,ComputeFunc newFunc,ComputeFunc existsFunc);
+int hashtable_delete(HashTable *h,const HashKey_t key);
+int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet);
+void *hashtable_set_user_data(HashTable *h,void *data);
+void *hashtable_get_user_data(const HashTable *h);
+KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *,KeyDestroyFunc d);
+ValDestroyFunc hashtable_set_value_destroy_func(HashTable *,ValDestroyFunc d);
+uint32_t hashtable_get_count(const HashTable *h);
+void hashtable_rehash(HashTable *h);
+void hashtable_rehash_compute(HashTable *h,CollisionFunc cf);
+
+#endif // __QUANTHASH_H__
diff --git a/libImaging/QuantHeap.c b/libImaging/QuantHeap.c
index 9332a5cd7..9006903b7 100644
--- a/libImaging/QuantHeap.c
+++ b/libImaging/QuantHeap.c
@@ -21,31 +21,29 @@
#include
#include
-#include "QuantHash.h"
-#include "QuantDefines.h"
+#include "QuantHeap.h"
-typedef struct {
+struct _Heap {
void **heap;
int heapsize;
int heapcount;
HeapCmpFunc cf;
-} IntHeap;
+};
#define INITIAL_SIZE 256
-#define DEBUG
+// #define DEBUG
#ifdef DEBUG
-static int _heap_test(Heap);
+static int _heap_test(Heap *);
#endif
-void ImagingQuantHeapFree(Heap H) {
- IntHeap *h=(IntHeap *)H;
+void ImagingQuantHeapFree(Heap *h) {
free(h->heap);
free(h);
}
-static int _heap_grow(IntHeap *h,int newsize) {
+static int _heap_grow(Heap *h,int newsize) {
void *newheap;
if (!newsize) newsize=h->heapsize<<1;
if (newsizeheapsize) return 0;
@@ -59,15 +57,14 @@ static int _heap_grow(IntHeap *h,int newsize) {
}
#ifdef DEBUG
-static int _heap_test(Heap H) {
- IntHeap *h=(IntHeap *)H;
+static int _heap_test(Heap *h) {
int k;
for (k=1;k*2<=h->heapcount;k++) {
- if (h->cf(H,h->heap[k],h->heap[k*2])<0) {
+ if (h->cf(h,h->heap[k],h->heap[k*2])<0) {
printf ("heap is bad\n");
return 0;
}
- if (k*2+1<=h->heapcount && h->cf(H,h->heap[k],h->heap[k*2+1])<0) {
+ if (k*2+1<=h->heapcount && h->cf(h,h->heap[k],h->heap[k*2+1])<0) {
printf ("heap is bad\n");
return 0;
}
@@ -76,8 +73,7 @@ static int _heap_test(Heap H) {
}
#endif
-int ImagingQuantHeapRemove(Heap H,void **r) {
- IntHeap *h=(IntHeap *)H;
+int ImagingQuantHeapRemove(Heap* h,void **r) {
int k,l;
void *v;
@@ -89,31 +85,30 @@ int ImagingQuantHeapRemove(Heap H,void **r) {
for (k=1;k*2<=h->heapcount;k=l) {
l=k*2;
if (lheapcount) {
- if (h->cf(H,h->heap[l],h->heap[l+1])<0) {
+ if (h->cf(h,h->heap[l],h->heap[l+1])<0) {
l++;
}
}
- if (h->cf(H,v,h->heap[l])>0) {
+ if (h->cf(h,v,h->heap[l])>0) {
break;
}
h->heap[k]=h->heap[l];
}
h->heap[k]=v;
#ifdef DEBUG
- if (!_heap_test(H)) { printf ("oops - heap_remove messed up the heap\n"); exit(1); }
+ if (!_heap_test(h)) { printf ("oops - heap_remove messed up the heap\n"); exit(1); }
#endif
return 1;
}
-int ImagingQuantHeapAdd(Heap H,void *val) {
- IntHeap *h=(IntHeap *)H;
+int ImagingQuantHeapAdd(Heap *h,void *val) {
int k;
if (h->heapcount==h->heapsize-1) {
_heap_grow(h,0);
}
k=++h->heapcount;
while (k!=1) {
- if (h->cf(H,val,h->heap[k/2])<=0) {
+ if (h->cf(h,val,h->heap[k/2])<=0) {
break;
}
h->heap[k]=h->heap[k/2];
@@ -121,13 +116,12 @@ int ImagingQuantHeapAdd(Heap H,void *val) {
}
h->heap[k]=val;
#ifdef DEBUG
- if (!_heap_test(H)) { printf ("oops - heap_add messed up the heap\n"); exit(1); }
+ if (!_heap_test(h)) { printf ("oops - heap_add messed up the heap\n"); exit(1); }
#endif
return 1;
}
-int ImagingQuantHeapTop(Heap H,void **r) {
- IntHeap *h=(IntHeap *)H;
+int ImagingQuantHeapTop(Heap *h,void **r) {
if (!h->heapcount) {
return 0;
}
@@ -136,15 +130,14 @@ int ImagingQuantHeapTop(Heap H,void **r) {
}
Heap *ImagingQuantHeapNew(HeapCmpFunc cf) {
- IntHeap *h;
-
- h=malloc(sizeof(IntHeap));
+ Heap *h;
+
+ h=malloc(sizeof(Heap));
if (!h) return NULL;
h->heapsize=INITIAL_SIZE;
h->heap=malloc(sizeof(void *)*h->heapsize);
if (!h->heap) { free(h); return NULL; }
h->heapcount=0;
h->cf=cf;
- return (Heap)h;
+ return h;
}
-
diff --git a/libImaging/QuantHeap.h b/libImaging/QuantHeap.h
index 5a213c42a..77bf0d9d5 100644
--- a/libImaging/QuantHeap.h
+++ b/libImaging/QuantHeap.h
@@ -9,15 +9,19 @@
* See the README file for information on usage and redistribution.
*/
-#ifndef __HEAP_H__
-#define __HEAP_H__
+#ifndef __QUANTHEAP_H__
+#define __QUANTHEAP_H__
#include "QuantTypes.h"
-void ImagingQuantHeapFree(Heap);
-int ImagingQuantHeapRemove(Heap,void **);
-int ImagingQuantHeapAdd(Heap,void *);
-int ImagingQuantHeapTop(Heap,void **);
+typedef struct _Heap Heap;
+
+typedef int (*HeapCmpFunc)(const Heap *,const void *,const void *);
+
+void ImagingQuantHeapFree(Heap *);
+int ImagingQuantHeapRemove(Heap *,void **);
+int ImagingQuantHeapAdd(Heap *,void *);
+int ImagingQuantHeapTop(Heap *,void **);
Heap *ImagingQuantHeapNew(HeapCmpFunc);
-#endif
+#endif // __QUANTHEAP_H__
diff --git a/libImaging/QuantOctree.c b/libImaging/QuantOctree.c
new file mode 100644
index 000000000..b233ccd2e
--- /dev/null
+++ b/libImaging/QuantOctree.c
@@ -0,0 +1,454 @@
+/* Copyright (c) 2010 Oliver Tonnhofer , Omniscale
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+*/
+
+/*
+// This file implements a variation of the octree color quantization algorithm.
+*/
+
+#include
+#include
+#include
+
+#include "QuantOctree.h"
+
+typedef struct _ColorBucket{
+ /* contains palette index when used for look up cube */
+ uint32_t count;
+ uint64_t r;
+ uint64_t g;
+ uint64_t b;
+ uint64_t a;
+} *ColorBucket;
+
+typedef struct _ColorCube{
+ unsigned int rBits, gBits, bBits, aBits;
+ unsigned int rWidth, gWidth, bWidth, aWidth;
+ unsigned int rOffset, gOffset, bOffset, aOffset;
+
+ long size;
+ ColorBucket buckets;
+} *ColorCube;
+
+#define MAX(a, b) (a)>(b) ? (a) : (b)
+
+static ColorCube
+new_color_cube(int r, int g, int b, int a) {
+ ColorCube cube;
+
+ cube = malloc(sizeof(struct _ColorCube));
+ if (!cube) return NULL;
+
+ cube->rBits = MAX(r, 0);
+ cube->gBits = MAX(g, 0);
+ cube->bBits = MAX(b, 0);
+ cube->aBits = MAX(a, 0);
+
+ /* the width of the cube for each dimension */
+ cube->rWidth = 1<rBits;
+ cube->gWidth = 1<gBits;
+ cube->bWidth = 1<bBits;
+ cube->aWidth = 1<aBits;
+
+ /* the offsets of each color */
+
+ cube->rOffset = cube->gBits + cube->bBits + cube->aBits;
+ cube->gOffset = cube->bBits + cube->aBits;
+ cube->bOffset = cube->aBits;
+ cube->aOffset = 0;
+
+ /* the number of color buckets */
+ cube->size = cube->rWidth * cube->gWidth * cube->bWidth * cube->aWidth;
+ cube->buckets = calloc(cube->size, sizeof(struct _ColorBucket));
+
+ if (!cube->buckets) {
+ free(cube);
+ return NULL;
+ }
+ return cube;
+}
+
+static void
+free_color_cube(ColorCube cube) {
+ if (cube != NULL) {
+ free(cube->buckets);
+ free(cube);
+ }
+}
+
+static long
+color_bucket_offset_pos(const ColorCube cube,
+ unsigned int r, unsigned int g, unsigned int b, unsigned int a)
+{
+ return r<rOffset | g<gOffset | b<bOffset | a<aOffset;
+}
+
+static long
+color_bucket_offset(const ColorCube cube, const Pixel *p) {
+ unsigned int r = p->c.r>>(8-cube->rBits);
+ unsigned int g = p->c.g>>(8-cube->gBits);
+ unsigned int b = p->c.b>>(8-cube->bBits);
+ unsigned int a = p->c.a>>(8-cube->aBits);
+ return color_bucket_offset_pos(cube, r, g, b, a);
+}
+
+static ColorBucket
+color_bucket_from_cube(const ColorCube cube, const Pixel *p) {
+ unsigned int offset = color_bucket_offset(cube, p);
+ return &cube->buckets[offset];
+}
+
+static void
+add_color_to_color_cube(const ColorCube cube, const Pixel *p) {
+ ColorBucket bucket = color_bucket_from_cube(cube, p);
+ bucket->count += 1;
+ bucket->r += p->c.r;
+ bucket->g += p->c.g;
+ bucket->b += p->c.b;
+ bucket->a += p->c.a;
+}
+
+static long
+count_used_color_buckets(const ColorCube cube) {
+ long usedBuckets = 0;
+ long i;
+ for (i=0; i < cube->size; i++) {
+ if (cube->buckets[i].count > 0) {
+ usedBuckets += 1;
+ }
+ }
+ return usedBuckets;
+}
+
+static void
+avg_color_from_color_bucket(const ColorBucket bucket, Pixel *dst) {
+ float count = bucket->count;
+ dst->c.r = (int)(bucket->r / count);
+ dst->c.g = (int)(bucket->g / count);
+ dst->c.b = (int)(bucket->b / count);
+ dst->c.a = (int)(bucket->a / count);
+}
+
+static int
+compare_bucket_count(const ColorBucket a, const ColorBucket b) {
+ return b->count - a->count;
+}
+
+static ColorBucket
+create_sorted_color_palette(const ColorCube cube) {
+ ColorBucket buckets;
+ buckets = malloc(sizeof(struct _ColorBucket)*cube->size);
+ if (!buckets) return NULL;
+ memcpy(buckets, cube->buckets, sizeof(struct _ColorBucket)*cube->size);
+
+ qsort(buckets, cube->size, sizeof(struct _ColorBucket),
+ (int (*)(void const *, void const *))&compare_bucket_count);
+
+ return buckets;
+}
+
+void add_bucket_values(ColorBucket src, ColorBucket dst) {
+ dst->count += src->count;
+ dst->r += src->r;
+ dst->g += src->g;
+ dst->b += src->b;
+ dst->a += src->a;
+}
+
+/* expand or shrink a given cube to level */
+static ColorCube copy_color_cube(const ColorCube cube,
+ int rBits, int gBits, int bBits, int aBits)
+{
+ unsigned int r, g, b, a;
+ long src_pos, dst_pos;
+ unsigned int src_reduce[4] = {0}, dst_reduce[4] = {0};
+ unsigned int width[4];
+ ColorCube result;
+
+ result = new_color_cube(rBits, gBits, bBits, aBits);
+ if (!result) return NULL;
+
+ if (cube->rBits > rBits) {
+ dst_reduce[0] = cube->rBits - result->rBits;
+ width[0] = cube->rWidth;
+ } else {
+ src_reduce[0] = result->rBits - cube->rBits;
+ width[0] = result->rWidth;
+ }
+ if (cube->gBits > gBits) {
+ dst_reduce[1] = cube->gBits - result->gBits;
+ width[1] = cube->gWidth;
+ } else {
+ src_reduce[1] = result->gBits - cube->gBits;
+ width[1] = result->gWidth;
+ }
+ if (cube->bBits > bBits) {
+ dst_reduce[2] = cube->bBits - result->bBits;
+ width[2] = cube->bWidth;
+ } else {
+ src_reduce[2] = result->bBits - cube->bBits;
+ width[2] = result->bWidth;
+ }
+ if (cube->aBits > aBits) {
+ dst_reduce[3] = cube->aBits - result->aBits;
+ width[3] = cube->aWidth;
+ } else {
+ src_reduce[3] = result->aBits - cube->aBits;
+ width[3] = result->aWidth;
+ }
+
+ for (r=0; r>src_reduce[0],
+ g>>src_reduce[1],
+ b>>src_reduce[2],
+ a>>src_reduce[3]);
+ dst_pos = color_bucket_offset_pos(result,
+ r>>dst_reduce[0],
+ g>>dst_reduce[1],
+ b>>dst_reduce[2],
+ a>>dst_reduce[3]);
+ add_bucket_values(
+ &cube->buckets[src_pos],
+ &result->buckets[dst_pos]
+ );
+ }
+ }
+ }
+ }
+ return result;
+}
+
+void
+subtract_color_buckets(ColorCube cube, ColorBucket buckets, long nBuckets) {
+ ColorBucket minuend, subtrahend;
+ long i;
+ Pixel p;
+ for (i=0; icount -= subtrahend->count;
+ minuend->r -= subtrahend->r;
+ minuend->g -= subtrahend->g;
+ minuend->b -= subtrahend->b;
+ minuend->a -= subtrahend->a;
+ }
+}
+
+static void
+set_lookup_value(const ColorCube cube, const Pixel *p, long value) {
+ ColorBucket bucket = color_bucket_from_cube(cube, p);
+ bucket->count = value;
+}
+
+uint64_t
+lookup_color(const ColorCube cube, const Pixel *p) {
+ ColorBucket bucket = color_bucket_from_cube(cube, p);
+ return bucket->count;
+}
+
+void add_lookup_buckets(ColorCube cube, ColorBucket palette, long nColors, long offset) {
+ long i;
+ Pixel p;
+ for (i=offset; i 64).
+
+ For a quantization to 256 colors all 64 coarse colors will be used
+ plus the 192 most used color buckets from the fine color cube.
+ The average of all colors within one bucket is used as the actual
+ color for that bucket.
+
+ For images with alpha the cubes gets a forth dimension,
+ 8x16x8x8 and 4x4x4x4.
+ */
+
+ /* create fine cube */
+ fineCube = new_color_cube(cubeBits[0], cubeBits[1],
+ cubeBits[2], cubeBits[3]);
+ if (!fineCube) goto error;
+ for (i=0; i nQuantPixels)
+ nCoarseColors = nQuantPixels;
+
+ /* how many space do we have in our palette for fine colors? */
+ nFineColors = nQuantPixels - nCoarseColors;
+
+ /* create fine color palette */
+ paletteBucketsFine = create_sorted_color_palette(fineCube);
+ if (!paletteBucketsFine) goto error;
+
+ /* remove the used fine colors from the coarse cube */
+ subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors);
+
+ /* did the substraction cleared one or more coarse bucket? */
+ while (nCoarseColors > count_used_color_buckets(coarseCube)) {
+ /* then we can use the free buckets for fine colors */
+ nAlreadySubtracted = nFineColors;
+ nCoarseColors = count_used_color_buckets(coarseCube);
+ nFineColors = nQuantPixels - nCoarseColors;
+ subtract_color_buckets(coarseCube, &paletteBucketsFine[nAlreadySubtracted],
+ nFineColors-nAlreadySubtracted);
+ }
+
+ /* create our palette buckets with fine and coarse combined */
+ paletteBucketsCoarse = create_sorted_color_palette(coarseCube);
+ if (!paletteBucketsCoarse) goto error;
+ paletteBuckets = combined_palette(paletteBucketsCoarse, nCoarseColors,
+ paletteBucketsFine, nFineColors);
+
+ free(paletteBucketsFine);
+ paletteBucketsFine = NULL;
+ free(paletteBucketsCoarse);
+ paletteBucketsCoarse = NULL;
+
+ /* add all coarse colors to our coarse lookup cube. */
+ coarseLookupCube = new_color_cube(cubeBits[4], cubeBits[5],
+ cubeBits[6], cubeBits[7]);
+ if (!coarseLookupCube) goto error;
+ add_lookup_buckets(coarseLookupCube, paletteBuckets, nCoarseColors, 0);
+
+ /* expand coarse cube (64) to larger fine cube (4k). the value of each
+ coarse bucket is then present in the according 64 fine buckets. */
+ lookupCube = copy_color_cube(coarseLookupCube, cubeBits[0], cubeBits[1],
+ cubeBits[2], cubeBits[3]);
+ if (!lookupCube) goto error;
+
+ /* add fine colors to the lookup cube */
+ add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors);
+
+ /* create result pixles and map palatte indices */
+ qp = malloc(sizeof(Pixel)*nPixels);
+ if (!qp) goto error;
+ map_image_pixels(pixelData, nPixels, lookupCube, qp);
+
+ /* convert palette buckets to RGB pixel palette */
+ *palette = create_palette_array(paletteBuckets, nQuantPixels);
+ if (!(*palette)) goto error;
+
+ *quantizedPixels = qp;
+ *paletteLength = nQuantPixels;
+
+ free_color_cube(coarseCube);
+ free_color_cube(fineCube);
+ free_color_cube(lookupCube);
+ free_color_cube(coarseLookupCube);
+ free(paletteBuckets);
+ return 1;
+
+error:
+ /* everything is initialized to NULL
+ so we are safe to call free */
+ free(qp);
+ free_color_cube(lookupCube);
+ free_color_cube(coarseLookupCube);
+ free(paletteBucketsCoarse);
+ free(paletteBucketsFine);
+ free_color_cube(coarseCube);
+ free_color_cube(fineCube);
+ return 0;
+}
diff --git a/libImaging/QuantOctree.h b/libImaging/QuantOctree.h
new file mode 100644
index 000000000..968644eda
--- /dev/null
+++ b/libImaging/QuantOctree.h
@@ -0,0 +1,14 @@
+#ifndef __QUANT_OCTREE_H__
+#define __QUANT_OCTREE_H__
+
+#include "QuantTypes.h"
+
+int quantize_octree(Pixel *,
+ uint32_t,
+ uint32_t,
+ Pixel **,
+ uint32_t *,
+ uint32_t **,
+ int);
+
+#endif
diff --git a/libImaging/QuantTypes.h b/libImaging/QuantTypes.h
index 308b51c13..411485498 100644
--- a/libImaging/QuantTypes.h
+++ b/libImaging/QuantTypes.h
@@ -12,17 +12,21 @@
#ifndef __TYPES_H__
#define __TYPES_H__
-typedef void *HashTable;
-typedef void *Heap;
+#ifdef _MSC_VER
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include
+#endif
-typedef unsigned long (*HashFunc)(const HashTable,const void *);
-typedef int (*HashCmpFunc)(const HashTable,const void *,const void *);
-typedef void (*IteratorFunc)(const HashTable,const void *,const void *,void *);
-typedef void (*IteratorUpdateFunc)(const HashTable,const void *,void **,void *);
-typedef void (*DestroyFunc)(const HashTable,void *);
-typedef void (*ComputeFunc)(const HashTable,const void *,void **);
-typedef void (*CollisionFunc)(const HashTable,void **,void **,void *,void *);
-
-typedef int (*HeapCmpFunc)(const Heap,const void *,const void *);
+typedef union {
+ struct {
+ unsigned char r,g,b,a;
+ } c;
+ struct {
+ unsigned char v[4];
+ } a;
+ uint32_t v;
+} Pixel;
#endif
diff --git a/libImaging/RankFilter.c b/libImaging/RankFilter.c
index 84090f751..7bc4d5b10 100644
--- a/libImaging/RankFilter.c
+++ b/libImaging/RankFilter.c
@@ -81,6 +81,7 @@ ImagingRankFilter(Imaging im, int size, int rank)
size * sizeof(type));\
IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\
}\
+ free(buf); \
} while (0)
if (im->image8)
@@ -94,7 +95,7 @@ ImagingRankFilter(Imaging im, int size, int rank)
ImagingDelete(imOut);
return (Imaging) ImagingError_ModeError();
}
-
+
ImagingCopyInfo(imOut, im);
return imOut;
diff --git a/libImaging/RawDecode.c b/libImaging/RawDecode.c
index 5aadb2b44..40c0cb79a 100644
--- a/libImaging/RawDecode.c
+++ b/libImaging/RawDecode.c
@@ -69,7 +69,7 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
return ptr - buf;
/* Unpack data */
- state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize, ptr, state->xsize);
ptr += state->bytes;
diff --git a/libImaging/Storage.c b/libImaging/Storage.c
index 76a66c2a5..d31db5cb2 100644
--- a/libImaging/Storage.c
+++ b/libImaging/Storage.c
@@ -28,7 +28,7 @@
* 2003-09-26 fl Added "LA" and "PA" modes (experimental)
* 2005-10-02 fl Added image counter
*
- * Copyright (c) 1998-2005 by Secret Labs AB
+ * Copyright (c) 1998-2005 by Secret Labs AB
* Copyright (c) 1995-2005 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
@@ -36,6 +36,7 @@
#include "Imaging.h"
+#include
int ImagingNewCount = 0;
@@ -53,7 +54,7 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
im = (Imaging) calloc(1, size);
if (!im)
- return (Imaging) ImagingError_MemoryError();
+ return (Imaging) ImagingError_MemoryError();
/* Setup image descriptor */
im->xsize = xsize;
@@ -104,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;
@@ -177,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 */
@@ -194,8 +203,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
ImagingSectionLeave(&cookie);
if (!im->image) {
- free(im);
- return (Imaging) ImagingError_MemoryError();
+ free(im);
+ return (Imaging) ImagingError_MemoryError();
}
ImagingNewCount++;
@@ -218,16 +227,16 @@ ImagingNewEpilogue(Imaging im)
assume that it couldn't allocate the required amount of
memory. */
if (!im->destroy)
- return (Imaging) ImagingError_MemoryError();
+ return (Imaging) ImagingError_MemoryError();
/* Initialize alias pointers to pixel data. */
switch (im->pixelsize) {
case 1: case 2: case 3:
- im->image8 = (UINT8 **) im->image;
- break;
+ im->image8 = (UINT8 **) im->image;
+ break;
case 4:
- im->image32 = (INT32 **) im->image;
- break;
+ im->image32 = (INT32 **) im->image;
+ break;
}
return im;
@@ -237,16 +246,16 @@ void
ImagingDelete(Imaging im)
{
if (!im)
- return;
+ return;
if (im->palette)
- ImagingPaletteDelete(im->palette);
+ ImagingPaletteDelete(im->palette);
if (im->destroy)
- im->destroy(im);
+ im->destroy(im);
if (im->image)
- free(im->image);
+ free(im->image);
free(im);
}
@@ -262,9 +271,9 @@ ImagingDestroyArray(Imaging im)
int y;
if (im->image)
- for (y = 0; y < im->ysize; y++)
- if (im->image[y])
- free(im->image[y]);
+ for (y = 0; y < im->ysize; y++)
+ if (im->image[y])
+ free(im->image[y]);
}
Imaging
@@ -278,24 +287,24 @@ ImagingNewArray(const char *mode, int xsize, int ysize)
im = ImagingNewPrologue(mode, xsize, ysize);
if (!im)
- return NULL;
+ return NULL;
ImagingSectionEnter(&cookie);
/* Allocate image as an array of lines */
for (y = 0; y < im->ysize; y++) {
- p = (char *) malloc(im->linesize);
- if (!p) {
- ImagingDestroyArray(im);
- break;
- }
+ p = (char *) malloc(im->linesize);
+ if (!p) {
+ ImagingDestroyArray(im);
+ break;
+ }
im->image[y] = p;
}
ImagingSectionLeave(&cookie);
if (y == im->ysize)
- im->destroy = ImagingDestroyArray;
+ im->destroy = ImagingDestroyArray;
return ImagingNewEpilogue(im);
}
@@ -309,22 +318,22 @@ static void
ImagingDestroyBlock(Imaging im)
{
if (im->block)
- free(im->block);
+ free(im->block);
}
Imaging
ImagingNewBlock(const char *mode, int xsize, int ysize)
{
Imaging im;
- int y, i;
- int bytes;
+ Py_ssize_t y, i;
+ Py_ssize_t bytes;
im = ImagingNewPrologue(mode, xsize, ysize);
if (!im)
- return NULL;
+ return NULL;
/* Use a single block */
- bytes = im->ysize * im->linesize;
+ bytes = (Py_ssize_t) im->ysize * im->linesize;
if (bytes <= 0)
/* some platforms return NULL for malloc(0); this fix
prevents MemoryError on zero-sized images on such
@@ -333,13 +342,14 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
im->block = (char *) malloc(bytes);
if (im->block) {
+ memset(im->block, 0, bytes);
- for (y = i = 0; y < im->ysize; y++) {
- im->image[y] = im->block + i;
- i += im->linesize;
- }
+ for (y = i = 0; y < im->ysize; y++) {
+ im->image[y] = im->block + i;
+ i += im->linesize;
+ }
- im->destroy = ImagingDestroyBlock;
+ im->destroy = ImagingDestroyBlock;
}
@@ -350,9 +360,9 @@ ImagingNewBlock(const char *mode, int xsize, int ysize)
* Create a new, internally allocated, image.
*/
#if defined(IMAGING_SMALL_MODEL)
-#define THRESHOLD 16384L
+#define THRESHOLD 16384L
#else
-#define THRESHOLD (2048*2048*4L)
+#define THRESHOLD (2048*2048*4L)
#endif
Imaging
@@ -369,7 +379,7 @@ ImagingNew(const char* mode, int xsize, int ysize)
} else
bytes = strlen(mode); /* close enough */
- if ((long) xsize * ysize * bytes <= THRESHOLD) {
+ if ((Py_ssize_t) xsize * ysize * bytes <= THRESHOLD) {
im = ImagingNewBlock(mode, xsize, ysize);
if (im)
return im;
@@ -408,6 +418,6 @@ ImagingCopyInfo(Imaging destination, Imaging source)
if (source->palette) {
if (destination->palette)
ImagingPaletteDelete(destination->palette);
- destination->palette = ImagingPaletteDuplicate(source->palette);
+ destination->palette = ImagingPaletteDuplicate(source->palette);
}
}
diff --git a/libImaging/SunRleDecode.c b/libImaging/SunRleDecode.c
index bf34d9382..6c240e400 100644
--- a/libImaging/SunRleDecode.c
+++ b/libImaging/SunRleDecode.c
@@ -62,7 +62,7 @@ ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
}
memset(state->buffer + state->x, ptr[2], n);
-
+
ptr += 3;
bytes -= 3;
diff --git a/libImaging/TiffDecode.c b/libImaging/TiffDecode.c
new file mode 100644
index 000000000..787cd4506
--- /dev/null
+++ b/libImaging/TiffDecode.c
@@ -0,0 +1,422 @@
+/*
+ * The Python Imaging Library.
+ * $Id: //modules/pil/libImaging/TiffDecode.c#1 $
+ *
+ * LibTiff-based Group3 and Group4 decoder
+ *
+ *
+ * started modding to use non-private tiff functions to port to libtiff 4.x
+ * eds 3/12/12
+ *
+ */
+
+#include "Imaging.h"
+
+#ifdef HAVE_LIBTIFF
+
+#ifndef uint
+#define uint uint32
+#endif
+
+#include "TiffDecode.h"
+
+void dump_state(const TIFFSTATE *state){
+ TRACE(("State: Location %u size %d eof %d data: %p \n", (uint)state->loc,
+ (int)state->size, (uint)state->eof, state->data));
+}
+
+/*
+ procs for TIFFOpenClient
+*/
+
+tsize_t _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+ tsize_t to_read;
+
+ TRACE(("_tiffReadProc: %d \n", (int)size));
+ dump_state(state);
+
+ to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc);
+ TRACE(("to_read: %d\n", (int)to_read));
+
+ _TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read);
+ state->loc += (toff_t)to_read;
+
+ TRACE( ("location: %u\n", (uint)state->loc));
+ return to_read;
+}
+
+tsize_t _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+ tsize_t to_write;
+
+ TRACE(("_tiffWriteProc: %d \n", (int)size));
+ dump_state(state);
+
+ to_write = min(size, state->size - (tsize_t)state->loc);
+ if (state->flrealloc && size>to_write) {
+ tdata_t new;
+ tsize_t newsize=state->size;
+ while (newsize < (size + state->size)) {
+ newsize += 64*1024;
+ // newsize*=2; // UNDONE, by 64k chunks?
+ }
+ TRACE(("Reallocing in write to %d bytes\n", (int)newsize));
+ new = realloc(state->data, newsize);
+ if (!new) {
+ // fail out
+ return 0;
+ }
+ state->data = new;
+ state->size = newsize;
+ to_write = size;
+ }
+
+ TRACE(("to_write: %d\n", (int)to_write));
+
+ _TIFFmemcpy((UINT8 *)state->data + state->loc, buf, to_write);
+ state->loc += (toff_t)to_write;
+ state->eof = max(state->loc, state->eof);
+
+ dump_state(state);
+ return to_write;
+}
+
+toff_t _tiffSeekProc(thandle_t hdata, toff_t off, int whence) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+
+ TRACE(("_tiffSeekProc: off: %u whence: %d \n", (uint)off, whence));
+ dump_state(state);
+ switch (whence) {
+ case 0:
+ state->loc = off;
+ break;
+ case 1:
+ state->loc += off;
+ break;
+ case 2:
+ state->loc = state->eof + off;
+ break;
+ }
+ dump_state(state);
+ return state->loc;
+}
+
+int _tiffCloseProc(thandle_t hdata) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+
+ TRACE(("_tiffCloseProc \n"));
+ dump_state(state);
+
+ return 0;
+}
+
+
+toff_t _tiffSizeProc(thandle_t hdata) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+
+ TRACE(("_tiffSizeProc \n"));
+ dump_state(state);
+
+ return (toff_t)state->size;
+}
+int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
+ TIFFSTATE *state = (TIFFSTATE *)hdata;
+
+ TRACE(("_tiffMapProc input size: %u, data: %p\n", (uint)*psize, *pbase));
+ dump_state(state);
+
+ *pbase = state->data;
+ *psize = state->size;
+ TRACE(("_tiffMapProc returning size: %u, data: %p\n", (uint)*psize, *pbase));
+ return (1);
+}
+
+int _tiffNullMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) {
+ (void) hdata; (void) pbase; (void) psize;
+ return (0);
+}
+
+void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
+ TRACE(("_tiffUnMapProc\n"));
+ (void) hdata; (void) base; (void) size;
+}
+
+int ImagingLibTiffInit(ImagingCodecState state, int fp) {
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+
+ TRACE(("initing libtiff\n"));
+ TRACE(("filepointer: %d \n", fp));
+ TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
+ state->x, state->y, state->ystep));
+ TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
+ state->xoff, state->yoff));
+ TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
+ TRACE(("State: context %p \n", state->context));
+
+ clientstate->loc = 0;
+ clientstate->size = 0;
+ clientstate->data = 0;
+ clientstate->fp = fp;
+ clientstate->eof = 0;
+
+ return 1;
+}
+
+int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+ char *filename = "tempfile.tif";
+ char *mode = "r";
+ TIFF *tiff;
+ int size;
+
+
+ /* buffer is the encoded file, bytes is the length of the encoded file */
+ /* it all ends up in state->buffer, which is a uint8* from Imaging.h */
+
+ TRACE(("in decoder: bytes %d\n", bytes));
+ TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
+ state->x, state->y, state->ystep));
+ TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
+ state->xoff, state->yoff));
+ TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
+ TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
+ TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3]));
+ TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
+ im->mode, im->type, im->bands, im->xsize, im->ysize));
+ TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n",
+ im->image8, im->image32, im->image, im->block));
+ TRACE(("Image: pixelsize: %d, linesize %d \n",
+ im->pixelsize, im->linesize));
+
+ dump_state(clientstate);
+ clientstate->size = bytes;
+ clientstate->eof = clientstate->size;
+ clientstate->loc = 0;
+ clientstate->data = (tdata_t)buffer;
+ clientstate->flrealloc = 0;
+
+ dump_state(clientstate);
+
+ TIFFSetWarningHandler(NULL);
+ TIFFSetWarningHandlerExt(NULL);
+
+ if (clientstate->fp) {
+ TRACE(("Opening using fd: %d\n",clientstate->fp));
+ lseek(clientstate->fp,0,SEEK_SET); // Sometimes, I get it set to the end.
+ tiff = TIFFFdOpen(clientstate->fp, filename, mode);
+ } else {
+ TRACE(("Opening from string\n"));
+ tiff = TIFFClientOpen(filename, mode,
+ (thandle_t) clientstate,
+ _tiffReadProc, _tiffWriteProc,
+ _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
+ _tiffMapProc, _tiffUnmapProc);
+ }
+
+ if (!tiff){
+ TRACE(("Error, didn't get the tiff\n"));
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ size = TIFFScanlineSize(tiff);
+ TRACE(("ScanlineSize: %d \n", size));
+ if (size > state->bytes) {
+ TRACE(("Error, scanline size > buffer size\n"));
+ state->errcode = IMAGING_CODEC_BROKEN;
+ TIFFClose(tiff);
+ return -1;
+ }
+
+ // Have to do this row by row and shove stuff into the buffer that way,
+ // with shuffle. (or, just alloc a buffer myself, then figure out how to get it
+ // back in. Can't use read encoded stripe.
+
+ // This thing pretty much requires that I have the whole image in one shot.
+ // Prehaps a stub version would work better???
+ while(state->y < state->ysize){
+ if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) {
+ TRACE(("Decode Error, row %d\n", state->y));
+ state->errcode = IMAGING_CODEC_BROKEN;
+ TIFFClose(tiff);
+ return -1;
+ }
+ /* TRACE(("Decoded row %d \n", state->y)); */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize,
+ state->buffer,
+ state->xsize);
+
+ state->y++;
+ }
+
+ TIFFClose(tiff);
+ TRACE(("Done Decoding, Returning \n"));
+ // Returning -1 here to force ImageFile.load to break, rather than
+ // even think about looping back around.
+ return -1;
+}
+
+int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) {
+ // Open the FD or the pointer as a tiff file, for writing.
+ // We may have to do some monkeying around to make this really work.
+ // If we have a fp, then we're good.
+ // If we have a memory string, we're probably going to have to malloc, then
+ // shuffle bytes into the writescanline process.
+ // Going to have to deal with the directory as well.
+
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+ int bufsize = 64*1024;
+ char *mode = "w";
+
+ TRACE(("initing libtiff\n"));
+ TRACE(("Filename %s, filepointer: %d \n", filename, fp));
+ TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
+ state->x, state->y, state->ystep));
+ TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
+ state->xoff, state->yoff));
+ TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
+ TRACE(("State: context %p \n", state->context));
+
+ clientstate->loc = 0;
+ clientstate->size = 0;
+ clientstate->eof =0;
+ clientstate->data = 0;
+ clientstate->flrealloc = 0;
+ clientstate->fp = fp;
+
+ state->state = 0;
+
+ if (fp) {
+ TRACE(("Opening using fd: %d for writing \n",clientstate->fp));
+ clientstate->tiff = TIFFFdOpen(clientstate->fp, filename, mode);
+ } else {
+ // malloc a buffer to write the tif, we're going to need to realloc or something if we need bigger.
+ TRACE(("Opening a buffer for writing \n"));
+ clientstate->data = malloc(bufsize);
+ clientstate->size = bufsize;
+ clientstate->flrealloc=1;
+
+ if (!clientstate->data) {
+ TRACE(("Error, couldn't allocate a buffer of size %d\n", bufsize));
+ return 0;
+ }
+
+ clientstate->tiff = TIFFClientOpen(filename, mode,
+ (thandle_t) clientstate,
+ _tiffReadProc, _tiffWriteProc,
+ _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
+ _tiffNullMapProc, _tiffUnmapProc); /*force no mmap*/
+
+ }
+
+ if (!clientstate->tiff) {
+ TRACE(("Error, couldn't open tiff file\n"));
+ return 0;
+ }
+
+ return 1;
+
+}
+
+int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){
+ // after tif_dir.c->TIFFSetField.
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+ va_list ap;
+ int status;
+
+ va_start(ap, tag);
+ status = TIFFVSetField(clientstate->tiff, tag, ap);
+ va_end(ap);
+ return status;
+}
+
+
+int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
+ /* One shot encoder. Encode everything to the tiff in the clientstate.
+ If we're running off of a FD, then run once, we're good, everything
+ ends up in the file, we close and we're done.
+
+ If we're going to memory, then we need to write the whole file into memory, then
+ parcel it back out to the pystring buffer bytes at a time.
+
+ */
+
+ TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
+ TIFF *tiff = clientstate->tiff;
+
+ TRACE(("in encoder: bytes %d\n", bytes));
+ TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
+ state->x, state->y, state->ystep));
+ TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
+ state->xoff, state->yoff));
+ TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
+ TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
+ TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3]));
+ TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
+ im->mode, im->type, im->bands, im->xsize, im->ysize));
+ TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n",
+ im->image8, im->image32, im->image, im->block));
+ TRACE(("Image: pixelsize: %d, linesize %d \n",
+ im->pixelsize, im->linesize));
+
+ dump_state(clientstate);
+
+ if (state->state == 0) {
+ TRACE(("Encoding line bt line"));
+ while(state->y < state->ysize){
+ state->shuffle(state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize,
+ state->xsize);
+
+ if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y, 0) == -1) {
+ TRACE(("Encode Error, row %d\n", state->y));
+ state->errcode = IMAGING_CODEC_BROKEN;
+ TIFFClose(tiff);
+ if (!clientstate->fp){
+ free(clientstate->data);
+ }
+ return -1;
+ }
+ state->y++;
+ }
+
+ if (state->y == state->ysize) {
+ state->state=1;
+
+ TRACE(("Flushing \n"));
+ if (!TIFFFlush(tiff)) {
+ TRACE(("Error flushing the tiff"));
+ // likely reason is memory.
+ state->errcode = IMAGING_CODEC_MEMORY;
+ TIFFClose(tiff);
+ if (!clientstate->fp){
+ free(clientstate->data);
+ }
+ return -1;
+ }
+ TRACE(("Closing \n"));
+ TIFFClose(tiff);
+ // reset the clientstate metadata to use it to read out the buffer.
+ clientstate->loc = 0;
+ clientstate->size = clientstate->eof; // redundant?
+ }
+ }
+
+ if (state->state == 1 && !clientstate->fp) {
+ int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes);
+ TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
+ if (clientstate->loc == clientstate->eof) {
+ TRACE(("Hit EOF, calling an end, freeing data"));
+ state->errcode = IMAGING_CODEC_END;
+ free(clientstate->data);
+ }
+ return read;
+ }
+
+ state->errcode = IMAGING_CODEC_END;
+ return 0;
+}
+#endif
diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h
new file mode 100644
index 000000000..90fe3c9d4
--- /dev/null
+++ b/libImaging/TiffDecode.h
@@ -0,0 +1,63 @@
+/*
+ * The Python Imaging Library.
+ * $Id: //modules/pil/libImaging/Tiff.h#1 $
+ *
+ * declarations for the LibTiff-based Group3 and Group4 decoder
+ *
+ */
+
+#ifndef _TIFFIO_
+#include
+#endif
+#ifndef _TIFF_
+#include
+#endif
+
+#ifndef _UNISTD_H
+#include
+#endif
+
+
+#ifndef min
+#define min(x,y) (( x > y ) ? y : x )
+#define max(x,y) (( x < y ) ? y : x )
+#endif
+
+#ifndef _PIL_LIBTIFF_
+#define _PIL_LIBTIFF_
+
+typedef struct {
+ tdata_t data; /* tdata_t == void* */
+ toff_t loc; /* toff_t == uint32 */
+ tsize_t size; /* tsize_t == int32 */
+ int fp;
+ TIFF *tiff; /* Used in write */
+ toff_t eof;
+ int flrealloc; /* may we realloc */
+} TIFFSTATE;
+
+
+
+extern int ImagingLibTiffInit(ImagingCodecState state, int fp);
+extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
+extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);
+
+
+#if defined(_MSC_VER) && (_MSC_VER == 1310)
+/* VS2003/py2.4 can't use varargs. Skipping trace for now.*/
+#define TRACE(args)
+#else
+
+
+/*
+#define VA_ARGS(...) __VA_ARGS__
+#define TRACE(args) fprintf(stderr, VA_ARGS args)
+*/
+
+#define TRACE(args)
+
+#endif /* _MSC_VER */
+
+
+
+#endif
diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c
index 90a76feb3..70b11b1b0 100644
--- a/libImaging/Unpack.c
+++ b/libImaging/Unpack.c
@@ -33,17 +33,17 @@
#include "Imaging.h"
-#define R 0
-#define G 1
-#define B 2
-#define X 3
+#define R 0
+#define G 1
+#define B 2
+#define X 3
-#define A 3
+#define A 3
-#define C 0
-#define M 1
-#define Y 2
-#define K 3
+#define C 0
+#define M 1
+#define Y 2
+#define K 3
#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
@@ -110,18 +110,18 @@ unpack1(UINT8* out, const UINT8* in, int pixels)
{
/* bits (msb first, white is non-zero) */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
- case 1: *out++ = (byte & 128) ? 255 : 0;
- }
- pixels -= 8;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1;
+ case 1: *out++ = (byte & 128) ? 255 : 0;
+ }
+ pixels -= 8;
}
}
@@ -130,18 +130,18 @@ unpack1I(UINT8* out, const UINT8* in, int pixels)
{
/* bits (msb first, white is zero) */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 7: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 6: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 5: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 4: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 3: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 2: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
- case 1: *out++ = (byte & 128) ? 0 : 255;
- }
- pixels -= 8;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 7: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 6: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 5: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 4: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 3: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 2: *out++ = (byte & 128) ? 0 : 255; byte <<= 1;
+ case 1: *out++ = (byte & 128) ? 0 : 255;
+ }
+ pixels -= 8;
}
}
@@ -150,18 +150,18 @@ unpack1R(UINT8* out, const UINT8* in, int pixels)
{
/* bits (lsb first, white is non-zero) */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 7: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 6: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 5: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 4: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 3: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 2: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
- case 1: *out++ = (byte & 1) ? 255 : 0;
- }
- pixels -= 8;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 7: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 6: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 5: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 4: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 3: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 2: *out++ = (byte & 1) ? 255 : 0; byte >>= 1;
+ case 1: *out++ = (byte & 1) ? 255 : 0;
+ }
+ pixels -= 8;
}
}
@@ -170,18 +170,18 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels)
{
/* bits (lsb first, white is zero) */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 7: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 6: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 5: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 4: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 3: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 2: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
- case 1: *out++ = (byte & 1) ? 0 : 255;
- }
- pixels -= 8;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 7: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 6: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 5: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 4: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 3: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 2: *out++ = (byte & 1) ? 0 : 255; byte >>= 1;
+ case 1: *out++ = (byte & 1) ? 0 : 255;
+ }
+ pixels -= 8;
}
}
@@ -193,14 +193,14 @@ unpackL2(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
- case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
- case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
- case 1: *out++ = ((byte >> 6) & 3) * 255 / 3;
- }
- pixels -= 4;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
+ case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
+ case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2;
+ case 1: *out++ = ((byte >> 6) & 3) * 255 / 3;
+ }
+ pixels -= 4;
}
}
@@ -209,12 +209,12 @@ unpackL4(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4;
- case 1: *out++ = ((byte >> 4) & 15) * 255 / 15;
- }
- pixels -= 2;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4;
+ case 1: *out++ = ((byte >> 4) & 15) * 255 / 15;
+ }
+ pixels -= 2;
}
}
@@ -224,9 +224,9 @@ unpackLA(UINT8* out, const UINT8* in, int pixels)
int i;
/* LA, pixel interleaved */
for (i = 0; i < pixels; i++) {
- out[R] = out[G] = out[B] = in[0];
- out[A] = in[1];
- in += 2; out += 4;
+ out[R] = out[G] = out[B] = in[0];
+ out[A] = in[1];
+ in += 2; out += 4;
}
}
@@ -236,9 +236,9 @@ unpackLAL(UINT8* out, const UINT8* in, int pixels)
int i;
/* LA, line interleaved */
for (i = 0; i < pixels; i++) {
- out[R] = out[G] = out[B] = in[i];
- out[A] = in[i+pixels];
- out += 4;
+ out[R] = out[G] = out[B] = in[i];
+ out[A] = in[i+pixels];
+ out += 4;
}
}
@@ -248,7 +248,7 @@ unpackLI(UINT8* out, const UINT8* in, int pixels)
/* negative */
int i;
for (i = 0; i < pixels; i++)
- out[i] = ~in[i];
+ out[i] = ~in[i];
}
static void
@@ -257,7 +257,7 @@ unpackLR(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, bit reversed */
for (i = 0; i < pixels; i++) {
- out[i] = BITFLIP[in[i]];
+ out[i] = BITFLIP[in[i]];
}
}
@@ -267,8 +267,8 @@ unpackL16(UINT8* out, const UINT8* in, int pixels)
/* int16 (upper byte, little endian) */
int i;
for (i = 0; i < pixels; i++) {
- out[i] = in[1];
- in += 2;
+ out[i] = in[1];
+ in += 2;
}
}
@@ -278,8 +278,8 @@ unpackL16B(UINT8* out, const UINT8* in, int pixels)
int i;
/* int16 (upper byte, big endian) */
for (i = 0; i < pixels; i++) {
- out[i] = in[0];
- in += 2;
+ out[i] = in[0];
+ in += 2;
}
}
@@ -291,18 +291,18 @@ unpackP1(UINT8* out, const UINT8* in, int pixels)
{
/* bits */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 7: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 6: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 5: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 4: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 3: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 2: *out++ = (byte >> 7) & 1; byte <<= 1;
- case 1: *out++ = (byte >> 7) & 1;
- }
- pixels -= 8;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 7: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 6: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 5: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 4: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 3: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 2: *out++ = (byte >> 7) & 1; byte <<= 1;
+ case 1: *out++ = (byte >> 7) & 1;
+ }
+ pixels -= 8;
}
}
@@ -311,14 +311,14 @@ unpackP2(UINT8* out, const UINT8* in, int pixels)
{
/* bit pairs */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte >> 6) & 3; byte <<= 2;
- case 3: *out++ = (byte >> 6) & 3; byte <<= 2;
- case 2: *out++ = (byte >> 6) & 3; byte <<= 2;
- case 1: *out++ = (byte >> 6) & 3;
- }
- pixels -= 4;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte >> 6) & 3; byte <<= 2;
+ case 3: *out++ = (byte >> 6) & 3; byte <<= 2;
+ case 2: *out++ = (byte >> 6) & 3; byte <<= 2;
+ case 1: *out++ = (byte >> 6) & 3;
+ }
+ pixels -= 4;
}
}
@@ -327,12 +327,12 @@ unpackP4(UINT8* out, const UINT8* in, int pixels)
{
/* nibbles */
while (pixels > 0) {
- UINT8 byte = *in++;
- switch (pixels) {
- default: *out++ = (byte >> 4) & 15; byte <<= 4;
- case 1: *out++ = (byte >> 4) & 15;
- }
- pixels -= 2;
+ UINT8 byte = *in++;
+ switch (pixels) {
+ default: *out++ = (byte >> 4) & 15; byte <<= 4;
+ case 1: *out++ = (byte >> 4) & 15;
+ }
+ pixels -= 2;
}
}
@@ -344,11 +344,11 @@ unpackP2L(UINT8* out, const UINT8* in, int pixels)
m = 128;
s = (pixels+7)/8;
for (i = j = 0; i < pixels; i++) {
- out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0);
- if ((m >>= 1) == 0) {
- m = 128;
- j++;
- }
+ out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0);
+ if ((m >>= 1) == 0) {
+ m = 128;
+ j++;
+ }
}
}
@@ -360,12 +360,12 @@ unpackP4L(UINT8* out, const UINT8* in, int pixels)
m = 128;
s = (pixels+7)/8;
for (i = j = 0; i < pixels; i++) {
- out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) +
- ((in[j + 2*s] & m) ? 4 : 0) + ((in[j + 3*s] & m) ? 8 : 0);
- if ((m >>= 1) == 0) {
- m = 128;
- j++;
- }
+ out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) +
+ ((in[j + 2*s] & m) ? 4 : 0) + ((in[j + 3*s] & m) ? 8 : 0);
+ if ((m >>= 1) == 0) {
+ m = 128;
+ j++;
+ }
}
}
@@ -377,11 +377,11 @@ ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB triplets */
for (i = 0; i < pixels; i++) {
- out[R] = in[0];
- out[G] = in[1];
- out[B] = in[2];
- out[A] = 255;
- out += 4; in += 3;
+ out[R] = in[0];
+ out[G] = in[1];
+ out[B] = in[2];
+ out[A] = 255;
+ out += 4; in += 3;
}
}
@@ -391,11 +391,11 @@ unpackRGB16B(UINT8* out, const UINT8* in, int pixels)
int i;
/* 16-bit RGB triplets, big-endian order */
for (i = 0; i < pixels; i++) {
- out[R] = in[0];
- out[G] = in[2];
- out[B] = in[4];
- out[A] = 255;
- out += 4; in += 6;
+ out[R] = in[0];
+ out[G] = in[2];
+ out[B] = in[4];
+ out[A] = 255;
+ out += 4; in += 6;
}
}
@@ -405,11 +405,11 @@ unpackRGBL(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, line interleaved */
for (i = 0; i < pixels; i++) {
- out[R] = in[i];
- out[G] = in[i+pixels];
- out[B] = in[i+pixels+pixels];
- out[A] = 255;
- out += 4;
+ out[R] = in[i];
+ out[G] = in[i+pixels];
+ out[B] = in[i+pixels+pixels];
+ out[A] = 255;
+ out += 4;
}
}
@@ -419,11 +419,11 @@ unpackRGBR(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, bit reversed */
for (i = 0; i < pixels; i++) {
- out[R] = BITFLIP[in[0]];
- out[G] = BITFLIP[in[1]];
- out[B] = BITFLIP[in[2]];
- out[A] = 255;
- out += 4; in += 3;
+ out[R] = BITFLIP[in[0]];
+ out[G] = BITFLIP[in[1]];
+ out[B] = BITFLIP[in[2]];
+ out[A] = 255;
+ out += 4; in += 3;
}
}
@@ -433,11 +433,41 @@ ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, reversed bytes */
for (i = 0; i < pixels; i++) {
- out[R] = in[2];
- out[G] = in[1];
- out[B] = in[0];
- out[A] = 255;
- out += 4; in += 3;
+ out[R] = in[2];
+ out[G] = in[1];
+ out[B] = in[0];
+ out[A] = 255;
+ out += 4; in += 3;
+ }
+}
+
+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;
}
}
@@ -447,12 +477,42 @@ ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels)
int i, pixel;
/* RGB, reversed bytes, 5 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] = 255;
- out += 4; in += 2;
+ 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] = 255;
+ out += 4; in += 2;
+ }
+}
+
+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;
}
}
@@ -462,12 +522,42 @@ ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels)
int i, pixel;
/* RGB, reversed bytes, 5/6/5 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) & 63) * 255 / 63;
- out[R] = ((pixel>>11) & 31) * 255 / 31;
- out[A] = 255;
- out += 4; in += 2;
+ pixel = in[0] + (in[1] << 8);
+ out[B] = (pixel & 31) * 255 / 31;
+ out[G] = ((pixel>>5) & 63) * 255 / 63;
+ out[R] = ((pixel>>11) & 31) * 255 / 31;
+ out[A] = 255;
+ out += 4; in += 2;
+ }
+}
+
+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;
}
}
@@ -477,11 +567,11 @@ ImagingUnpackBGRX(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, reversed bytes with padding */
for (i = 0; i < pixels; i++) {
- out[R] = in[2];
- out[G] = in[1];
- out[B] = in[0];
- out[A] = 255;
- out += 4; in += 4;
+ out[R] = in[2];
+ out[G] = in[1];
+ out[B] = in[0];
+ out[A] = 255;
+ out += 4; in += 4;
}
}
@@ -491,11 +581,11 @@ ImagingUnpackXRGB(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, leading pad */
for (i = 0; i < pixels; i++) {
- out[R] = in[1];
- out[G] = in[2];
- out[B] = in[3];
- out[A] = 255;
- out += 4; in += 4;
+ out[R] = in[1];
+ out[G] = in[2];
+ out[B] = in[3];
+ out[A] = 255;
+ out += 4; in += 4;
}
}
@@ -505,11 +595,11 @@ ImagingUnpackXBGR(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGB, reversed bytes, leading pad */
for (i = 0; i < pixels; i++) {
- out[R] = in[3];
- out[G] = in[2];
- out[B] = in[1];
- out[A] = 255;
- out += 4; in += 4;
+ out[R] = in[3];
+ out[G] = in[2];
+ out[B] = in[1];
+ out[A] = 255;
+ out += 4; in += 4;
}
}
@@ -521,9 +611,9 @@ unpackRGBALA(UINT8* out, const UINT8* in, int pixels)
int i;
/* greyscale with alpha */
for (i = 0; i < pixels; i++) {
- out[R] = out[G] = out[B] = in[0];
- out[A] = in[1];
- out += 4; in += 2;
+ out[R] = out[G] = out[B] = in[0];
+ out[A] = in[1];
+ out += 4; in += 2;
}
}
@@ -533,9 +623,9 @@ unpackRGBALA16B(UINT8* out, const UINT8* in, int pixels)
int i;
/* 16-bit greyscale with alpha, big-endian */
for (i = 0; i < pixels; i++) {
- out[R] = out[G] = out[B] = in[0];
- out[A] = in[2];
- out += 4; in += 4;
+ out[R] = out[G] = out[B] = in[0];
+ out[A] = in[2];
+ out += 4; in += 4;
}
}
@@ -545,7 +635,7 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels)
int i;
/* premultiplied RGBA */
for (i = 0; i < pixels; i++) {
- int a = in[3];
+ int a = in[3];
if (!a)
out[R] = out[G] = out[B] = out[A] = 0;
else {
@@ -554,7 +644,7 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels)
out[B] = CLIP(in[2] * 255 / a);
out[A] = a;
}
- out += 4; in += 4;
+ out += 4; in += 4;
}
}
@@ -564,11 +654,11 @@ unpackRGBAI(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGBA, inverted RGB bytes (FlashPix) */
for (i = 0; i < pixels; i++) {
- out[R] = ~in[0];
- out[G] = ~in[1];
- out[B] = ~in[2];
- out[A] = in[3];
- out += 4; in += 4;
+ out[R] = ~in[0];
+ out[G] = ~in[1];
+ out[B] = ~in[2];
+ out[A] = in[3];
+ out += 4; in += 4;
}
}
@@ -579,11 +669,11 @@ unpackRGBAL(UINT8* out, const UINT8* in, int pixels)
/* RGBA, line interleaved */
for (i = 0; i < pixels; i++) {
- out[R] = in[i];
- out[G] = in[i+pixels];
- out[B] = in[i+pixels+pixels];
- out[A] = in[i+pixels+pixels+pixels];
- out += 4;
+ out[R] = in[i];
+ out[G] = in[i+pixels];
+ out[B] = in[i+pixels+pixels];
+ out[A] = in[i+pixels+pixels+pixels];
+ out += 4;
}
}
@@ -593,11 +683,11 @@ unpackRGBA16B(UINT8* out, const UINT8* in, int pixels)
int i;
/* 16-bit RGBA, big-endian order */
for (i = 0; i < pixels; i++) {
- out[R] = in[0];
- out[G] = in[2];
- out[B] = in[4];
- out[A] = in[6];
- out += 4; in += 8;
+ out[R] = in[0];
+ out[G] = in[2];
+ out[B] = in[4];
+ out[A] = in[6];
+ out += 4; in += 8;
}
}
@@ -607,11 +697,11 @@ unpackARGB(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGBA, leading pad */
for (i = 0; i < pixels; i++) {
- out[R] = in[1];
- out[G] = in[2];
- out[B] = in[3];
- out[A] = in[0];
- out += 4; in += 4;
+ out[R] = in[1];
+ out[G] = in[2];
+ out[B] = in[3];
+ out[A] = in[0];
+ out += 4; in += 4;
}
}
@@ -621,11 +711,11 @@ unpackABGR(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGBA, reversed bytes */
for (i = 0; i < pixels; i++) {
- out[R] = in[3];
- out[G] = in[2];
- out[B] = in[1];
- out[A] = in[0];
- out += 4; in += 4;
+ out[R] = in[3];
+ out[G] = in[2];
+ out[B] = in[1];
+ out[A] = in[0];
+ out += 4; in += 4;
}
}
@@ -635,11 +725,11 @@ unpackBGRA(UINT8* out, const UINT8* in, int pixels)
int i;
/* RGBA, reversed bytes */
for (i = 0; i < pixels; i++) {
- out[R] = in[2];
- out[G] = in[1];
- out[B] = in[0];
- out[A] = in[3];
- out += 4; in += 4;
+ out[R] = in[2];
+ out[G] = in[1];
+ out[B] = in[0];
+ out[A] = in[3];
+ out += 4; in += 4;
}
}
@@ -652,14 +742,113 @@ unpackCMYKI(UINT8* out, const UINT8* in, int pixels)
int i;
/* CMYK, inverted bytes (Photoshop 2.5) */
for (i = 0; i < pixels; i++) {
- out[C] = ~in[0];
- out[M] = ~in[1];
- out[Y] = ~in[2];
- out[K] = ~in[3];
- out += 4; in += 4;
+ out[C] = ~in[0];
+ out[M] = ~in[1];
+ out[Y] = ~in[2];
+ out[K] = ~in[3];
+ out += 4; in += 4;
}
}
+/* 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
+unpackI12_I16(UINT8* out, const UINT8* in, int pixels){
+ /* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs.
+
+ According to the TIFF spec:
+
+ FillOrder = 2 should be used only when BitsPerSample = 1 and
+ the data is either uncompressed or compressed using CCITT 1D
+ or 2D compression, to avoid potentially ambigous situations.
+
+ Yeah. I thought so. We'll see how well people read the spec.
+ We've got several fillorder=2 modes in TiffImagePlugin.py
+
+ There's no spec I can find. It appears that the in storage
+ layout is: 00 80 00 ... -> (128 , 0 ...). The samples are
+ stored in a single big bitian 12bit block, but need to be
+ pulled out to little endian format to be stored in a 2 byte
+ int.
+ */
+
+ int i;
+ UINT16 pixel;
+#ifdef WORDS_BIGENDIAN
+ UINT8* tmp = (UINT8 *)&pixel;
+#endif
+ UINT16* out16 = (UINT16 *)out;
+ for (i = 0; i < pixels-1; i+=2) {
+ pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4);
+#ifdef WORDS_BIGENDIAN
+ out[0] = tmp[1]; out[1] = tmp[0];
+#else
+ out16[0] = pixel;
+#endif
+
+ pixel = (((UINT16) (in[1] & 0x0F)) << 8) + in[2];
+#ifdef WORDS_BIGENDIAN
+ out[2] = tmp[1]; out[3] = tmp[0];
+#else
+ out16[1] = pixel;
+#endif
+
+ in += 3; out16 += 2; out+=4;
+ }
+ if (i == pixels-1) {
+ pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4);
+#ifdef WORDS_BIGENDIAN
+ out[0] = tmp[1]; out[1] = tmp[0];
+#else
+ out16[0] = pixel;
+#endif
+ }
+}
+
+
static void
copy1(UINT8* out, const UINT8* in, int pixels)
{
@@ -674,6 +863,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)
{
@@ -753,8 +949,8 @@ band0(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 0 only */
for (i = 0; i < pixels; i++) {
- out[0] = in[i];
- out += 4;
+ out[0] = in[i];
+ out += 4;
}
}
@@ -764,8 +960,8 @@ band1(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 1 only */
for (i = 0; i < pixels; i++) {
- out[1] = in[i];
- out += 4;
+ out[1] = in[i];
+ out += 4;
}
}
@@ -775,8 +971,8 @@ band2(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 2 only */
for (i = 0; i < pixels; i++) {
- out[2] = in[i];
- out += 4;
+ out[2] = in[i];
+ out += 4;
}
}
@@ -786,8 +982,8 @@ band3(UINT8* out, const UINT8* in, int pixels)
/* band 3 only */
int i;
for (i = 0; i < pixels; i++) {
- out[3] = in[i];
- out += 4;
+ out[3] = in[i];
+ out += 4;
}
}
@@ -797,8 +993,8 @@ band0I(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 0 only */
for (i = 0; i < pixels; i++) {
- out[0] = ~in[i];
- out += 4;
+ out[0] = ~in[i];
+ out += 4;
}
}
@@ -808,8 +1004,8 @@ band1I(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 1 only */
for (i = 0; i < pixels; i++) {
- out[1] = ~in[i];
- out += 4;
+ out[1] = ~in[i];
+ out += 4;
}
}
@@ -819,8 +1015,8 @@ band2I(UINT8* out, const UINT8* in, int pixels)
int i;
/* band 2 only */
for (i = 0; i < pixels; i++) {
- out[2] = ~in[i];
- out += 4;
+ out[2] = ~in[i];
+ out += 4;
}
}
@@ -830,8 +1026,8 @@ band3I(UINT8* out, const UINT8* in, int pixels)
/* band 3 only */
int i;
for (i = 0; i < pixels; i++) {
- out[3] = ~in[i];
- out += 4;
+ out[3] = ~in[i];
+ out += 4;
}
}
@@ -852,157 +1048,176 @@ static struct {
interleave, "S" signed, "F" floating point */
/* bilevel */
- {"1", "1", 1, unpack1},
- {"1", "1;I", 1, unpack1I},
- {"1", "1;R", 1, unpack1R},
- {"1", "1;IR", 1, unpack1IR},
+ {"1", "1", 1, unpack1},
+ {"1", "1;I", 1, unpack1I},
+ {"1", "1;R", 1, unpack1R},
+ {"1", "1;IR", 1, unpack1IR},
/* greyscale */
- {"L", "L;2", 2, unpackL2},
- {"L", "L;4", 4, unpackL4},
- {"L", "L", 8, copy1},
- {"L", "L;I", 8, unpackLI},
- {"L", "L;R", 8, unpackLR},
- {"L", "L;16", 16, unpackL16},
- {"L", "L;16B", 16, unpackL16B},
+ {"L", "L;2", 2, unpackL2},
+ {"L", "L;4", 4, unpackL4},
+ {"L", "L", 8, copy1},
+ {"L", "L;I", 8, unpackLI},
+ {"L", "L;R", 8, unpackLR},
+ {"L", "L;16", 16, unpackL16},
+ {"L", "L;16B", 16, unpackL16B},
/* greyscale w. alpha */
- {"LA", "LA", 16, unpackLA},
- {"LA", "LA;L", 16, unpackLAL},
+ {"LA", "LA", 16, unpackLA},
+ {"LA", "LA;L", 16, unpackLAL},
/* palette */
- {"P", "P;1", 1, unpackP1},
- {"P", "P;2", 2, unpackP2},
- {"P", "P;2L", 2, unpackP2L},
- {"P", "P;4", 4, unpackP4},
- {"P", "P;4L", 4, unpackP4L},
- {"P", "P", 8, copy1},
- {"P", "P;R", 8, unpackLR},
+ {"P", "P;1", 1, unpackP1},
+ {"P", "P;2", 2, unpackP2},
+ {"P", "P;2L", 2, unpackP2L},
+ {"P", "P;4", 4, unpackP4},
+ {"P", "P;4L", 4, unpackP4L},
+ {"P", "P", 8, copy1},
+ {"P", "P;R", 8, unpackLR},
/* palette w. alpha */
- {"PA", "PA", 16, unpackLA},
- {"PA", "PA;L", 16, unpackLAL},
+ {"PA", "PA", 16, unpackLA},
+ {"PA", "PA;L", 16, unpackLAL},
/* true colour */
- {"RGB", "RGB", 24, ImagingUnpackRGB},
- {"RGB", "RGB;L", 24, unpackRGBL},
- {"RGB", "RGB;R", 24, unpackRGBR},
- {"RGB", "RGB;16B", 48, unpackRGB16B},
- {"RGB", "BGR", 24, ImagingUnpackBGR},
- {"RGB", "BGR;15", 16, ImagingUnpackBGR15},
- {"RGB", "BGR;16", 16, ImagingUnpackBGR16},
- {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
- {"RGB", "RGBX", 32, copy4},
- {"RGB", "RGBX;L", 32, unpackRGBAL},
- {"RGB", "BGRX", 32, ImagingUnpackBGRX},
- {"RGB", "XRGB", 24, ImagingUnpackXRGB},
- {"RGB", "XBGR", 32, ImagingUnpackXBGR},
- {"RGB", "YCC;P", 24, ImagingUnpackYCC},
- {"RGB", "R", 8, band0},
- {"RGB", "G", 8, band1},
- {"RGB", "B", 8, band2},
+ {"RGB", "RGB", 24, ImagingUnpackRGB},
+ {"RGB", "RGB;L", 24, unpackRGBL},
+ {"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},
+ {"RGB", "BGRX", 32, ImagingUnpackBGRX},
+ {"RGB", "XRGB", 24, ImagingUnpackXRGB},
+ {"RGB", "XBGR", 32, ImagingUnpackXBGR},
+ {"RGB", "YCC;P", 24, ImagingUnpackYCC},
+ {"RGB", "R", 8, band0},
+ {"RGB", "G", 8, band1},
+ {"RGB", "B", 8, band2},
/* true colour w. alpha */
- {"RGBA", "LA", 16, unpackRGBALA},
- {"RGBA", "LA;16B", 32, unpackRGBALA16B},
- {"RGBA", "RGBA", 32, copy4},
- {"RGBA", "RGBa", 32, unpackRGBa},
- {"RGBA", "RGBA;I", 32, unpackRGBAI},
- {"RGBA", "RGBA;L", 32, unpackRGBAL},
- {"RGBA", "RGBA;16B", 64, unpackRGBA16B},
- {"RGBA", "BGRA", 32, unpackBGRA},
- {"RGBA", "ARGB", 32, unpackARGB},
- {"RGBA", "ABGR", 32, unpackABGR},
- {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
- {"RGBA", "R", 8, band0},
- {"RGBA", "G", 8, band1},
- {"RGBA", "B", 8, band2},
- {"RGBA", "A", 8, band3},
+ {"RGBA", "LA", 16, unpackRGBALA},
+ {"RGBA", "LA;16B", 32, unpackRGBALA16B},
+ {"RGBA", "RGBA", 32, copy4},
+ {"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},
+ {"RGBA", "ABGR", 32, unpackABGR},
+ {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
+ {"RGBA", "R", 8, band0},
+ {"RGBA", "G", 8, band1},
+ {"RGBA", "B", 8, band2},
+ {"RGBA", "A", 8, band3},
/* true colour w. padding */
- {"RGBX", "RGB", 24, ImagingUnpackRGB},
- {"RGBX", "RGB;L", 24, unpackRGBL},
- {"RGBX", "RGB;16B", 48, unpackRGB16B},
- {"RGBX", "BGR", 24, ImagingUnpackBGR},
- {"RGBX", "BGR;15", 16, ImagingUnpackBGR15},
- {"RGB", "BGR;16", 16, ImagingUnpackBGR16},
- {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
- {"RGBX", "RGBX", 32, copy4},
- {"RGBX", "RGBX;L", 32, unpackRGBAL},
- {"RGBX", "BGRX", 32, ImagingUnpackBGRX},
- {"RGBX", "XRGB", 24, ImagingUnpackXRGB},
- {"RGBX", "XBGR", 32, ImagingUnpackXBGR},
- {"RGBX", "YCC;P", 24, ImagingUnpackYCC},
- {"RGBX", "R", 8, band0},
- {"RGBX", "G", 8, band1},
- {"RGBX", "B", 8, band2},
- {"RGBX", "X", 8, band3},
+ {"RGBX", "RGB", 24, ImagingUnpackRGB},
+ {"RGBX", "RGB;L", 24, unpackRGBL},
+ {"RGBX", "RGB;16B", 48, unpackRGB16B},
+ {"RGBX", "BGR", 24, ImagingUnpackBGR},
+ {"RGBX", "RGB;15", 16, ImagingUnpackRGB15},
+ {"RGBX", "BGR;15", 16, ImagingUnpackBGR15},
+ {"RGBX", "RGB;4B", 16, ImagingUnpackRGB4B},
+ {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */
+ {"RGBX", "RGBX", 32, copy4},
+ {"RGBX", "RGBX;L", 32, unpackRGBAL},
+ {"RGBX", "BGRX", 32, ImagingUnpackBGRX},
+ {"RGBX", "XRGB", 24, ImagingUnpackXRGB},
+ {"RGBX", "XBGR", 32, ImagingUnpackXBGR},
+ {"RGBX", "YCC;P", 24, ImagingUnpackYCC},
+ {"RGBX", "R", 8, band0},
+ {"RGBX", "G", 8, band1},
+ {"RGBX", "B", 8, band2},
+ {"RGBX", "X", 8, band3},
/* colour separation */
- {"CMYK", "CMYK", 32, copy4},
- {"CMYK", "CMYK;I", 32, unpackCMYKI},
- {"CMYK", "CMYK;L", 32, unpackRGBAL},
- {"CMYK", "C", 8, band0},
- {"CMYK", "M", 8, band1},
- {"CMYK", "Y", 8, band2},
- {"CMYK", "K", 8, band3},
- {"CMYK", "C;I", 8, band0I},
- {"CMYK", "M;I", 8, band1I},
- {"CMYK", "Y;I", 8, band2I},
- {"CMYK", "K;I", 8, band3I},
+ {"CMYK", "CMYK", 32, copy4},
+ {"CMYK", "CMYK;I", 32, unpackCMYKI},
+ {"CMYK", "CMYK;L", 32, unpackRGBAL},
+ {"CMYK", "C", 8, band0},
+ {"CMYK", "M", 8, band1},
+ {"CMYK", "Y", 8, band2},
+ {"CMYK", "K", 8, band3},
+ {"CMYK", "C;I", 8, band0I},
+ {"CMYK", "M;I", 8, band1I},
+ {"CMYK", "Y;I", 8, band2I},
+ {"CMYK", "K;I", 8, band3I},
/* video (YCbCr) */
- {"YCbCr", "YCbCr", 24, ImagingUnpackRGB},
- {"YCbCr", "YCbCr;L", 24, unpackRGBL},
- {"YCbCr", "YCbCrX", 32, copy4},
- {"YCbCr", "YCbCrK", 32, copy4},
+ {"YCbCr", "YCbCr", 24, ImagingUnpackRGB},
+ {"YCbCr", "YCbCr;L", 24, unpackRGBL},
+ {"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},
- {"I", "I;8S", 8, unpackI8S},
- {"I", "I;16", 16, unpackI16},
- {"I", "I;16S", 16, unpackI16S},
- {"I", "I;16B", 16, unpackI16B},
- {"I", "I;16BS", 16, unpackI16BS},
- {"I", "I;16N", 16, unpackI16N},
- {"I", "I;16NS", 16, unpackI16NS},
- {"I", "I;32", 32, unpackI32},
- {"I", "I;32S", 32, unpackI32S},
- {"I", "I;32B", 32, unpackI32B},
- {"I", "I;32BS", 32, unpackI32BS},
- {"I", "I;32N", 32, unpackI32N},
- {"I", "I;32NS", 32, unpackI32NS},
+ {"I", "I", 32, copy4},
+ {"I", "I;8", 8, unpackI8},
+ {"I", "I;8S", 8, unpackI8S},
+ {"I", "I;16", 16, unpackI16},
+ {"I", "I;16S", 16, unpackI16S},
+ {"I", "I;16B", 16, unpackI16B},
+ {"I", "I;16BS", 16, unpackI16BS},
+ {"I", "I;16N", 16, unpackI16N},
+ {"I", "I;16NS", 16, unpackI16NS},
+ {"I", "I;32", 32, unpackI32},
+ {"I", "I;32S", 32, unpackI32S},
+ {"I", "I;32B", 32, unpackI32B},
+ {"I", "I;32BS", 32, unpackI32BS},
+ {"I", "I;32N", 32, unpackI32N},
+ {"I", "I;32NS", 32, unpackI32NS},
/* floating point variations */
- {"F", "F", 32, copy4},
- {"F", "F;8", 8, unpackF8},
- {"F", "F;8S", 8, unpackF8S},
- {"F", "F;16", 16, unpackF16},
- {"F", "F;16S", 16, unpackF16S},
- {"F", "F;16B", 16, unpackF16B},
- {"F", "F;16BS", 16, unpackF16BS},
- {"F", "F;16N", 16, unpackF16N},
- {"F", "F;16NS", 16, unpackF16NS},
- {"F", "F;32", 32, unpackF32},
- {"F", "F;32S", 32, unpackF32S},
- {"F", "F;32B", 32, unpackF32B},
- {"F", "F;32BS", 32, unpackF32BS},
- {"F", "F;32N", 32, unpackF32N},
- {"F", "F;32NS", 32, unpackF32NS},
- {"F", "F;32F", 32, unpackF32F},
- {"F", "F;32BF", 32, unpackF32BF},
- {"F", "F;32NF", 32, unpackF32NF},
+ {"F", "F", 32, copy4},
+ {"F", "F;8", 8, unpackF8},
+ {"F", "F;8S", 8, unpackF8S},
+ {"F", "F;16", 16, unpackF16},
+ {"F", "F;16S", 16, unpackF16S},
+ {"F", "F;16B", 16, unpackF16B},
+ {"F", "F;16BS", 16, unpackF16BS},
+ {"F", "F;16N", 16, unpackF16N},
+ {"F", "F;16NS", 16, unpackF16NS},
+ {"F", "F;32", 32, unpackF32},
+ {"F", "F;32S", 32, unpackF32S},
+ {"F", "F;32B", 32, unpackF32B},
+ {"F", "F;32BS", 32, unpackF32BS},
+ {"F", "F;32N", 32, unpackF32N},
+ {"F", "F;32NS", 32, unpackF32NS},
+ {"F", "F;32F", 32, unpackF32F},
+ {"F", "F;32BF", 32, unpackF32BF},
+ {"F", "F;32NF", 32, unpackF32NF},
#ifdef FLOAT64
- {"F", "F;64F", 64, unpackF64F},
- {"F", "F;64BF", 64, unpackF64BF},
- {"F", "F;64NF", 64, unpackF64NF},
+ {"F", "F;64F", 64, unpackF64F},
+ {"F", "F;64BF", 64, unpackF64BF},
+ {"F", "F;64NF", 64, unpackF64NF},
#endif
/* storage modes */
- {"I;16", "I;16", 16, copy2},
- {"I;16B", "I;16B", 16, copy2},
- {"I;16L", "I;16L", 16, copy2},
+ {"I;16", "I;16", 16, copy2},
+ {"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},
+
+ {"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits.
{NULL} /* sentinel */
};
@@ -1015,12 +1230,12 @@ ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out)
/* find a suitable pixel unpacker */
for (i = 0; unpackers[i].rawmode; i++)
- if (strcmp(unpackers[i].mode, mode) == 0 &&
+ if (strcmp(unpackers[i].mode, mode) == 0 &&
strcmp(unpackers[i].rawmode, rawmode) == 0) {
- if (bits_out)
- *bits_out = unpackers[i].bits;
- return unpackers[i].unpack;
- }
+ if (bits_out)
+ *bits_out = unpackers[i].bits;
+ return unpackers[i].unpack;
+ }
/* FIXME: configure a general unpacker based on the type codes... */
diff --git a/libImaging/Zip.h b/libImaging/Zip.h
index d961407e3..21a336f90 100644
--- a/libImaging/Zip.h
+++ b/libImaging/Zip.h
@@ -28,6 +28,11 @@ typedef struct {
/* Optimize (max compression) SLOW!!! */
int optimize;
+ /* 0 no compression, 9 best compression, -1 default compression */
+ int compress_level;
+ /* compression strategy Z_XXX */
+ int compress_type;
+
/* Predefined dictionary (experimental) */
char* dictionary;
int dictionary_size;
@@ -49,9 +54,9 @@ typedef struct {
UINT8* output; /* output data */
int prefix; /* size of filter prefix (0 for TIFF data) */
-
+
int interlaced; /* is the image interlaced? (PNG) */
-
+
int pass; /* current pass of the interlaced image (PNG) */
} ZIPSTATE;
diff --git a/libImaging/ZipDecode.c b/libImaging/ZipDecode.c
index 08cdc7189..707033a66 100644
--- a/libImaging/ZipDecode.c
+++ b/libImaging/ZipDecode.c
@@ -32,7 +32,7 @@ static const int ROW_INCREMENT[] = { 8, 8, 8, 4, 4, 2, 2 };
* for interlaced images */
static int get_row_len(ImagingCodecState state, int pass)
{
- int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass];
+ int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass];
return ((row_len * state->bits) + 7) / 8;
}
@@ -202,7 +202,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
if (state->bits >= 8) {
/* Stuff pixels in their correct location, one by one */
for (i = 0; i < row_len; i += ((state->bits + 7) / 8)) {
- state->shuffle((UINT8*) im->image[state->y] +
+ state->shuffle((UINT8*) im->image[state->y] +
col * im->pixelsize,
state->buffer + context->prefix + i, 1);
col += COL_INCREMENT[context->pass];
@@ -214,7 +214,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
for (i = 0; i < row_bits; i += state->bits) {
UINT8 byte = *(state->buffer + context->prefix + (i / 8));
byte <<= (i % 8);
- state->shuffle((UINT8*) im->image[state->y] +
+ state->shuffle((UINT8*) im->image[state->y] +
col * im->pixelsize, &byte, 1);
col += COL_INCREMENT[context->pass];
}
@@ -235,7 +235,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
memset(state->buffer, 0, state->bytes+1);
}
} else {
- state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->buffer + context->prefix,
state->xsize);
diff --git a/libImaging/ZipEncode.c b/libImaging/ZipEncode.c
index 19b2b7787..a4f76ffb4 100644
--- a/libImaging/ZipEncode.c
+++ b/libImaging/ZipEncode.c
@@ -26,6 +26,7 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
ZIPSTATE* context = (ZIPSTATE*) state->context;
int err;
+ int compress_level, compress_type;
UINT8* ptr;
int i, bpp, s, sum;
ImagingSectionCookie cookie;
@@ -73,17 +74,25 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
context->z_stream.next_in = 0;
context->z_stream.avail_in = 0;
+ compress_level = (context->optimize) ? Z_BEST_COMPRESSION
+ : context->compress_level;
+
+ if (context->compress_type == -1) {
+ compress_type = (context->mode == ZIP_PNG) ? Z_FILTERED
+ : Z_DEFAULT_STRATEGY;
+ } else {
+ compress_type = context->compress_type;
+ }
+
err = deflateInit2(&context->z_stream,
/* compression level */
- (context->optimize) ? Z_BEST_COMPRESSION
- : Z_DEFAULT_COMPRESSION,
+ compress_level,
/* compression method */
Z_DEFLATED,
/* compression memory resources */
15, 9,
/* compression strategy (image data are filtered)*/
- (context->mode == ZIP_PNG) ? Z_FILTERED
- : Z_DEFAULT_STRATEGY);
+ compress_type);
if (err < 0) {
state->errcode = IMAGING_CODEC_CONFIG;
return -1;
@@ -147,7 +156,7 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
/* Stuff image data into the compressor */
state->shuffle(state->buffer+1,
- (UINT8*) im->image[state->y + state->yoff] +
+ (UINT8*) im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->xsize);
diff --git a/map.c b/map.c
index f15b8200f..95d5d1d35 100644
--- a/map.c
+++ b/map.c
@@ -20,24 +20,25 @@
#include "Python.h"
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
#include "Imaging.h"
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
+#undef INT8
+#undef UINT8
+#undef INT16
+#undef UINT16
#undef INT32
#undef INT64
#undef UINT32
#include "windows.h"
#endif
+#include "py3.h"
+
/* compatibility wrappers (defined in _imaging.c) */
extern int PyImaging_CheckBuffer(PyObject* buffer);
-extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr);
+extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view);
/* -------------------------------------------------------------------- */
/* Standard mapper */
@@ -53,18 +54,19 @@ typedef struct {
#endif
} ImagingMapperObject;
-staticforward PyTypeObject ImagingMapperType;
+static PyTypeObject ImagingMapperType;
ImagingMapperObject*
PyImaging_MapperNew(const char* filename, int readonly)
{
ImagingMapperObject *mapper;
- ImagingMapperType.ob_type = &PyType_Type;
+ if (PyType_Ready(&ImagingMapperType) < 0)
+ return NULL;
mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType);
if (mapper == NULL)
- return NULL;
+ return NULL;
mapper->base = NULL;
mapper->size = mapper->offset = 0;
@@ -92,7 +94,7 @@ PyImaging_MapperNew(const char* filename, int readonly)
PAGE_READONLY,
0, 0, NULL);
if (mapper->hMap == (HANDLE)-1) {
- CloseHandle(mapper->hFile);
+ CloseHandle(mapper->hFile);
PyErr_SetString(PyExc_IOError, "cannot map file");
PyObject_Del(mapper);
return NULL;
@@ -114,11 +116,11 @@ mapping_dealloc(ImagingMapperObject* mapper)
{
#ifdef WIN32
if (mapper->base != 0)
- UnmapViewOfFile(mapper->base);
+ UnmapViewOfFile(mapper->base);
if (mapper->hMap != (HANDLE)-1)
- CloseHandle(mapper->hMap);
+ CloseHandle(mapper->hMap);
if (mapper->hFile != (HANDLE)-1)
- CloseHandle(mapper->hFile);
+ CloseHandle(mapper->hFile);
mapper->base = 0;
mapper->hMap = mapper->hFile = (HANDLE)-1;
#endif
@@ -128,14 +130,14 @@ mapping_dealloc(ImagingMapperObject* mapper)
/* -------------------------------------------------------------------- */
/* standard file operations */
-static PyObject*
+static PyObject*
mapping_read(ImagingMapperObject* mapper, PyObject* args)
{
PyObject* buf;
int size = -1;
if (!PyArg_ParseTuple(args, "|i", &size))
- return NULL;
+ return NULL;
/* check size */
if (size < 0 || mapper->offset + size > mapper->size)
@@ -143,25 +145,25 @@ mapping_read(ImagingMapperObject* mapper, PyObject* args)
if (size < 0)
size = 0;
- buf = PyString_FromStringAndSize(NULL, size);
+ buf = PyBytes_FromStringAndSize(NULL, size);
if (!buf)
- return NULL;
+ return NULL;
if (size > 0) {
- memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size);
+ memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size);
mapper->offset += size;
}
return buf;
}
-static PyObject*
+static PyObject*
mapping_seek(ImagingMapperObject* mapper, PyObject* args)
{
int offset;
int whence = 0;
if (!PyArg_ParseTuple(args, "i|i", &offset, &whence))
- return NULL;
+ return NULL;
switch (whence) {
case 0: /* SEEK_SET */
@@ -193,7 +195,7 @@ ImagingDestroyMap(Imaging im)
return; /* nothing to do! */
}
-static PyObject*
+static PyObject*
mapping_readimage(ImagingMapperObject* mapper, PyObject* args)
{
int y, size;
@@ -206,7 +208,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args)
int orientation;
if (!PyArg_ParseTuple(args, "s(ii)ii", &mode, &xsize, &ysize,
&stride, &orientation))
- return NULL;
+ return NULL;
if (stride <= 0) {
/* FIXME: maybe we should call ImagingNewPrologue instead */
@@ -242,7 +244,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args)
if (!ImagingNewEpilogue(im))
return NULL;
- mapper->offset += size;
+ mapper->offset += size;
return PyImagingNew(im);
}
@@ -256,34 +258,46 @@ static struct PyMethodDef methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-mapping_getattr(ImagingMapperObject* self, char* name)
-{
- return Py_FindMethod(methods, (PyObject*) self, name);
-}
-
-statichere PyTypeObject ImagingMapperType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
- "ImagingMapper", /*tp_name*/
- sizeof(ImagingMapperObject), /*tp_size*/
- 0, /*tp_itemsize*/
- /* methods */
- (destructor)mapping_dealloc, /*tp_dealloc*/
- 0, /*tp_print*/
- (getattrfunc)mapping_getattr, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_compare*/
- 0, /*tp_repr*/
- 0, /*tp_hash*/
+static PyTypeObject ImagingMapperType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "ImagingMapper", /*tp_name*/
+ sizeof(ImagingMapperObject), /*tp_size*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)mapping_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
-PyObject*
+PyObject*
PyImaging_Mapper(PyObject* self, PyObject* args)
{
char* filename;
if (!PyArg_ParseTuple(args, "s", &filename))
- return NULL;
+ return NULL;
return (PyObject*) PyImaging_MapperNew(filename, 1);
}
@@ -294,36 +308,37 @@ PyImaging_Mapper(PyObject* self, PyObject* args)
typedef struct ImagingBufferInstance {
struct ImagingMemoryInstance im;
PyObject* target;
+ Py_buffer view;
} ImagingBufferInstance;
static void
mapping_destroy_buffer(Imaging im)
{
ImagingBufferInstance* buffer = (ImagingBufferInstance*) im;
-
+
+ PyBuffer_Release(&buffer->view);
Py_XDECREF(buffer->target);
}
-PyObject*
+PyObject*
PyImaging_MapBuffer(PyObject* self, PyObject* args)
{
- int y, size;
+ Py_ssize_t y, size;
Imaging im;
- char* ptr;
- int bytes;
PyObject* target;
+ Py_buffer view;
char* mode;
char* codec;
PyObject* bbox;
- int offset;
+ Py_ssize_t offset;
int xsize, ysize;
int stride;
int ystep;
- if (!PyArg_ParseTuple(args, "O(ii)sOi(sii)", &target, &xsize, &ysize,
+ if (!PyArg_ParseTuple(args, "O(ii)sOn(sii)", &target, &xsize, &ysize,
&codec, &bbox, &offset, &mode, &stride, &ystep))
- return NULL;
+ return NULL;
if (!PyImaging_CheckBuffer(target)) {
PyErr_SetString(PyExc_TypeError, "expected string or buffer");
@@ -339,15 +354,17 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
stride = xsize * 4;
}
- size = ysize * stride;
+ size = (Py_ssize_t) ysize * stride;
/* check buffer size */
- bytes = PyImaging_ReadBuffer(target, (const void**) &ptr);
- if (bytes < 0) {
+ if (PyImaging_GetBuffer(target, &view) < 0)
+ return NULL;
+
+ if (view.len < 0) {
PyErr_SetString(PyExc_ValueError, "buffer has negative size");
return NULL;
}
- if (offset + size > bytes) {
+ if (offset + size > view.len) {
PyErr_SetString(PyExc_ValueError, "buffer is not large enough");
return NULL;
}
@@ -361,15 +378,16 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args)
/* setup file pointers */
if (ystep > 0)
for (y = 0; y < ysize; y++)
- im->image[y] = ptr + offset + y * stride;
+ im->image[y] = (char*)view.buf + offset + y * stride;
else
for (y = 0; y < ysize; y++)
- im->image[ysize-y-1] = ptr + offset + y * stride;
+ im->image[ysize-y-1] = (char*)view.buf + offset + y * stride;
im->destroy = mapping_destroy_buffer;
Py_INCREF(target);
((ImagingBufferInstance*) im)->target = target;
+ ((ImagingBufferInstance*) im)->view = view;
if (!ImagingNewEpilogue(im))
return NULL;
diff --git a/outline.c b/outline.c
index 6bb2ebb54..25e63aeaf 100644
--- a/outline.c
+++ b/outline.c
@@ -19,11 +19,6 @@
#include "Python.h"
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
#include "Imaging.h"
@@ -35,15 +30,18 @@ typedef struct {
ImagingOutline outline;
} OutlineObject;
-staticforward PyTypeObject OutlineType;
+static PyTypeObject OutlineType;
-#define PyOutline_Check(op) ((op)->ob_type == &OutlineType)
+#define PyOutline_Check(op) (Py_TYPE(op) == &OutlineType)
static OutlineObject*
_outline_new(void)
{
OutlineObject *self;
+ if (PyType_Ready(&OutlineType) < 0)
+ return NULL;
+
self = PyObject_New(OutlineObject, &OutlineType);
if (self == NULL)
return NULL;
@@ -159,21 +157,36 @@ static struct PyMethodDef _outline_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-_outline_getattr(OutlineObject* self, char* name)
-{
- return Py_FindMethod(_outline_methods, (PyObject*) self, name);
-}
-
-statichere PyTypeObject OutlineType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
+static PyTypeObject OutlineType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
"Outline", /*tp_name*/
sizeof(OutlineObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)_outline_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)_outline_getattr, /*tp_getattr*/
- 0 /*tp_setattr*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ 0, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ _outline_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
};
diff --git a/path.c b/path.c
index f083b971d..db4a68e24 100644
--- a/path.c
+++ b/path.c
@@ -30,23 +30,11 @@
#include
-#if PY_VERSION_HEX < 0x01060000
-#define PyObject_New PyObject_NEW
-#define PyObject_Del PyMem_DEL
-#endif
-
-#if PY_VERSION_HEX < 0x02050000
-#define Py_ssize_t int
-#define lenfunc inquiry
-#define ssizeargfunc intargfunc
-#define ssizessizeargfunc intintargfunc
-#define ssizeobjargproc intobjargproc
-#define ssizessizeobjargproc intintobjargproc
-#endif
+#include "py3.h"
/* compatibility wrappers (defined in _imaging.c) */
extern int PyImaging_CheckBuffer(PyObject* buffer);
-extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr);
+extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view);
/* -------------------------------------------------------------------- */
/* Class */
@@ -59,10 +47,10 @@ typedef struct {
int index; /* temporary use, e.g. in decimate */
} PyPathObject;
-staticforward PyTypeObject PyPathType;
+static PyTypeObject PyPathType;
static double*
-alloc_array(int count)
+alloc_array(Py_ssize_t count)
{
double* xy;
if (count < 0) {
@@ -89,6 +77,9 @@ path_new(Py_ssize_t count, double* xy, int duplicate)
xy = p;
}
+ if (PyType_Ready(&PyPathType) < 0)
+ return NULL;
+
path = PyObject_New(PyPathObject, &PyPathType);
if (path == NULL)
return NULL;
@@ -110,7 +101,7 @@ path_dealloc(PyPathObject* path)
/* Helpers */
/* -------------------------------------------------------------------- */
-#define PyPath_Check(op) ((op)->ob_type == &PyPathType)
+#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType)
int
PyPath_Flatten(PyObject* data, double **pxy)
@@ -128,19 +119,23 @@ PyPath_Flatten(PyObject* data, double **pxy)
*pxy = xy;
return path->count;
}
-
+
if (PyImaging_CheckBuffer(data)) {
/* Assume the buffer contains floats */
- float* ptr;
- int n = PyImaging_ReadBuffer(data, (const void**) &ptr);
- n /= 2 * sizeof(float);
- xy = alloc_array(n);
- if (!xy)
- return -1;
- for (i = 0; i < n+n; i++)
- xy[i] = ptr[i];
- *pxy = xy;
- return n;
+ Py_buffer buffer;
+ if (PyImaging_GetBuffer(data, &buffer) == 0) {
+ int n = buffer.len / (2 * sizeof(float));
+ float *ptr = (float*) buffer.buf;
+ xy = alloc_array(n);
+ if (!xy)
+ return -1;
+ for (i = 0; i < n+n; i++)
+ xy[i] = ptr[i];
+ *pxy = xy;
+ PyBuffer_Release(&buffer);
+ return n;
+ }
+ PyErr_Clear();
}
if (!PySequence_Check(data)) {
@@ -251,7 +246,7 @@ PyPath_Create(PyObject* self, PyObject* args)
Py_ssize_t count;
double *xy;
- if (PyArg_ParseTuple(args, "i:Path", &count)) {
+ if (PyArg_ParseTuple(args, "n:Path", &count)) {
/* number of vertices */
xy = alloc_array(count);
@@ -361,6 +356,8 @@ path_getbbox(PyPathObject* self, PyObject* args)
static PyObject*
path_getitem(PyPathObject* self, int i)
{
+ if (i < 0)
+ i = self->count + i;
if (i < 0 || i >= self->count) {
PyErr_SetString(PyExc_IndexError, "path index out of range");
return NULL;
@@ -383,7 +380,7 @@ path_getslice(PyPathObject* self, Py_ssize_t ilow, Py_ssize_t ihigh)
ihigh = ilow;
else if (ihigh > self->count)
ihigh = self->count;
-
+
return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1);
}
@@ -539,22 +536,56 @@ static struct PyMethodDef methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyObject*
-path_getattr(PyPathObject* self, char* name)
+static PyObject*
+path_getattr_id(PyPathObject* self, void* closure)
{
- PyObject* res;
+ return Py_BuildValue("n", (Py_ssize_t) self->xy);
+}
- res = Py_FindMethod(methods, (PyObject*) self, name);
- if (res)
- return res;
+static struct PyGetSetDef getsetters[] = {
+ { "id", (getter) path_getattr_id },
+ { NULL }
+};
- PyErr_Clear();
+static PyObject*
+path_subscript(PyPathObject* self, PyObject* item) {
+ if (PyIndex_Check(item)) {
+ Py_ssize_t i;
+ i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ return path_getitem(self, i);
+ }
+ if (PySlice_Check(item)) {
+ int len = 4;
+ Py_ssize_t start, stop, step, slicelength;
- if (strcmp(name, "id") == 0)
- return Py_BuildValue("l", (long) self->xy);
+#if PY_VERSION_HEX >= 0x03020000
+ if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+#else
+ if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0)
+ return NULL;
+#endif
- PyErr_SetString(PyExc_AttributeError, name);
- return NULL;
+ if (slicelength <= 0) {
+ double *xy = alloc_array(0);
+ return (PyObject*) path_new(0, xy, 0);
+ }
+ else if (step == 1) {
+ return path_getslice(self, start, stop);
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "slice steps not supported");
+ return NULL;
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "Path indices must be integers, not %.200s",
+ Py_TYPE(&item)->tp_name);
+ return NULL;
+ }
}
static PySequenceMethods path_as_sequence = {
@@ -567,21 +598,43 @@ static PySequenceMethods path_as_sequence = {
(ssizessizeobjargproc)0, /*sq_ass_slice*/
};
-statichere PyTypeObject PyPathType = {
- PyObject_HEAD_INIT(NULL)
- 0, /*ob_size*/
+static PyMappingMethods path_as_mapping = {
+ (lenfunc)path_len,
+ (binaryfunc)path_subscript,
+ NULL
+};
+
+static PyTypeObject PyPathType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
"Path", /*tp_name*/
sizeof(PyPathObject), /*tp_size*/
0, /*tp_itemsize*/
/* methods */
(destructor)path_dealloc, /*tp_dealloc*/
0, /*tp_print*/
- (getattrfunc)path_getattr, /*tp_getattr*/
+ 0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number */
&path_as_sequence, /*tp_as_sequence */
- 0, /*tp_as_mapping */
- 0, /*tp_hash*/
+ &path_as_mapping, /*tp_as_mapping */
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ methods, /*tp_methods*/
+ 0, /*tp_members*/
+ getsetters, /*tp_getset*/
};
+
diff --git a/py3.h b/py3.h
new file mode 100644
index 000000000..8adfac081
--- /dev/null
+++ b/py3.h
@@ -0,0 +1,55 @@
+/*
+ Python3 definition file to consistently map the code to Python 2.6 or
+ Python 3.
+
+ PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions
+ are mapped to PyLong.
+
+ PyString, on the other hand, was split into PyBytes and PyUnicode. We map
+ both back onto PyString, so use PyBytes or PyUnicode where appropriate. The
+ only exception to this is _imagingft.c, where PyUnicode is left alone.
+*/
+
+#if PY_VERSION_HEX >= 0x03000000
+#define PY_ARG_BYTES_LENGTH "y#"
+
+/* Map PyInt -> PyLong */
+#define PyInt_AsLong PyLong_AsLong
+#define PyInt_Check PyLong_Check
+#define PyInt_FromLong PyLong_FromLong
+#define PyInt_AS_LONG PyLong_AS_LONG
+#define PyInt_FromSsize_t PyLong_FromSsize_t
+
+#else /* PY_VERSION_HEX < 0x03000000 */
+#define PY_ARG_BYTES_LENGTH "s#"
+
+#if !defined(KEEP_PY_UNICODE)
+/* Map PyUnicode -> PyString */
+#undef PyUnicode_AsString
+#undef PyUnicode_AS_STRING
+#undef PyUnicode_Check
+#undef PyUnicode_FromStringAndSize
+#undef PyUnicode_FromString
+#undef PyUnicode_FromFormat
+#undef PyUnicode_DecodeFSDefault
+
+#define PyUnicode_AsString PyString_AsString
+#define PyUnicode_AS_STRING PyString_AS_STRING
+#define PyUnicode_Check PyString_Check
+#define PyUnicode_FromStringAndSize PyString_FromStringAndSize
+#define PyUnicode_FromString PyString_FromString
+#define PyUnicode_FromFormat PyString_FromFormat
+#define PyUnicode_DecodeFSDefault PyString_FromString
+#endif
+
+/* Map PyBytes -> PyString */
+#define PyBytesObject PyStringObject
+#define PyBytes_AsString PyString_AsString
+#define PyBytes_AS_STRING PyString_AS_STRING
+#define PyBytes_Check PyString_Check
+#define PyBytes_AsStringAndSize PyString_AsStringAndSize
+#define PyBytes_FromStringAndSize PyString_FromStringAndSize
+#define PyBytes_FromString PyString_FromString
+#define _PyBytes_Resize _PyString_Resize
+
+#endif /* PY_VERSION_HEX < 0x03000000 */
diff --git a/selftest.py b/selftest.py
index 22bbe433b..1f905b9a7 100644
--- a/selftest.py
+++ b/selftest.py
@@ -1,27 +1,34 @@
# minimal sanity check
+from __future__ import print_function
+
+import sys
+import os
+
+if "--installed" in sys.argv:
+ sys_path_0 = sys.path[0]
+ del sys.path[0]
+
+from PIL import Image, ImageDraw, ImageFilter, ImageMath
+
+if "--installed" in sys.argv:
+ sys.path.insert(0, sys_path_0)
ROOT = "."
-import os, sys
-sys.path.insert(0, ROOT)
-
-from PIL import Image
-from PIL import ImageDraw
-from PIL import ImageFilter
-from PIL import ImageMath
-
try:
Image.core.ping
-except ImportError, v:
- print "***", v
+except ImportError as v:
+ print("***", v)
sys.exit()
except AttributeError:
pass
+
def _info(im):
im.load()
return im.format, im.mode, im.size
+
def testimage():
"""
PIL lets you create in-memory images with various pixel types:
@@ -49,19 +56,19 @@ def testimage():
('PPM', 'RGB', (128, 128))
>>> try:
... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg")))
- ... except IOError, v:
- ... print v
+ ... except IOError as v:
+ ... print(v)
('JPEG', 'RGB', (128, 128))
PIL doesn't actually load the image data until it's needed,
or you call the "load" method:
>>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm"))
- >>> print im.im # internal image attribute
+ >>> print(im.im) # internal image attribute
None
>>> a = im.load()
- >>> type(im.im)
-
+ >>> type(im.im) # doctest: +ELLIPSIS
+ <... '...ImagingCore'>
You can apply many different operations on images. Most
operations return a new image:
@@ -89,17 +96,17 @@ def testimage():
2
>>> len(im.histogram())
768
- >>> _info(im.point(range(256)*3))
+ >>> _info(im.point(list(range(256))*3))
(None, 'RGB', (128, 128))
>>> _info(im.resize((64, 64)))
(None, 'RGB', (64, 64))
>>> _info(im.rotate(45))
(None, 'RGB', (128, 128))
- >>> map(_info, im.split())
+ >>> [_info(ch) for ch in im.split()]
[(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))]
>>> len(im.convert("1").tobitmap())
10456
- >>> len(im.tostring())
+ >>> len(im.tobytes())
49152
>>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0)))
(None, 'RGB', (512, 512))
@@ -157,17 +164,18 @@ def testimage():
def check_module(feature, module):
try:
- __import__("PIL." + module)
+ __import__(module)
except ImportError:
- print "***", feature, "support not installed"
+ print("***", feature, "support not installed")
else:
- print "---", feature, "support ok"
+ print("---", feature, "support ok")
+
def check_codec(feature, codec):
if codec + "_encoder" not in dir(Image.core):
- print "***", feature, "support not installed"
+ print("***", feature, "support not installed")
else:
- print "---", feature, "support ok"
+ print("---", feature, "support ok")
if __name__ == "__main__":
@@ -175,28 +183,39 @@ if __name__ == "__main__":
exit_status = 0
- print "-"*68
- print "PIL", Image.VERSION, "TEST SUMMARY "
- print "-"*68
- print "Python modules loaded from", os.path.dirname(Image.__file__)
- print "Binary modules loaded from", os.path.dirname(Image.core.__file__)
- print "-"*68
- check_module("PIL CORE", "_imaging")
- check_module("TKINTER", "_imagingtk")
+ print("-"*68)
+ print("Pillow", Image.PILLOW_VERSION, "TEST SUMMARY ")
+ print("-"*68)
+ print("Python modules loaded from", os.path.dirname(Image.__file__))
+ print("Binary modules loaded from", os.path.dirname(Image.core.__file__))
+ print("-"*68)
+ check_module("PIL CORE", "PIL._imaging")
+ check_module("TKINTER", "PIL._imagingtk")
check_codec("JPEG", "jpeg")
check_codec("ZLIB (PNG/ZIP)", "zip")
- check_module("FREETYPE2", "_imagingft")
- check_module("LITTLECMS", "_imagingcms")
- print "-"*68
+ check_codec("LIBTIFF", "libtiff")
+ check_module("FREETYPE2", "PIL._imagingft")
+ check_module("LITTLECMS2", "PIL._imagingcms")
+ check_module("WEBP", "PIL._webp")
+ try:
+ from PIL import _webp
+ if _webp.WebPDecoderBuggyAlpha():
+ print("***", "Transparent WEBP", "support not installed")
+ else:
+ print("---", "Transparent WEBP", "support ok")
+ except Exception:
+ pass
+ print("-"*68)
# use doctest to make sure the test program behaves as documented!
- import doctest, selftest
- print "Running selftest:"
+ import doctest
+ import selftest
+ print("Running selftest:")
status = doctest.testmod(selftest)
if status[0]:
- print "*** %s tests of %d failed." % status
+ print("*** %s tests of %d failed." % status)
exit_status = 1
else:
- print "--- %s tests passed." % status[1]
+ print("--- %s tests passed." % status[1])
sys.exit(exit_status)
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 861a9f554..000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[egg_info]
-tag_build =
-tag_date = 0
-tag_svn_revision = 0
-
diff --git a/setup.py b/setup.py
index 5e1a0a7c1..fb2aeffb3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,33 +1,91 @@
-#!/usr/bin/env python
-#
-# Setup script for PIL 1.1.5 and later (repackaged by Chris McDonough and
-# Hanno Schlichting for setuptools compatibility)
-#
-# Usage: python setup.py install
-#
+# > pyroma .
+# ------------------------------
+# Checking .
+# Found Pillow
+# ------------------------------
+# Final rating: 10/10
+# Your cheese is so fresh most people think it's a cream: Mascarpone
+# ------------------------------
+from __future__ import print_function
+import glob
+import os
+import platform as plat
+import re
+import struct
+import sys
-import glob, os, re, struct, string, sys
+from distutils.command.build_ext import build_ext
+from distutils import sysconfig
+from setuptools import Extension, setup, find_packages
-def libinclude(root):
+
+_IMAGING = (
+ "decode", "encode", "map", "display", "outline", "path")
+
+_LIB_IMAGING = (
+ "Access", "AlphaComposite", "Antialias", "Bands", "BitDecode", "Blend",
+ "Chops", "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw",
+ "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
+ "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
+ "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix",
+ "ModeFilter", "MspDecode", "Negative", "Offset", "Pack",
+ "PackDecode", "Palette", "Paste", "Quant", "QuantOctree", "QuantHash",
+ "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
+ "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
+ "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
+ "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode")
+
+
+def _add_directory(path, dir, where=None):
+ if dir is None:
+ return
+ dir = os.path.realpath(dir)
+ if os.path.isdir(dir) and dir not in path:
+ if where is None:
+ path.append(dir)
+ else:
+ path.insert(where, dir)
+
+
+def _find_include_file(self, include):
+ for directory in self.compiler.include_dirs:
+ if os.path.isfile(os.path.join(directory, include)):
+ return 1
+ return 0
+
+
+def _find_library_file(self, library):
+ # Fix for 3.2.x <3.2.4, 3.3.0, shared lib extension is the python shared
+ # lib extension, not the system shared lib extension: e.g. .cpython-33.so
+ # vs .so. See Python bug http://bugs.python.org/16754
+ if 'cpython' in self.compiler.shared_lib_extension:
+ existing = self.compiler.shared_lib_extension
+ self.compiler.shared_lib_extension = "." + existing.split('.')[-1]
+ ret = self.compiler.find_library_file(
+ self.compiler.library_dirs, library)
+ self.compiler.shared_lib_extension = existing
+ return ret
+ else:
+ return self.compiler.find_library_file(
+ self.compiler.library_dirs, library)
+
+
+def _lib_include(root):
# map root to (root/lib, root/include)
return os.path.join(root, "lib"), os.path.join(root, "include")
-# --------------------------------------------------------------------
-# Library pointers.
-#
-# Use None to look for the libraries in well-known library locations.
-# Use a string to specify a single directory, for both the library and
-# the include files. Use a tuple to specify separate directories:
-# (libpath, includepath). Examples:
-#
-# JPEG_ROOT = "/home/libraries/jpeg-6b"
-# TIFF_ROOT = "/opt/tiff/lib", "/opt/tiff/include"
-#
-# If you have "lib" and "include" directories under a common parent,
-# you can use the "libinclude" helper:
-#
-# TIFF_ROOT = libinclude("/opt/tiff")
+def _read(file):
+ return open(file, 'rb').read()
+
+try:
+ import _tkinter
+except ImportError:
+ _tkinter = None
+
+
+NAME = 'Pillow'
+VERSION = '2.3.0'
TCL_ROOT = None
JPEG_ROOT = None
ZLIB_ROOT = None
@@ -35,79 +93,51 @@ TIFF_ROOT = None
FREETYPE_ROOT = None
LCMS_ROOT = None
-# FIXME: add mechanism to explicitly *disable* the use of a library
-
-# --------------------------------------------------------------------
-# Identification
-
-NAME = "Pillow"
-DESCRIPTION = "Python Imaging Library (fork)"
-#AUTHOR = "Secret Labs AB (PythonWare)", "info@pythonware.com"
-AUTHOR = "Alex Clark (fork author)", "aclark@aclark.net"
-#HOMEPAGE = "http://www.pythonware.com/products/pil"
-HOMEPAGE = "http://github.com/Pillow"
-#DOWNLOAD_URL = "http://effbot.org/downloads/%s-%s.tar.gz" # name, version
-
-# --------------------------------------------------------------------
-# Core library
-
-IMAGING = [
- "decode", "encode", "map", "display", "outline", "path",
- ]
-
-LIBIMAGING = [
- "Access", "Antialias", "Bands", "BitDecode", "Blend", "Chops",
- "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw",
- "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
- "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
- "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix",
- "ModeFilter", "MspDecode", "Negative", "Offset", "Pack",
- "PackDecode", "Palette", "Paste", "Quant", "QuantHash",
- "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
- "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
- "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
- "XbmEncode", "ZipDecode", "ZipEncode"
- ]
-
-# --------------------------------------------------------------------
-
-from distutils import sysconfig
-from distutils.command.build_ext import build_ext
-from setuptools import setup, find_packages, Extension
-
-try:
- import _tkinter
-except ImportError:
- _tkinter = None
-
-def add_directory(path, dir, where=None):
- if dir and os.path.isdir(dir) and dir not in path:
- if where is None:
- path.append(dir)
- else:
- path.insert(where, dir)
-
-def find_include_file(self, include):
- for directory in self.compiler.include_dirs:
- if os.path.isfile(os.path.join(directory, include)):
- return 1
- return 0
-
-def find_library_file(self, library):
- return self.compiler.find_library_file(self.compiler.library_dirs, library)
-
-def find_version(filename):
- for line in open(filename).readlines():
- m = re.search("VERSION\s*=\s*\"([^\"]+)\"", line)
- if m:
- return m.group(1)
- return None
-
-#VERSION = find_version("PIL/Image.py")
-VERSION = "1.0"
class pil_build_ext(build_ext):
+ class feature:
+ zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None
+ required = []
+
+ def require(self, feat):
+ return feat in self.required
+ def want(self, feat):
+ return getattr(self, feat) is None
+
+ def __iter__(self):
+ for x in dir(self):
+ if x[1] != '_':
+ yield x
+
+ feature = feature()
+
+ user_options = build_ext.user_options + [
+ ('disable-%s' % x, None, 'Disable support for %s' % x)
+ for x in feature
+ ] + [
+ ('enable-%s' % x, None, 'Enable support for %s' % x)
+ for x in feature
+ ]
+
+ def initialize_options(self):
+ build_ext.initialize_options(self)
+ for x in self.feature:
+ setattr(self, 'disable_%s' % x, None)
+ setattr(self, 'enable_%s' % x, None)
+
+ def finalize_options(self):
+ build_ext.finalize_options(self)
+ for x in self.feature:
+ if getattr(self, 'disable_%s' % x):
+ setattr(self.feature, x, False)
+ if getattr(self, 'enable_%s' % x):
+ raise ValueError(
+ 'Conflicting options: --enable-%s and --disable-%s'
+ % (x, x))
+ if getattr(self, 'enable_%s' % x):
+ self.feature.required.append(x)
+
def build_extensions(self):
global TCL_ROOT
@@ -115,36 +145,134 @@ class pil_build_ext(build_ext):
library_dirs = []
include_dirs = []
- add_directory(include_dirs, "libImaging")
+ _add_directory(include_dirs, "libImaging")
+
+ #
+ # add configured kits
+
+ for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT,
+ FREETYPE_ROOT, LCMS_ROOT):
+ if isinstance(root, type(())):
+ lib_root, include_root = root
+ else:
+ lib_root = include_root = root
+ _add_directory(library_dirs, lib_root)
+ _add_directory(include_dirs, include_root)
+
+ # 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', 'CPATH', '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'):
+ if k in os.environ:
+ for d in os.environ[k].split(os.path.pathsep):
+ _add_directory(library_dirs, d)
+
+ prefix = sysconfig.get_config_var("prefix")
+ if prefix:
+ _add_directory(library_dirs, os.path.join(prefix, "lib"))
+ _add_directory(include_dirs, os.path.join(prefix, "include"))
#
# add platform directories
if sys.platform == "cygwin":
# pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory
- add_directory(library_dirs, os.path.join(
- "/usr/lib", "python%s" % sys.version[:3], "config"
- ))
+ _add_directory(library_dirs, os.path.join(
+ "/usr/lib", "python%s" % sys.version[:3], "config"))
elif sys.platform == "darwin":
# attempt to make sure we pick freetype2 over other versions
- add_directory(include_dirs, "/sw/include/freetype2")
- add_directory(include_dirs, "/sw/lib/freetype2/include")
+ _add_directory(include_dirs, "/sw/include/freetype2")
+ _add_directory(include_dirs, "/sw/lib/freetype2/include")
# fink installation directories
- add_directory(library_dirs, "/sw/lib")
- add_directory(include_dirs, "/sw/include")
+ _add_directory(library_dirs, "/sw/lib")
+ _add_directory(include_dirs, "/sw/include")
# darwin ports installation directories
- add_directory(library_dirs, "/opt/local/lib")
- add_directory(include_dirs, "/opt/local/include")
+ _add_directory(library_dirs, "/opt/local/lib")
+ _add_directory(include_dirs, "/opt/local/include")
+
+ # if homebrew is installed, use its lib and include directories
+ import subprocess
+ try:
+ prefix = subprocess.check_output(['brew', '--prefix'])
+ if prefix:
+ prefix = prefix.strip()
+ _add_directory(library_dirs, os.path.join(prefix, 'lib'))
+ _add_directory(include_dirs, os.path.join(prefix, 'include'))
+
+ # freetype2 is a key-only brew under opt/
+ _add_directory(library_dirs, os.path.join(prefix, 'opt', 'freetype', 'lib'))
+ _add_directory(include_dirs, os.path.join(prefix, 'opt', 'freetype', 'include'))
+ except:
+ pass # homebrew not installed
+
+ # freetype2 ships with X11 (after homebrew, so that homebrew freetype is preferred)
+ _add_directory(library_dirs, "/usr/X11/lib")
+ _add_directory(include_dirs, "/usr/X11/include")
+
+ elif sys.platform.startswith("linux"):
+ for platform_ in (plat.processor(), plat.architecture()[0]):
+
+ if not platform_:
+ continue
+
+ if platform_ in ["x86_64", "64bit"]:
+ _add_directory(library_dirs, "/lib64")
+ _add_directory(library_dirs, "/usr/lib64")
+ _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu")
+ break
+ elif platform_ in ["i386", "i686", "32bit"]:
+ _add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
+ break
+ elif platform_ in ["aarch64"]:
+ _add_directory(library_dirs, "/usr/lib64")
+ _add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu")
+ break
+ elif platform_ in ["arm", "armv7l"]:
+ _add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi")
+ break
+ elif platform_ in ["ppc64"]:
+ _add_directory(library_dirs, "/usr/lib64")
+ _add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu")
+ _add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu")
+ break
+ elif platform_ in ["ppc"]:
+ _add_directory(library_dirs, "/usr/lib/ppc-linux-gnu")
+ _add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu")
+ break
+ elif platform_ in ["s390x"]:
+ _add_directory(library_dirs, "/usr/lib64")
+ _add_directory(library_dirs, "/usr/lib/s390x-linux-gnu")
+ break
+ elif platform_ in ["s390"]:
+ _add_directory(library_dirs, "/usr/lib/s390-linux-gnu")
+ break
+ else:
+ raise ValueError(
+ "Unable to identify Linux platform: `%s`" % platform_)
+
+ # XXX Kludge. Above /\ we brute force support multiarch. Here we
+ # try Barry's more general approach. Afterward, something should
+ # work ;-)
+ self.add_multiarch_paths()
+
+ elif sys.platform.startswith("netbsd"):
+ _add_directory(library_dirs, "/usr/pkg/lib")
+ _add_directory(include_dirs, "/usr/pkg/include")
- add_directory(library_dirs, "/usr/local/lib")
# FIXME: check /opt/stuff directories here?
- prefix = sysconfig.get_config_var("prefix")
- if prefix:
- add_directory(library_dirs, os.path.join(prefix, "lib"))
- add_directory(include_dirs, os.path.join(prefix, "include"))
-
#
# locate tkinter libraries
@@ -163,46 +291,32 @@ class pil_build_ext(build_ext):
os.path.join("/py" + PYVERSION, "Tcl"),
os.path.join("/python" + PYVERSION, "Tcl"),
"/Tcl", "/Tcl" + TCLVERSION, "/Tcl" + TCL_VERSION,
- os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"),
- ]
+ os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"), ]
for TCL_ROOT in roots:
TCL_ROOT = os.path.abspath(TCL_ROOT)
if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")):
# FIXME: use distutils logging (?)
- print "--- using Tcl/Tk libraries at", TCL_ROOT
- print "--- using Tcl/Tk version", TCL_VERSION
- TCL_ROOT = libinclude(TCL_ROOT)
+ print("--- using Tcl/Tk libraries at", TCL_ROOT)
+ print("--- using Tcl/Tk version", TCL_VERSION)
+ TCL_ROOT = _lib_include(TCL_ROOT)
break
else:
TCL_ROOT = None
- #
- # add configured kits
-
- for root in (TCL_ROOT, JPEG_ROOT, TCL_ROOT, TIFF_ROOT, ZLIB_ROOT,
- FREETYPE_ROOT, LCMS_ROOT):
- if isinstance(root, type(())):
- lib_root, include_root = root
- else:
- lib_root = include_root = root
- add_directory(library_dirs, lib_root)
- add_directory(include_dirs, include_root)
-
- #
# add standard directories
# look for tcl specific subdirectory (e.g debian)
if _tkinter:
tcl_dir = "/usr/include/tcl" + TCL_VERSION
if os.path.isfile(os.path.join(tcl_dir, "tk.h")):
- add_directory(include_dirs, tcl_dir)
+ _add_directory(include_dirs, tcl_dir)
# standard locations
- add_directory(library_dirs, "/usr/local/lib")
- add_directory(include_dirs, "/usr/local/include")
+ _add_directory(library_dirs, "/usr/local/lib")
+ _add_directory(include_dirs, "/usr/local/include")
- add_directory(library_dirs, "/usr/lib")
- add_directory(include_dirs, "/usr/include")
+ _add_directory(library_dirs, "/usr/lib")
+ _add_directory(include_dirs, "/usr/include")
#
# insert new dirs *before* default libs, to avoid conflicts
@@ -214,69 +328,98 @@ class pil_build_ext(build_ext):
#
# look for available libraries
- class feature:
- zlib = jpeg = tiff = freetype = tcl = tk = lcms = None
- feature = feature()
+ feature = self.feature
- if find_include_file(self, "zlib.h"):
- if find_library_file(self, "z"):
- feature.zlib = "z"
- elif sys.platform == "win32" and find_library_file(self, "zlib"):
- feature.zlib = "zlib" # alternative name
+ if feature.want('zlib'):
+ if _find_include_file(self, "zlib.h"):
+ if _find_library_file(self, "z"):
+ feature.zlib = "z"
+ elif sys.platform == "win32" and _find_library_file(self, "zlib"):
+ feature.zlib = "zlib" # alternative name
- if find_include_file(self, "jpeglib.h"):
- if find_library_file(self, "jpeg"):
- feature.jpeg = "jpeg"
- elif sys.platform == "win32" and find_library_file(self, "libjpeg"):
- feature.jpeg = "libjpeg" # alternative name
+ if feature.want('jpeg'):
+ if _find_include_file(self, "jpeglib.h"):
+ if _find_library_file(self, "jpeg"):
+ feature.jpeg = "jpeg"
+ elif (
+ sys.platform == "win32" and
+ _find_library_file(self, "libjpeg")):
+ feature.jpeg = "libjpeg" # alternative name
- if find_library_file(self, "tiff"):
- feature.tiff = "tiff"
+ if feature.want('tiff'):
+ if _find_library_file(self, "tiff"):
+ feature.tiff = "tiff"
+ if sys.platform == "win32" and _find_library_file(self, "libtiff"):
+ feature.tiff = "libtiff"
+ if sys.platform == "darwin" and _find_library_file(self, "libtiff"):
+ feature.tiff = "libtiff"
- if find_library_file(self, "freetype"):
- # look for freetype2 include files
- freetype_version = 0
- for dir in self.compiler.include_dirs:
- if os.path.isfile(os.path.join(dir, "ft2build.h")):
- freetype_version = 21
+ if feature.want('freetype'):
+ if _find_library_file(self, "freetype"):
+ # look for freetype2 include files
+ freetype_version = 0
+ for dir in self.compiler.include_dirs:
+ if os.path.isfile(os.path.join(dir, "ft2build.h")):
+ freetype_version = 21
+ dir = os.path.join(dir, "freetype2")
+ break
dir = os.path.join(dir, "freetype2")
- break
- dir = os.path.join(dir, "freetype2")
- if os.path.isfile(os.path.join(dir, "ft2build.h")):
- freetype_version = 21
- break
- if os.path.isdir(os.path.join(dir, "freetype")):
- freetype_version = 20
- break
- if freetype_version:
- feature.freetype = "freetype"
- feature.freetype_version = freetype_version
- if dir:
- add_directory(self.compiler.include_dirs, dir, 0)
+ if os.path.isfile(os.path.join(dir, "ft2build.h")):
+ freetype_version = 21
+ break
+ if os.path.isdir(os.path.join(dir, "freetype")):
+ freetype_version = 20
+ break
+ if freetype_version:
+ feature.freetype = "freetype"
+ feature.freetype_version = freetype_version
+ if dir:
+ _add_directory(self.compiler.include_dirs, dir, 0)
- if find_include_file(self, "lcms.h"):
- if find_library_file(self, "lcms"):
- feature.lcms = "lcms"
+ if feature.want('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"):
+ if _tkinter and _find_include_file(self, "tk.h"):
# the library names may vary somewhat (e.g. tcl84 or tcl8.4)
version = TCL_VERSION[0] + TCL_VERSION[2]
- if find_library_file(self, "tcl" + version):
- feature.tcl = "tcl" + version
- elif find_library_file(self, "tcl" + TCL_VERSION):
- feature.tcl = "tcl" + TCL_VERSION
- if find_library_file(self, "tk" + version):
- feature.tk = "tk" + version
- elif find_library_file(self, "tk" + TCL_VERSION):
- feature.tk = "tk" + TCL_VERSION
+ if feature.want('tcl'):
+ if _find_library_file(self, "tcl" + version):
+ feature.tcl = "tcl" + version
+ elif _find_library_file(self, "tcl" + TCL_VERSION):
+ feature.tcl = "tcl" + TCL_VERSION
+ if feature.want('tk'):
+ if _find_library_file(self, "tk" + version):
+ feature.tk = "tk" + version
+ elif _find_library_file(self, "tk" + TCL_VERSION):
+ feature.tk = "tk" + TCL_VERSION
+
+ if feature.want('webp'):
+ if (_find_include_file(self, "webp/encode.h") and
+ _find_include_file(self, "webp/decode.h")):
+ if _find_library_file(self, "webp"): # in googles precompiled zip it is call "libwebp"
+ feature.webp = "webp"
+
+ if feature.want('webpmux'):
+ if (_find_include_file(self, "webp/mux.h") and
+ _find_include_file(self, "webp/demux.h")):
+ if _find_library_file(self, "webpmux") and _find_library_file(self, "webpdemux"):
+ feature.webpmux = "webpmux"
+
+ for f in feature:
+ if not getattr(feature, f) and feature.require(f):
+ raise ValueError(
+ '--enable-%s requested but %s not found, aborting.'
+ % (f, f))
#
# core library
files = ["_imaging.c"]
- for file in IMAGING:
+ for file in _IMAGING:
files.append(file + ".c")
- for file in LIBIMAGING:
+ for file in _LIB_IMAGING:
files.append(os.path.join("libImaging", file + ".c"))
libs = []
@@ -287,14 +430,16 @@ class pil_build_ext(build_ext):
if feature.zlib:
libs.append(feature.zlib)
defs.append(("HAVE_LIBZ", None))
+ if feature.tiff:
+ libs.append(feature.tiff)
+ defs.append(("HAVE_LIBTIFF", None))
if sys.platform == "win32":
libs.extend(["kernel32", "user32", "gdi32"])
- if struct.unpack("h", "\0\1")[0] == 1:
+ if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1:
defs.append(("WORDS_BIGENDIAN", None))
exts = [(Extension(
- "_imaging", files, libraries=libs, define_macros=defs
- ))]
+ "PIL._imaging", files, libraries=libs, define_macros=defs))]
#
# additional libraries
@@ -304,54 +449,61 @@ class pil_build_ext(build_ext):
if feature.freetype_version == 20:
defs.append(("USE_FREETYPE_2_0", None))
exts.append(Extension(
- "_imagingft", ["_imagingft.c"], libraries=["freetype"],
- define_macros=defs
- ))
+ "PIL._imagingft", ["_imagingft.c"], libraries=["freetype"],
+ define_macros=defs))
if os.path.isfile("_imagingtiff.c") and feature.tiff:
exts.append(Extension(
- "_imagingtiff", ["_imagingtiff.c"], libraries=["tiff"]
- ))
+ "PIL._imagingtiff", ["_imagingtiff.c"], libraries=["tiff"]))
if os.path.isfile("_imagingcms.c") and feature.lcms:
extra = []
if sys.platform == "win32":
extra.extend(["user32", "gdi32"])
exts.append(Extension(
- "_imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra
- ))
+ "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra))
+
+ if os.path.isfile("_webp.c") and feature.webp:
+ libs = ["webp"]
+ defs = []
+
+ if feature.webpmux:
+ defs.append(("HAVE_WEBPMUX", None))
+ libs.append("webpmux")
+ libs.append("webpdemux")
+
+ exts.append(Extension(
+ "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs))
if sys.platform == "darwin":
# locate Tcl/Tk frameworks
frameworks = []
framework_roots = [
"/Library/Frameworks",
- "/System/Library/Frameworks"
- ]
+ "/System/Library/Frameworks"]
for root in framework_roots:
- if (os.path.exists(os.path.join(root, "Tcl.framework")) and
- os.path.exists(os.path.join(root, "Tk.framework"))):
- print "--- using frameworks at", root
+ if (
+ os.path.exists(os.path.join(root, "Tcl.framework")) and
+ os.path.exists(os.path.join(root, "Tk.framework"))):
+ print("--- using frameworks at %s" % root)
frameworks = ["-framework", "Tcl", "-framework", "Tk"]
dir = os.path.join(root, "Tcl.framework", "Headers")
- add_directory(self.compiler.include_dirs, dir, 0)
+ _add_directory(self.compiler.include_dirs, dir, 0)
dir = os.path.join(root, "Tk.framework", "Headers")
- add_directory(self.compiler.include_dirs, dir, 1)
+ _add_directory(self.compiler.include_dirs, dir, 1)
break
if frameworks:
exts.append(Extension(
- "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
- extra_compile_args=frameworks, extra_link_args=frameworks
- ))
- feature.tcl = feature.tk = 1 # mark as present
+ "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
+ extra_compile_args=frameworks, extra_link_args=frameworks))
+ feature.tcl = feature.tk = 1 # mark as present
elif feature.tcl and feature.tk:
exts.append(Extension(
- "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
- libraries=[feature.tcl, feature.tk]
- ))
+ "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
+ libraries=[feature.tcl, feature.tk]))
if os.path.isfile("_imagingmath.c"):
- exts.append(Extension("_imagingmath", ["_imagingmath.c"]))
+ exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"]))
self.extensions[:] = exts
@@ -369,56 +521,57 @@ class pil_build_ext(build_ext):
def summary_report(self, feature, unsafe_zlib):
- print "-" * 68
- print "PIL", VERSION, "SETUP SUMMARY"
- print "-" * 68
- print "version ", VERSION
- v = string.split(sys.version, "[")
- print "platform ", sys.platform, string.strip(v[0])
+ print("-" * 68)
+ print("PIL SETUP SUMMARY")
+ print("-" * 68)
+ print("version Pillow %s" % VERSION)
+ v = sys.version.split("[")
+ print("platform %s %s" % (sys.platform, v[0].strip()))
for v in v[1:]:
- print " ", string.strip("[" + v)
- print "-" * 68
+ print(" [%s" % v.strip())
+ print("-" * 68)
options = [
(feature.tcl and feature.tk, "TKINTER"),
(feature.jpeg, "JPEG"),
(feature.zlib, "ZLIB (PNG/ZIP)"),
- # (feature.tiff, "experimental TIFF G3/G4 read"),
+ (feature.tiff, "LIBTIFF"),
(feature.freetype, "FREETYPE2"),
- (feature.lcms, "LITTLECMS"),
- ]
+ (feature.lcms, "LITTLECMS2"),
+ (feature.webp, "WEBP"),
+ (feature.webpmux, "WEBPMUX"), ]
all = 1
for option in options:
if option[0]:
- print "---", option[1], "support available"
+ print("--- %s support available" % option[1])
else:
- print "***", option[1], "support not available",
+ print("*** %s support not available" % option[1])
if option[1] == "TKINTER" and _tkinter:
version = _tkinter.TCL_VERSION
- print "(Tcl/Tk %s libraries needed)" % version,
- print
+ print("(Tcl/Tk %s libraries needed)" % version)
all = 0
if feature.zlib and unsafe_zlib:
- print
- print "*** Warning: zlib", unsafe_zlib,
- print "may contain a security vulnerability."
- print "*** Consider upgrading to zlib 1.2.3 or newer."
- print "*** See: http://www.kb.cert.org/vuls/id/238678"
- print " http://www.kb.cert.org/vuls/id/680620"
- print " http://www.gzip.org/zlib/advisory-2002-03-11.txt"
- print
+ print("")
+ print("*** Warning: zlib", unsafe_zlib)
+ print("may contain a security vulnerability.")
+ print("*** Consider upgrading to zlib 1.2.3 or newer.")
+ print("*** See: http://www.kb.cert.org/vuls/id/238678")
+ print(" http://www.kb.cert.org/vuls/id/680620")
+ print(" http://www.gzip.org/zlib/advisory-2002-03-11.txt")
+ print("")
- print "-" * 68
+ print("-" * 68)
if not all:
- print "To add a missing option, make sure you have the required"
- print "library, and set the corresponding ROOT variable in the"
- print "setup.py script."
- print
+ print("To add a missing option, make sure you have the required")
+ print("library, and set the corresponding ROOT variable in the")
+ print("setup.py script.")
+ print("")
- print "To check the build, run the selftest.py script."
+ print("To check the build, run the selftest.py script.")
+ print("")
def check_zlib_version(self, include_dirs):
# look for unsafe versions of zlib
@@ -435,43 +588,62 @@ class pil_build_ext(build_ext):
if m.group(1) < "1.2.3":
return m.group(1)
-#
-# build!
+ # http://hg.python.org/users/barry/rev/7e8deab93d5a
+ def add_multiarch_paths(self):
+ # Debian/Ubuntu multiarch support.
+ # https://wiki.ubuntu.com/MultiarchSpec
+ # self.build_temp
+ tmpfile = os.path.join(self.build_temp, 'multiarch')
+ if not os.path.exists(self.build_temp):
+ os.makedirs(self.build_temp)
+ ret = os.system(
+ 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' %
+ tmpfile)
+ try:
+ if ret >> 8 == 0:
+ fp = open(tmpfile, 'r')
+ multiarch_path_component = fp.readline().strip()
+ _add_directory(
+ self.compiler.library_dirs,
+ '/usr/lib/' + multiarch_path_component)
+ _add_directory(
+ self.compiler.include_dirs,
+ '/usr/include/' + multiarch_path_component)
+ finally:
+ os.unlink(tmpfile)
-if __name__ == "__main__":
+setup(
+ name=NAME,
+ version=VERSION,
+ description='Python Imaging Library (Fork)',
+ long_description=(
+ _read('README.rst') + b'\n' +
+ _read('CHANGES.rst')).decode('utf-8'),
+ author='Alex Clark (fork author)',
+ author_email='aclark@aclark.net',
+ url='http://python-imaging.github.io/',
+ classifiers=[
+ "Development Status :: 6 - Mature",
+ "Topic :: Multimedia :: Graphics",
+ "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
+ "Topic :: Multimedia :: Graphics :: Capture :: Scanners",
+ "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
+ "Topic :: Multimedia :: Graphics :: Graphics Conversion",
+ "Topic :: Multimedia :: Graphics :: Viewers",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.2",
+ "Programming Language :: Python :: 3.3", ],
+ cmdclass={"build_ext": pil_build_ext},
+ ext_modules=[Extension("PIL._imaging", ["_imaging.c"])],
+ include_package_data=True,
+ packages=find_packages(),
+ scripts=glob.glob("Scripts/pil*.py"),
+ test_suite='PIL.tests',
+ keywords=["Imaging",],
+ license='Standard PIL License',
+ zip_safe=True,
+ )
- try:
- # add necessary to distutils (for backwards compatibility)
- from distutils.dist import DistributionMetadata
- DistributionMetadata.classifiers = None
- DistributionMetadata.download_url = None
- DistributionMetadata.platforms = None
- except:
- pass
-
- setup(
- author=AUTHOR[0], author_email=AUTHOR[1],
- classifiers=[
- "Development Status :: 6 - Mature",
- "Topic :: Multimedia :: Graphics",
- "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
- "Topic :: Multimedia :: Graphics :: Capture :: Scanners",
- "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
- "Topic :: Multimedia :: Graphics :: Graphics Conversion",
- "Topic :: Multimedia :: Graphics :: Viewers",
- ],
- cmdclass = {"build_ext": pil_build_ext},
- description=DESCRIPTION,
-# download_url=DOWNLOAD_URL % (NAME, VERSION),
- ext_modules = [Extension("_imaging", ["_imaging.c"])], # dummy
- extra_path = "PIL",
- license="Python (MIT style)",
- long_description=DESCRIPTION,
- name=NAME,
- packages=find_packages(),
- setup_requires=["setuptools_hg"],
- platforms="Python 1.5.2 and later.",
- scripts = glob.glob("Scripts/pil*.py"),
- url=HOMEPAGE,
- version=VERSION,
- )
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 000000000..8d6ea7067
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,14 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27, py32, py33
+
+[testenv]
+commands =
+ {envpython} setup.py clean
+ {envpython} setup.py build_ext --inplace
+ {envpython} selftest.py
+ {envpython} Tests/run.py --installed