%s" % (file, filename))
+ else:
+ os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
+ try: os.unlink(file)
+ except: pass
+
+
+# --------------------------------------------------------------------
+# GIF utilities
+
+def getheader(im, info=None):
+ """Return a list of strings representing a GIF header"""
+
+ optimize = info and info.get("optimize", 0)
+
+ s = [
+ "GIF87a" + # magic
+ o16(im.size[0]) + # size
+ o16(im.size[1]) +
+ chr(7 + 128) + # flags: bits + palette
+ chr(0) + # background
+ chr(0) # reserved/aspect
+ ]
+
+ if optimize:
+ # minimize color palette
+ i = 0
+ maxcolor = 0
+ for count in im.histogram():
+ if count:
+ maxcolor = i
+ i = i + 1
+ else:
+ maxcolor = 256
+
+ # global palette
+ if im.mode == "P":
+ # colour palette
+ s.append(im.im.getpalette("RGB")[:maxcolor*3])
+ else:
+ # greyscale
+ for i in range(maxcolor):
+ s.append(chr(i) * 3)
+
+ return s
+
+def getdata(im, offset = (0, 0), **params):
+ """Return a list of strings representing this image.
+ The first string is a local image header, the rest contains
+ encoded image data."""
+
+ class collector:
+ data = []
+ def write(self, data):
+ self.data.append(data)
+
+ im.load() # make sure raster data is available
+
+ fp = collector()
+
+ try:
+ im.encoderinfo = params
+
+ # local image header
+ fp.write("," +
+ o16(offset[0]) + # offset
+ o16(offset[1]) +
+ o16(im.size[0]) + # size
+ o16(im.size[1]) +
+ chr(0) + # flags
+ chr(8)) # bits
+
+ ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
+
+ fp.write("\0") # end of image data
+
+ finally:
+ del im.encoderinfo
+
+ return fp.data
+
+
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open(GifImageFile.format, GifImageFile, _accept)
+Image.register_save(GifImageFile.format, _save)
+Image.register_extension(GifImageFile.format, ".gif")
+Image.register_mime(GifImageFile.format, "image/gif")
+
+#
+# Uncomment the following line if you wish to use NETPBM/PBMPLUS
+# instead of the built-in "uncompressed" GIF encoder
+
+# Image.register_save(GifImageFile.format, _save_netpbm)
diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py
new file mode 100644
index 000000000..1a850dc53
--- /dev/null
+++ b/PIL/GimpGradientFile.py
@@ -0,0 +1,124 @@
+#
+# Python Imaging Library
+# $Id$
+#
+# stuff to read (and render) GIMP gradient files
+#
+# History:
+# 97-08-23 fl Created
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+from math import pi, log, sin, sqrt
+import string
+
+# --------------------------------------------------------------------
+# Stuff to translate curve segments to palette values (derived from
+# the corresponding code in GIMP, written by Federico Mena Quintero.
+# See the GIMP distribution for more information.)
+#
+
+EPSILON = 1e-10
+
+def linear(middle, pos):
+ if pos <= middle:
+ if middle < EPSILON:
+ return 0.0
+ else:
+ return 0.5 * pos / middle
+ else:
+ pos = pos - middle
+ middle = 1.0 - middle
+ if middle < EPSILON:
+ return 1.0
+ else:
+ return 0.5 + 0.5 * pos / middle
+
+def curved(middle, pos):
+ return pos ** (log(0.5) / log(max(middle, EPSILON)))
+
+def sine(middle, pos):
+ return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0
+
+def sphere_increasing(middle, pos):
+ return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2)
+
+def sphere_decreasing(middle, pos):
+ return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2)
+
+SEGMENTS = [ linear, curved, sine, sphere_increasing, sphere_decreasing ]
+
+class GradientFile:
+
+ gradient = None
+
+ def getpalette(self, entries = 256):
+
+ palette = []
+
+ ix = 0
+ x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix]
+
+ for i in range(entries):
+
+ x = i / float(entries-1)
+
+ while x1 < x:
+ ix = ix + 1
+ x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix]
+
+ w = x1 - x0
+
+ if w < EPSILON:
+ scale = segment(0.5, 0.5)
+ else:
+ scale = segment((xm - x0) / w, (x - x0) / w)
+
+ # expand to RGBA
+ r = chr(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5))
+ g = chr(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5))
+ b = chr(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5))
+ a = chr(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5))
+
+ # add to palette
+ palette.append(r + g + b + a)
+
+ return string.join(palette, ""), "RGBA"
+
+##
+# File handler for GIMP's gradient format.
+
+class GimpGradientFile(GradientFile):
+
+ def __init__(self, fp):
+
+ if fp.readline()[:13] != "GIMP Gradient":
+ raise SyntaxError, "not a GIMP gradient file"
+
+ count = int(fp.readline())
+
+ gradient = []
+
+ for i in range(count):
+
+ s = string.split(fp.readline())
+ w = map(float, s[:11])
+
+ x0, x1 = w[0], w[2]
+ xm = w[1]
+ rgb0 = w[3:7]
+ rgb1 = w[7:11]
+
+ segment = SEGMENTS[int(s[11])]
+ cspace = int(s[12])
+
+ if cspace != 0:
+ raise IOError, "cannot handle HSV colour space"
+
+ gradient.append((x0, x1, xm, rgb0, rgb1, segment))
+
+ self.gradient = gradient
diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py
new file mode 100644
index 000000000..aba85b71f
--- /dev/null
+++ b/PIL/GimpPaletteFile.py
@@ -0,0 +1,61 @@
+#
+# Python Imaging Library
+# $Id$
+#
+# stuff to read GIMP palette files
+#
+# History:
+# 1997-08-23 fl Created
+# 2004-09-07 fl Support GIMP 2.0 palette files.
+#
+# Copyright (c) Secret Labs AB 1997-2004. All rights reserved.
+# Copyright (c) Fredrik Lundh 1997-2004.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import re, string
+
+##
+# File handler for GIMP's palette format.
+
+class GimpPaletteFile:
+
+ rawmode = "RGB"
+
+ def __init__(self, fp):
+
+ self.palette = map(lambda i: chr(i)*3, range(256))
+
+ if fp.readline()[:12] != "GIMP Palette":
+ raise SyntaxError, "not a GIMP palette file"
+
+ i = 0
+
+ while i <= 255:
+
+ s = fp.readline()
+
+ if not s:
+ break
+ # skip fields and comment lines
+ if re.match("\w+:|#", s):
+ continue
+ if len(s) > 100:
+ raise SyntaxError, "bad palette file"
+
+ v = tuple(map(int, string.split(s)[:3]))
+ if len(v) != 3:
+ raise ValueError, "bad palette entry"
+
+ if 0 <= i <= 255:
+ self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2])
+
+ i = i + 1
+
+ self.palette = string.join(self.palette, "")
+
+
+ def getpalette(self):
+
+ return self.palette, self.rawmode
diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py
new file mode 100644
index 000000000..e232f90db
--- /dev/null
+++ b/PIL/GribStubImagePlugin.py
@@ -0,0 +1,68 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# GRIB stub adapter
+#
+# Copyright (c) 1996-2003 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image, ImageFile
+
+_handler = None
+
+##
+# Install application-specific GRIB image handler.
+#
+# @param handler Handler object.
+
+def register_handler(handler):
+ global _handler
+ _handler = handler
+
+# --------------------------------------------------------------------
+# Image adapter
+
+def _accept(prefix):
+ return prefix[0:4] == "GRIB" and prefix[7] == chr(1)
+
+class GribStubImageFile(ImageFile.StubImageFile):
+
+ format = "GRIB"
+ format_description = "GRIB"
+
+ def _open(self):
+
+ offset = self.fp.tell()
+
+ if not _accept(self.fp.read(8)):
+ raise SyntaxError("Not a GRIB file")
+
+ self.fp.seek(offset)
+
+ # make something up
+ self.mode = "F"
+ self.size = 1, 1
+
+ loader = self._load()
+ if loader:
+ loader.open(self)
+
+ def _load(self):
+ return _handler
+
+def _save(im, fp, filename):
+ if _handler is None or not hasattr("_handler", "save"):
+ raise IOError("GRIB save handler not installed")
+ _handler.save(im, fp, filename)
+
+
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept)
+Image.register_save(GribStubImageFile.format, _save)
+
+Image.register_extension(GribStubImageFile.format, ".grib")
diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py
new file mode 100644
index 000000000..bc5e37433
--- /dev/null
+++ b/PIL/Hdf5StubImagePlugin.py
@@ -0,0 +1,70 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# HDF5 stub adapter
+#
+# Copyright (c) 2000-2003 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image, ImageFile
+
+_handler = None
+
+##
+# Install application-specific HDF5 image handler.
+#
+# @param handler Handler object.
+
+def register_handler(handler):
+ global _handler
+ _handler = handler
+
+# --------------------------------------------------------------------
+# Image adapter
+
+def _accept(prefix):
+ return prefix[:8] == "\x89HDF\r\n\x1a\n"
+
+class HDF5StubImageFile(ImageFile.StubImageFile):
+
+ format = "HDF5"
+ format_description = "HDF5"
+
+ def _open(self):
+
+ offset = self.fp.tell()
+
+ if not _accept(self.fp.read(8)):
+ raise SyntaxError("Not an HDF file")
+
+ self.fp.seek(offset)
+
+ # make something up
+ self.mode = "F"
+ self.size = 1, 1
+
+ loader = self._load()
+ if loader:
+ loader.open(self)
+
+ def _load(self):
+ return _handler
+
+
+def _save(im, fp, filename):
+ if _handler is None or not hasattr("_handler", "save"):
+ raise IOError("HDF5 save handler not installed")
+ _handler.save(im, fp, filename)
+
+
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept)
+Image.register_save(HDF5StubImageFile.format, _save)
+
+Image.register_extension(HDF5StubImageFile.format, ".h5")
+Image.register_extension(HDF5StubImageFile.format, ".hdf")
diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py
new file mode 100644
index 000000000..4d68fa232
--- /dev/null
+++ b/PIL/IcnsImagePlugin.py
@@ -0,0 +1,211 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Mac OS X icns file decoder, based on icns.py by Bob Ippolito.
+#
+# history:
+# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies.
+#
+# Copyright (c) 2004 by Bob Ippolito.
+# Copyright (c) 2004 by Secret Labs.
+# Copyright (c) 2004 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image, ImageFile
+import string, struct
+
+HEADERSIZE = 8
+
+def nextheader(fobj):
+ return struct.unpack('>4sI', fobj.read(HEADERSIZE))
+
+def read_32t(fobj, (start, length), (width, height)):
+ # The 128x128 icon seems to have an extra header for some reason.
+ fobj.seek(start)
+ sig = fobj.read(4)
+ if sig != '\x00\x00\x00\x00':
+ raise SyntaxError, 'Unknown signature, expecting 0x00000000'
+ return read_32(fobj, (start + 4, length - 4), (width, height))
+
+def read_32(fobj, (start, length), size):
+ """
+ Read a 32bit RGB icon resource. Seems to be either uncompressed or
+ an RLE packbits-like scheme.
+ """
+ fobj.seek(start)
+ sizesq = size[0] * size[1]
+ if length == sizesq * 3:
+ # uncompressed ("RGBRGBGB")
+ indata = fobj.read(length)
+ im = Image.frombuffer("RGB", size, indata, "raw", "RGB", 0, 1)
+ else:
+ # decode image
+ im = Image.new("RGB", size, None)
+ for band_ix in range(3):
+ data = []
+ bytesleft = sizesq
+ while bytesleft > 0:
+ byte = fobj.read(1)
+ if not byte:
+ break
+ byte = ord(byte)
+ if byte & 0x80:
+ blocksize = byte - 125
+ byte = fobj.read(1)
+ for i in range(blocksize):
+ data.append(byte)
+ else:
+ blocksize = byte + 1
+ data.append(fobj.read(blocksize))
+ bytesleft = bytesleft - blocksize
+ if bytesleft <= 0:
+ break
+ if bytesleft != 0:
+ raise SyntaxError(
+ "Error reading channel [%r left]" % bytesleft
+ )
+ band = Image.frombuffer(
+ "L", size, string.join(data, ""), "raw", "L", 0, 1
+ )
+ im.im.putband(band.im, band_ix)
+ return {"RGB": im}
+
+def read_mk(fobj, (start, length), size):
+ # Alpha masks seem to be uncompressed
+ fobj.seek(start)
+ band = Image.frombuffer(
+ "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1
+ )
+ return {"A": band}
+
+class IcnsFile:
+
+ SIZES = {
+ (128, 128): [
+ ('it32', read_32t),
+ ('t8mk', read_mk),
+ ],
+ (48, 48): [
+ ('ih32', read_32),
+ ('h8mk', read_mk),
+ ],
+ (32, 32): [
+ ('il32', read_32),
+ ('l8mk', read_mk),
+ ],
+ (16, 16): [
+ ('is32', read_32),
+ ('s8mk', read_mk),
+ ],
+ }
+
+ def __init__(self, fobj):
+ """
+ fobj is a file-like object as an icns resource
+ """
+ # signature : (start, length)
+ self.dct = dct = {}
+ self.fobj = fobj
+ sig, filesize = nextheader(fobj)
+ if sig != 'icns':
+ raise SyntaxError, 'not an icns file'
+ i = HEADERSIZE
+ while i < filesize:
+ sig, blocksize = nextheader(fobj)
+ i = i + HEADERSIZE
+ blocksize = blocksize - HEADERSIZE
+ dct[sig] = (i, blocksize)
+ fobj.seek(blocksize, 1)
+ i = i + blocksize
+
+ def itersizes(self):
+ sizes = []
+ for size, fmts in self.SIZES.items():
+ for (fmt, reader) in fmts:
+ if self.dct.has_key(fmt):
+ sizes.append(size)
+ break
+ return sizes
+
+ def bestsize(self):
+ sizes = self.itersizes()
+ if not sizes:
+ raise SyntaxError, "No 32bit icon resources found"
+ return max(sizes)
+
+ def dataforsize(self, size):
+ """
+ Get an icon resource as {channel: array}. Note that
+ the arrays are bottom-up like windows bitmaps and will likely
+ need to be flipped or transposed in some way.
+ """
+ dct = {}
+ for code, reader in self.SIZES[size]:
+ desc = self.dct.get(code)
+ if desc is not None:
+ dct.update(reader(self.fobj, desc, size))
+ return dct
+
+ def getimage(self, size=None):
+ if size is None:
+ size = self.bestsize()
+ channels = self.dataforsize(size)
+ im = channels.get("RGB").copy()
+ try:
+ im.putalpha(channels["A"])
+ except KeyError:
+ pass
+ return im
+
+##
+# Image plugin for Mac OS icons.
+
+class IcnsImageFile(ImageFile.ImageFile):
+ """
+ PIL read-only image support for Mac OS .icns files.
+ Chooses the best resolution, but will possibly load
+ a different size image if you mutate the size attribute
+ before calling 'load'.
+
+ The info dictionary has a key 'sizes' that is a list
+ of sizes that the icns file has.
+ """
+
+ format = "ICNS"
+ format_description = "Mac OS icns resource"
+
+ def _open(self):
+ self.icns = IcnsFile(self.fp)
+ self.mode = 'RGBA'
+ self.size = self.icns.bestsize()
+ self.info['sizes'] = self.icns.itersizes()
+ # Just use this to see if it's loaded or not yet.
+ self.tile = ('',)
+
+ def load(self):
+ Image.Image.load(self)
+ if not self.tile:
+ return
+ self.load_prepare()
+ # This is likely NOT the best way to do it, but whatever.
+ im = self.icns.getimage(self.size)
+ self.im = im.im
+ self.mode = im.mode
+ self.size = im.size
+ self.fp = None
+ self.icns = None
+ self.tile = ()
+ self.load_end()
+
+
+Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == 'icns')
+Image.register_extension("ICNS", '.icns')
+
+if __name__ == '__main__':
+ import os, sys
+ im = Image.open(open(sys.argv[1], "rb"))
+ im.save("out.png")
+ os.startfile("out.png")
diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py
new file mode 100644
index 000000000..94846dc0c
--- /dev/null
+++ b/PIL/IcoImagePlugin.py
@@ -0,0 +1,86 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Windows Icon support for PIL
+#
+# Notes:
+# uses BmpImagePlugin.py to read the bitmap data.
+#
+# History:
+# 96-05-27 fl Created
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.1"
+
+import Image, BmpImagePlugin
+
+
+#
+# --------------------------------------------------------------------
+
+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)
+
+
+def _accept(prefix):
+ return prefix[:4] == "\0\0\1\0"
+
+##
+# Image plugin for Windows Icon files.
+
+class IcoImageFile(BmpImagePlugin.BmpImageFile):
+
+ format = "ICO"
+ format_description = "Windows Icon"
+
+ def _open(self):
+
+ # check magic
+ s = self.fp.read(6)
+ if not _accept(s):
+ raise SyntaxError, "not an ICO file"
+
+ # pick the largest icon in the file
+ m = ""
+ for i in range(i16(s[4:])):
+ s = self.fp.read(16)
+ if not m:
+ m = s
+ elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]):
+ m = s
+ #print "width", ord(s[0])
+ #print "height", ord(s[1])
+ #print "colors", ord(s[2])
+ #print "reserved", ord(s[3])
+ #print "planes", i16(s[4:])
+ #print "bitcount", i16(s[6:])
+ #print "bytes", i32(s[8:])
+ #print "offset", i32(s[12:])
+
+ # load as bitmap
+ self._bitmap(i32(m[12:]))
+
+ # patch up the bitmap height
+ self.size = self.size[0], self.size[1]/2
+ d, e, o, a = self.tile[0]
+ self.tile[0] = d, (0,0)+self.size, o, a
+
+ return
+
+
+#
+# --------------------------------------------------------------------
+
+Image.register_open("ICO", IcoImageFile, _accept)
+
+Image.register_extension("ICO", ".ico")
diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py
new file mode 100644
index 000000000..a76dcf04e
--- /dev/null
+++ b/PIL/ImImagePlugin.py
@@ -0,0 +1,336 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# IFUNC IM file handling for PIL
+#
+# history:
+# 1995-09-01 fl Created.
+# 1997-01-03 fl Save palette images
+# 1997-01-08 fl Added sequence support
+# 1997-01-23 fl Added P and RGB save support
+# 1997-05-31 fl Read floating point images
+# 1997-06-22 fl Save floating point images
+# 1997-08-27 fl Read and save 1-bit images
+# 1998-06-25 fl Added support for RGB+LUT images
+# 1998-07-02 fl Added support for YCC images
+# 1998-07-15 fl Renamed offset attribute to avoid name clash
+# 1998-12-29 fl Added I;16 support
+# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7)
+# 2003-09-26 fl Added LA/PA support
+#
+# Copyright (c) 1997-2003 by Secret Labs AB.
+# Copyright (c) 1995-2001 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.7"
+
+import re, string
+import Image, ImageFile, ImagePalette
+
+
+# --------------------------------------------------------------------
+# Standard tags
+
+COMMENT = "Comment"
+DATE = "Date"
+EQUIPMENT = "Digitalization equipment"
+FRAMES = "File size (no of images)"
+LUT = "Lut"
+NAME = "Name"
+SCALE = "Scale (x,y)"
+SIZE = "Image size (x*y)"
+MODE = "Image type"
+
+TAGS = { COMMENT:0, DATE:0, EQUIPMENT:0, FRAMES:0, LUT:0, NAME:0,
+ SCALE:0, SIZE:0, MODE:0 }
+
+OPEN = {
+ # ifunc93/p3cfunc formats
+ "0 1 image": ("1", "1"),
+ "L 1 image": ("1", "1"),
+ "Greyscale image": ("L", "L"),
+ "Grayscale image": ("L", "L"),
+ "RGB image": ("RGB", "RGB;L"),
+ "RLB image": ("RGB", "RLB"),
+ "RYB image": ("RGB", "RLB"),
+ "B1 image": ("1", "1"),
+ "B2 image": ("P", "P;2"),
+ "B4 image": ("P", "P;4"),
+ "X 24 image": ("RGB", "RGB"),
+ "L 32 S image": ("I", "I;32"),
+ "L 32 F image": ("F", "F;32"),
+ # old p3cfunc formats
+ "RGB3 image": ("RGB", "RGB;T"),
+ "RYB3 image": ("RGB", "RYB;T"),
+ # extensions
+ "LA image": ("LA", "LA;L"),
+ "RGBA image": ("RGBA", "RGBA;L"),
+ "RGBX image": ("RGBX", "RGBX;L"),
+ "CMYK image": ("CMYK", "CMYK;L"),
+ "YCC image": ("YCbCr", "YCbCr;L"),
+}
+
+# ifunc95 extensions
+for i in ["8", "8S", "16", "16S", "32", "32F"]:
+ OPEN["L %s image" % i] = ("F", "F;%s" % i)
+ OPEN["L*%s image" % i] = ("F", "F;%s" % i)
+for i in ["16", "16L", "16B"]:
+ OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i)
+ OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i)
+for i in ["32S"]:
+ OPEN["L %s image" % i] = ("I", "I;%s" % i)
+ OPEN["L*%s image" % i] = ("I", "I;%s" % i)
+for i in range(2, 33):
+ OPEN["L*%s image" % i] = ("F", "F;%s" % i)
+
+
+# --------------------------------------------------------------------
+# Read IM directory
+
+split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$")
+
+def number(s):
+ try:
+ return int(s)
+ except ValueError:
+ return float(s)
+
+##
+# Image plugin for the IFUNC IM file format.
+
+class ImImageFile(ImageFile.ImageFile):
+
+ format = "IM"
+ format_description = "IFUNC Image Memory"
+
+ def _open(self):
+
+ # Quick rejection: if there's not an 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"
+ self.fp.seek(0)
+
+ n = 0
+
+ # Default values
+ self.info[MODE] = "L"
+ self.info[SIZE] = (512, 512)
+ self.info[FRAMES] = 1
+
+ self.rawmode = "L"
+
+ while 1:
+
+ s = self.fp.read(1)
+
+ # Some versions of IFUNC uses \n\r instead of \r\n...
+ if s == "\r":
+ continue
+
+ if not s or s[0] == chr(0) or s[0] == chr(26):
+ break
+
+ # FIXME: this may read whole file if not a text file
+ s = s + self.fp.readline()
+
+ if len(s) > 100:
+ raise SyntaxError, "not an IM file"
+
+ if s[-2:] == '\r\n':
+ s = s[:-2]
+ elif s[-1:] == '\n':
+ s = s[:-1]
+
+ try:
+ m = split.match(s)
+ except re.error, v:
+ raise SyntaxError, "not an IM file"
+
+ if m:
+
+ k, v = m.group(1,2)
+
+ # Convert value as appropriate
+ if k in [FRAMES, SCALE, SIZE]:
+ v = string.replace(v, "*", ",")
+ v = tuple(map(number, string.split(v, ",")))
+ if len(v) == 1:
+ v = v[0]
+ elif k == MODE and OPEN.has_key(v):
+ v, self.rawmode = OPEN[v]
+
+ # Add to dictionary. Note that COMMENT tags are
+ # combined into a list of strings.
+ if k == COMMENT:
+ if self.info.has_key(k):
+ self.info[k].append(v)
+ else:
+ self.info[k] = [v]
+ else:
+ self.info[k] = v
+
+ if TAGS.has_key(k):
+ n = n + 1
+
+ else:
+
+ raise SyntaxError, "Syntax error in IM header: " + s
+
+ if not n:
+ raise SyntaxError, "Not an IM file"
+
+ # Basic attributes
+ self.size = self.info[SIZE]
+ self.mode = self.info[MODE]
+
+ # Skip forward to start of image data
+ while s and s[0] != chr(26):
+ s = self.fp.read(1)
+ if not s:
+ raise SyntaxError, "File truncated"
+
+ if self.info.has_key(LUT):
+ # convert lookup table to palette or lut attribute
+ palette = self.fp.read(768)
+ greyscale = 1 # greyscale palette
+ linear = 1 # linear greyscale palette
+ for i in range(256):
+ if palette[i] == palette[i+256] == palette[i+512]:
+ if palette[i] != chr(i):
+ linear = 0
+ else:
+ greyscale = 0
+ if self.mode == "L" or self.mode == "LA":
+ if greyscale:
+ if not linear:
+ self.lut = map(ord, palette[:256])
+ else:
+ if self.mode == "L":
+ self.mode = self.rawmode = "P"
+ elif self.mode == "LA":
+ self.mode = self.rawmode = "PA"
+ self.palette = ImagePalette.raw("RGB;L", palette)
+ elif self.mode == "RGB":
+ if not greyscale or not linear:
+ self.lut = map(ord, palette)
+
+ self.frame = 0
+
+ self.__offset = offs = self.fp.tell()
+
+ self.__fp = self.fp # FIXME: hack
+
+ if self.rawmode[:2] == "F;":
+
+ # ifunc95 formats
+ try:
+ # use bit decoder (if necessary)
+ bits = int(self.rawmode[2:])
+ if bits not in [8, 16, 32]:
+ self.tile = [("bit", (0,0)+self.size, offs,
+ (bits, 8, 3, 0, -1))]
+ return
+ except ValueError:
+ pass
+
+ if self.rawmode in ["RGB;T", "RYB;T"]:
+ # Old LabEye/3PC files. Would be very surprised if anyone
+ # ever stumbled upon such a file ;-)
+ size = self.size[0] * self.size[1]
+ self.tile = [("raw", (0,0)+self.size, offs, ("G", 0, -1)),
+ ("raw", (0,0)+self.size, offs+size, ("R", 0, -1)),
+ ("raw", (0,0)+self.size, offs+2*size, ("B", 0, -1))]
+ else:
+ # LabEye/IFUNC files
+ self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))]
+
+ def seek(self, frame):
+
+ if frame < 0 or frame >= self.info[FRAMES]:
+ raise EOFError, "seek outside sequence"
+
+ if self.frame == frame:
+ return
+
+ self.frame = frame
+
+ if self.mode == "1":
+ bits = 1
+ else:
+ bits = 8 * len(self.mode)
+
+ size = ((self.size[0] * bits + 7) / 8) * self.size[1]
+ offs = self.__offset + frame * size
+
+ self.fp = self.__fp
+
+ self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))]
+
+ def tell(self):
+
+ return self.frame
+
+#
+# --------------------------------------------------------------------
+# Save IM files
+
+SAVE = {
+ # mode: (im type, raw mode)
+ "1": ("0 1", "1"),
+ "L": ("Greyscale", "L"),
+ "LA": ("LA", "LA;L"),
+ "P": ("Greyscale", "P"),
+ "PA": ("LA", "PA;L"),
+ "I": ("L 32S", "I;32S"),
+ "I;16": ("L 16", "I;16"),
+ "I;16L": ("L 16L", "I;16L"),
+ "I;16B": ("L 16B", "I;16B"),
+ "F": ("L 32F", "F;32F"),
+ "RGB": ("RGB", "RGB;L"),
+ "RGBA": ("RGBA", "RGBA;L"),
+ "RGBX": ("RGBX", "RGBX;L"),
+ "CMYK": ("CMYK", "CMYK;L"),
+ "YCbCr": ("YCC", "YCbCr;L")
+}
+
+def _save(im, fp, filename, check=0):
+
+ try:
+ type, rawmode = SAVE[im.mode]
+ except KeyError:
+ raise ValueError, "Cannot save %s images as IM" % im.mode
+
+ try:
+ frames = im.encoderinfo["frames"]
+ except KeyError:
+ frames = 1
+
+ if check:
+ return check
+
+ fp.write("Image type: %s image\r\n" % type)
+ if filename:
+ fp.write("Name: %s\r\n" % filename)
+ fp.write("Image size (x*y): %d*%d\r\n" % im.size)
+ fp.write("File size (no of images): %d\r\n" % frames)
+ if im.mode == "P":
+ fp.write("Lut: 1\r\n")
+ fp.write("\000" * (511-fp.tell()) + "\032")
+ if im.mode == "P":
+ fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))])
+
+#
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open("IM", ImImageFile)
+Image.register_save("IM", _save)
+
+Image.register_extension("IM", ".im")
diff --git a/PIL/Image.py b/PIL/Image.py
new file mode 100644
index 000000000..77a532afa
--- /dev/null
+++ b/PIL/Image.py
@@ -0,0 +1,2127 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# the Image class wrapper
+#
+# partial release history:
+# 1995-09-09 fl Created
+# 1996-03-11 fl PIL release 0.0 (proof of concept)
+# 1996-04-30 fl PIL release 0.1b1
+# 1999-07-28 fl PIL release 1.0 final
+# 2000-06-07 fl PIL release 1.1
+# 2000-10-20 fl PIL release 1.1.1
+# 2001-05-07 fl PIL release 1.1.2
+# 2002-03-15 fl PIL release 1.1.3
+# 2003-05-10 fl PIL release 1.1.4
+# 2005-03-28 fl PIL release 1.1.5
+# 2006-12-02 fl PIL release 1.1.6
+# 2009-11-15 fl PIL release 1.1.7
+#
+# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved.
+# Copyright (c) 1995-2009 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+VERSION = "1.1.7"
+
+try:
+ import warnings
+except ImportError:
+ warnings = None
+
+class _imaging_not_installed:
+ # module placeholder
+ def __getattr__(self, id):
+ raise ImportError("The _imaging C module is not installed")
+
+try:
+ # give Tk a chance to set up the environment, in case we're
+ # using an _imaging module linked against libtcl/libtk (use
+ # __import__ to hide this from naive packagers; we don't really
+ # depend on Tk unless ImageTk is used, and that module already
+ # imports Tkinter)
+ __import__("FixTk")
+except ImportError:
+ pass
+
+try:
+ # If the _imaging C module is not present, you can still use
+ # the "open" function to identify files, but you cannot load
+ # them. Note that other modules should not refer to _imaging
+ # directly; import Image and use the Image.core variable instead.
+ import _imaging
+ core = _imaging
+ del _imaging
+except ImportError, v:
+ core = _imaging_not_installed()
+ if str(v)[:20] == "Module use of python" and warnings:
+ # The _imaging C module is present, but not compiled for
+ # the right version (windows only). Print a warning, if
+ # possible.
+ warnings.warn(
+ "The _imaging extension was built for another version "
+ "of Python; most PIL functions will be disabled",
+ RuntimeWarning
+ )
+
+import ImageMode
+import ImagePalette
+
+import os, string, sys
+
+# type stuff
+from types import IntType, StringType, TupleType
+
+try:
+ UnicodeStringType = type(unicode(""))
+ ##
+ # (Internal) Checks if an object is a string. If the current
+ # Python version supports Unicode, this checks for both 8-bit
+ # and Unicode strings.
+ def isStringType(t):
+ return isinstance(t, StringType) or isinstance(t, UnicodeStringType)
+except NameError:
+ def isStringType(t):
+ return isinstance(t, StringType)
+
+##
+# (Internal) Checks if an object is a tuple.
+
+def isTupleType(t):
+ return isinstance(t, TupleType)
+
+##
+# (Internal) Checks if an object is an image object.
+
+def isImageType(t):
+ return hasattr(t, "im")
+
+##
+# (Internal) Checks if an object is a string, and that it points to a
+# directory.
+
+def isDirectory(f):
+ return isStringType(f) and os.path.isdir(f)
+
+from operator import isNumberType, isSequenceType
+
+#
+# Debug level
+
+DEBUG = 0
+
+#
+# Constants (also defined in _imagingmodule.c!)
+
+NONE = 0
+
+# transpose
+FLIP_LEFT_RIGHT = 0
+FLIP_TOP_BOTTOM = 1
+ROTATE_90 = 2
+ROTATE_180 = 3
+ROTATE_270 = 4
+
+# transforms
+AFFINE = 0
+EXTENT = 1
+PERSPECTIVE = 2
+QUAD = 3
+MESH = 4
+
+# resampling filters
+NONE = 0
+NEAREST = 0
+ANTIALIAS = 1 # 3-lobed lanczos
+LINEAR = BILINEAR = 2
+CUBIC = BICUBIC = 3
+
+# dithers
+NONE = 0
+NEAREST = 0
+ORDERED = 1 # Not yet implemented
+RASTERIZE = 2 # Not yet implemented
+FLOYDSTEINBERG = 3 # default
+
+# palettes/quantizers
+WEB = 0
+ADAPTIVE = 1
+
+# categories
+NORMAL = 0
+SEQUENCE = 1
+CONTAINER = 2
+
+# --------------------------------------------------------------------
+# Registries
+
+ID = []
+OPEN = {}
+MIME = {}
+SAVE = {}
+EXTENSION = {}
+
+# --------------------------------------------------------------------
+# Modes supported by this version
+
+_MODEINFO = {
+ # NOTE: this table will be removed in future versions. use
+ # getmode* functions or ImageMode descriptors instead.
+
+ # official modes
+ "1": ("L", "L", ("1",)),
+ "L": ("L", "L", ("L",)),
+ "I": ("L", "I", ("I",)),
+ "F": ("L", "F", ("F",)),
+ "P": ("RGB", "L", ("P",)),
+ "RGB": ("RGB", "L", ("R", "G", "B")),
+ "RGBX": ("RGB", "L", ("R", "G", "B", "X")),
+ "RGBA": ("RGB", "L", ("R", "G", "B", "A")),
+ "CMYK": ("RGB", "L", ("C", "M", "Y", "K")),
+ "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")),
+
+ # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and
+ # BGR;24. Use these modes only if you know exactly what you're
+ # doing...
+
+}
+
+try:
+ byteorder = sys.byteorder
+except AttributeError:
+ import struct
+ if struct.unpack("h", "\0\1")[0] == 1:
+ byteorder = "big"
+ else:
+ byteorder = "little"
+
+if byteorder == 'little':
+ _ENDIAN = '<'
+else:
+ _ENDIAN = '>'
+
+_MODE_CONV = {
+ # official modes
+ "1": ('|b1', None), # broken
+ "L": ('|u1', None),
+ "I": (_ENDIAN + 'i4', None),
+ "F": (_ENDIAN + 'f4', None),
+ "P": ('|u1', None),
+ "RGB": ('|u1', 3),
+ "RGBX": ('|u1', 4),
+ "RGBA": ('|u1', 4),
+ "CMYK": ('|u1', 4),
+ "YCbCr": ('|u1', 4),
+}
+
+def _conv_type_shape(im):
+ shape = im.size[1], im.size[0]
+ typ, extra = _MODE_CONV[im.mode]
+ if extra is None:
+ return shape, typ
+ else:
+ return shape+(extra,), typ
+
+
+MODES = _MODEINFO.keys()
+MODES.sort()
+
+# raw modes that may be memory mapped. NOTE: if you change this, you
+# may have to modify the stride calculation in map.c too!
+_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B")
+
+##
+# Gets the "base" mode for given mode. This function returns "L" for
+# images that contain grayscale data, and "RGB" for images that
+# contain color data.
+#
+# @param mode Input mode.
+# @return "L" or "RGB".
+# @exception KeyError If the input mode was not a standard mode.
+
+def getmodebase(mode):
+ return ImageMode.getmode(mode).basemode
+
+##
+# Gets the storage type mode. Given a mode, this function returns a
+# single-layer mode suitable for storing individual bands.
+#
+# @param mode Input mode.
+# @return "L", "I", or "F".
+# @exception KeyError If the input mode was not a standard mode.
+
+def getmodetype(mode):
+ return ImageMode.getmode(mode).basetype
+
+##
+# Gets a list of individual band names. Given a mode, this function
+# returns a tuple containing the names of individual bands (use
+# {@link #getmodetype} to get the mode used to store each individual
+# band.
+#
+# @param mode Input mode.
+# @return A tuple containing band names. The length of the tuple
+# gives the number of bands in an image of the given mode.
+# @exception KeyError If the input mode was not a standard mode.
+
+def getmodebandnames(mode):
+ return ImageMode.getmode(mode).bands
+
+##
+# Gets the number of individual bands for this mode.
+#
+# @param mode Input mode.
+# @return The number of bands in this mode.
+# @exception KeyError If the input mode was not a standard mode.
+
+def getmodebands(mode):
+ return len(ImageMode.getmode(mode).bands)
+
+# --------------------------------------------------------------------
+# Helpers
+
+_initialized = 0
+
+##
+# Explicitly loads standard file format drivers.
+
+def preinit():
+ "Load standard file format drivers."
+
+ global _initialized
+ if _initialized >= 1:
+ return
+
+ try:
+ import BmpImagePlugin
+ except ImportError:
+ pass
+ try:
+ import GifImagePlugin
+ except ImportError:
+ pass
+ try:
+ import JpegImagePlugin
+ except ImportError:
+ pass
+ try:
+ import PpmImagePlugin
+ except ImportError:
+ pass
+ try:
+ import PngImagePlugin
+ except ImportError:
+ pass
+# try:
+# import TiffImagePlugin
+# except ImportError:
+# pass
+
+ _initialized = 1
+
+##
+# Explicitly initializes the Python Imaging Library. This function
+# loads all available file format drivers.
+
+def init():
+ "Load all 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
+
+ if OPEN or SAVE:
+ _initialized = 2
+ return 1
+
+# --------------------------------------------------------------------
+# Codec factories (used by tostring/fromstring and ImageFile.load)
+
+def _getdecoder(mode, decoder_name, args, extra=()):
+
+ # tweak arguments
+ if args is None:
+ args = ()
+ elif not isTupleType(args):
+ args = (args,)
+
+ try:
+ # get decoder
+ decoder = getattr(core, decoder_name + "_decoder")
+ # print decoder, (mode,) + args + extra
+ return apply(decoder, (mode,) + args + extra)
+ except AttributeError:
+ raise IOError("decoder %s not available" % decoder_name)
+
+def _getencoder(mode, encoder_name, args, extra=()):
+
+ # tweak arguments
+ if args is None:
+ args = ()
+ elif not isTupleType(args):
+ args = (args,)
+
+ try:
+ # get encoder
+ encoder = getattr(core, encoder_name + "_encoder")
+ # print encoder, (mode,) + args + extra
+ return apply(encoder, (mode,) + args + extra)
+ except AttributeError:
+ raise IOError("encoder %s not available" % encoder_name)
+
+
+# --------------------------------------------------------------------
+# Simple expression analyzer
+
+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 _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)):
+ return c, 0.0
+ if (a is stub and b == "__add__" and isNumberType(c)):
+ 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)):
+ return c, e
+ except TypeError: pass
+ raise ValueError("illegal expression")
+
+
+# --------------------------------------------------------------------
+# 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:
+
+ format = None
+ format_description = None
+
+ def __init__(self):
+ # FIXME: take "new" parameters / other image?
+ # FIXME: turn mode and size into delegating properties?
+ self.im = None
+ self.mode = ""
+ self.size = (0, 0)
+ self.palette = None
+ self.info = {}
+ self.category = NORMAL
+ self.readonly = 0
+
+ def _new(self, im):
+ new = Image()
+ new.im = im
+ new.mode = im.mode
+ new.size = im.size
+ new.palette = self.palette
+ if im.mode == "P":
+ new.palette = ImagePalette.ImagePalette()
+ try:
+ new.info = self.info.copy()
+ except AttributeError:
+ # fallback (pre-1.5.2)
+ new.info = {}
+ for k, v in self.info:
+ new.info[k] = v
+ return new
+
+ _makeself = _new # compatibility
+
+ def _copy(self):
+ self.load()
+ self.im = self.im.copy()
+ self.readonly = 0
+
+ def _dump(self, file=None, format=None):
+ import tempfile
+ if not file:
+ file = tempfile.mktemp()
+ self.load()
+ if not format or format == "PPM":
+ self.im.save_ppm(file)
+ else:
+ file = file + "." + format
+ self.save(file, format)
+ return file
+
+ def __repr__(self):
+ return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
+ self.__class__.__module__, self.__class__.__name__,
+ self.mode, self.size[0], self.size[1],
+ id(self)
+ )
+
+ def __getattr__(self, name):
+ if name == "__array_interface__":
+ # numpy array interface support
+ new = {}
+ shape, typestr = _conv_type_shape(self)
+ new['shape'] = shape
+ new['typestr'] = typestr
+ new['data'] = self.tostring()
+ 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 tostring(self, encoder_name="raw", *args):
+ "Return image as a binary string"
+
+ # may pass tuple instead of argument list
+ if len(args) == 1 and isTupleType(args[0]):
+ args = args[0]
+
+ if encoder_name == "raw" and args == ():
+ args = self.mode
+
+ self.load()
+
+ # unpack data
+ e = _getencoder(self.mode, encoder_name, args)
+ e.setimage(self.im)
+
+ bufsize = max(65536, self.size[0] * 4) # see RawEncode.c
+
+ data = []
+ while 1:
+ l, s, d = e.encode(bufsize)
+ data.append(d)
+ if s:
+ break
+ if s < 0:
+ raise RuntimeError("encoder error %d in tostring" % s)
+
+ return string.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"
+
+ def tobitmap(self, name="image"):
+ "Return image as an XBM bitmap"
+
+ 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, "};"], "")
+
+ ##
+ # 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 fromstring(self, data, decoder_name="raw", *args):
+ "Load data to image from binary string"
+
+ # may pass tuple instead of argument list
+ if len(args) == 1 and isTupleType(args[0]):
+ args = args[0]
+
+ # default format
+ if decoder_name == "raw" and args == ():
+ args = self.mode
+
+ # unpack data
+ d = _getdecoder(self.mode, decoder_name, args)
+ d.setimage(self.im)
+ s = d.decode(data)
+
+ if s[0] >= 0:
+ raise ValueError("not enough image data")
+ 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 load(self):
+ "Explicitly load pixel data."
+ if self.im and self.palette and self.palette.dirty:
+ # realize palette
+ apply(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)
+ 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."
+ 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,
+ palette=WEB, colors=256):
+ "Convert to other pixel format"
+
+ if not mode:
+ # determine default mode
+ if self.mode == "P":
+ self.load()
+ if self.palette:
+ mode = self.palette.mode
+ else:
+ mode = "RGB"
+ else:
+ return self.copy()
+
+ self.load()
+
+ if data:
+ # matrix conversion
+ if mode not in ("L", "RGB"):
+ raise ValueError("illegal conversion")
+ im = self.im.convert_matrix(mode, data)
+ return self._new(im)
+
+ if mode == "P" and palette == ADAPTIVE:
+ im = self.im.quantize(colors)
+ return self._new(im)
+
+ # colourspace conversion
+ if dither is None:
+ dither = FLOYDSTEINBERG
+
+ try:
+ im = self.im.convert(mode, dither)
+ except ValueError:
+ try:
+ # normalize source image and try again
+ im = self.im.convert(getmodebase(self.mode))
+ im = im.convert(mode, dither)
+ except KeyError:
+ raise ValueError("illegal conversion")
+
+ return self._new(im)
+
+ def quantize(self, colors=256, method=0, kmeans=0, palette=None):
+
+ # methods:
+ # 0 = median cut
+ # 1 = maximum coverage
+
+ # NOTE: this functionality will be moved to the extended
+ # quantizer interface in a later version of PIL.
+
+ self.load()
+
+ if palette:
+ # use palette from reference image
+ palette.load()
+ if palette.mode != "P":
+ raise ValueError("bad mode for palette image")
+ if self.mode != "RGB" and self.mode != "L":
+ raise ValueError(
+ "only RGB or L mode images can be quantized to a palette"
+ )
+ im = self.im.convert("P", 1, palette.im)
+ return self._makeself(im)
+
+ 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"
+
+ 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"
+
+ self.load()
+ if box is None:
+ return self.copy()
+
+ # 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"
+
+ pass
+
+ def _expand(self, xmargin, ymargin=None):
+ if ymargin is None:
+ ymargin = xmargin
+ 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"
+
+ self.load()
+
+ if callable(filter):
+ filter = filter()
+ if not hasattr(filter, "filter"):
+ raise TypeError("filter argument should be ImageFilter.Filter instance or class")
+
+ if self.im.bands == 1:
+ return self._new(filter.filter(self.im))
+ # fix to handle multiband images since _imaging doesn't
+ ims = []
+ for c in range(self.im.bands):
+ 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"
+
+ 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"
+
+ 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"
+
+ self.load()
+ if self.mode in ("1", "L", "P"):
+ h = self.im.histogram()
+ out = []
+ for i in range(256):
+ if h[i]:
+ out.append((h[i], i))
+ if len(out) > maxcolors:
+ return None
+ 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."
+
+ 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"
+
+ self.load()
+ if self.im.bands > 1:
+ extrema = []
+ for i in range(self.im.bands):
+ extrema.append(self.im.getband(i).getextrema())
+ 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"
+
+ 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."
+
+ self.load()
+ try:
+ return map(ord, 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"
+
+ 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"
+
+ 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.
+
+ def histogram(self, mask=None, extrema=None):
+ "Take histogram of image"
+
+ self.load()
+ if mask:
+ mask.load()
+ return self.im.histogram((0, 0), mask.im)
+ if self.mode in ("I", "F"):
+ if extrema is None:
+ extrema = self.getextrema()
+ 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"
+ if warnings:
+ warnings.warn(
+ "'offset' is deprecated; use 'ImageChops.offset' instead",
+ DeprecationWarning, stacklevel=2
+ )
+ 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"
+
+ if isImageType(box) and mask is None:
+ # abbreviated paste(im, mask) syntax
+ mask = box; box = None
+
+ if box is None:
+ # cover all of self
+ box = (0, 0) + self.size
+
+ if len(box) == 2:
+ # lower left corner given; get size from image or mask
+ if isImageType(im):
+ size = im.size
+ elif isImageType(mask):
+ size = mask.size
+ else:
+ # FIXME: use self.size here?
+ raise ValueError(
+ "cannot determine region size; use 4-item box"
+ )
+ box = box + (box[0]+size[0], box[1]+size[1])
+
+ if isStringType(im):
+ import ImageColor
+ im = ImageColor.getcolor(im, self.mode)
+
+ elif isImageType(im):
+ im.load()
+ if self.mode != im.mode:
+ if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"):
+ # should use an adapter for this!
+ im = im.convert(self.mode)
+ im = im.im
+
+ self.load()
+ if self.readonly:
+ self._copy()
+
+ if mask:
+ mask.load()
+ self.im.paste(im, box, mask.im)
+ 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"
+
+ self.load()
+
+ if isinstance(lut, ImagePointHandler):
+ return lut.point(self)
+
+ if not isSequenceType(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
+ 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
+
+ if self.mode == "F":
+ # FIXME: _imaging returns a confusing error message for this case
+ raise ValueError("point operation not supported for this mode")
+
+ 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"
+
+ self.load()
+ if self.readonly:
+ self._copy()
+
+ if self.mode not in ("LA", "RGBA"):
+ # attempt to promote self to a matching alpha mode
+ try:
+ mode = getmodebase(self.mode) + "A"
+ try:
+ self.im.setmode(mode)
+ except (AttributeError, ValueError):
+ # do things the hard way
+ im = self.im.convert(mode)
+ if im.mode not in ("LA", "RGBA"):
+ raise ValueError # sanity check
+ self.im = im
+ self.mode = self.im.mode
+ except (KeyError, ValueError):
+ raise ValueError("illegal image mode")
+
+ if self.mode == "LA":
+ band = 1
+ else:
+ band = 3
+
+ if isImageType(alpha):
+ # alpha layer
+ if alpha.mode not in ("1", "L"):
+ raise ValueError("illegal image mode")
+ alpha.load()
+ if alpha.mode == "1":
+ alpha = alpha.convert("L")
+ else:
+ # constant alpha
+ try:
+ self.im.fillband(band, alpha)
+ except (AttributeError, ValueError):
+ # do things the hard way
+ alpha = new("L", self.size, alpha)
+ else:
+ return
+
+ 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."
+
+ self.load()
+ if self.readonly:
+ self._copy()
+
+ 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."
+
+ if self.mode not in ("L", "P"):
+ raise ValueError("illegal image mode")
+ self.load()
+ if isinstance(data, ImagePalette.ImagePalette):
+ palette = ImagePalette.raw(data.rawmode, data.palette)
+ else:
+ if not isStringType(data):
+ data = string.join(map(chr, 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"
+
+ self.load()
+ if self.readonly:
+ self._copy()
+
+ 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"
+
+ if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS):
+ raise ValueError("unknown resampling filter")
+
+ self.load()
+
+ if self.mode in ("1", "P"):
+ resample = NEAREST
+
+ if resample == ANTIALIAS:
+ # requires stretch support (imToolkit & PIL 1.1.3)
+ try:
+ im = self.im.stretch(size, resample)
+ except AttributeError:
+ raise ValueError("unsupported resampling filter")
+ else:
+ im = self.im.resize(size, resample)
+
+ 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."
+
+ if expand:
+ import math
+ angle = -angle * math.pi / 180
+ matrix = [
+ 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):
+ return a*x + b*y + c, d*x + e*y + f
+
+ # calculate output size
+ w, h = self.size
+ xx = []
+ yy = []
+ for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
+ x, y = transform(x, y)
+ xx.append(x)
+ yy.append(y)
+ w = int(math.ceil(max(xx)) - math.floor(min(xx)))
+ h = int(math.ceil(max(yy)) - math.floor(min(yy)))
+
+ # adjust center
+ x, y = transform(w / 2.0, h / 2.0)
+ matrix[2] = self.size[0] / 2.0 - x
+ matrix[5] = self.size[1] / 2.0 - y
+
+ return self.transform((w, h), AFFINE, matrix, resample)
+
+ if resample not in (NEAREST, BILINEAR, BICUBIC):
+ raise ValueError("unknown resampling filter")
+
+ self.load()
+
+ if self.mode in ("1", "P"):
+ resample = NEAREST
+
+ 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"
+
+ if isStringType(fp):
+ filename = fp
+ else:
+ if hasattr(fp, "name") and isStringType(fp.name):
+ filename = fp.name
+ else:
+ filename = ""
+
+ # may mutate self!
+ self.load()
+
+ self.encoderinfo = params
+ self.encoderconfig = ()
+
+ preinit()
+
+ ext = string.lower(os.path.splitext(filename)[1])
+
+ if not format:
+ try:
+ format = EXTENSION[ext]
+ except KeyError:
+ init()
+ try:
+ format = EXTENSION[ext]
+ except KeyError:
+ raise KeyError(ext) # unknown extension
+
+ try:
+ save_handler = SAVE[string.upper(format)]
+ except KeyError:
+ init()
+ save_handler = SAVE[string.upper(format)] # unknown format
+
+ if isStringType(fp):
+ import __builtin__
+ fp = __builtin__.open(fp, "wb")
+ close = 1
+ else:
+ close = 0
+
+ try:
+ save_handler(self, fp, filename)
+ finally:
+ # do what we can to clean up
+ 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"
+
+ # 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)"
+
+ _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"
+
+ 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"
+
+ 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)"
+
+ # 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]
+ size = x, y
+
+ if size == self.size:
+ return
+
+ self.draft(None, size)
+
+ self.load()
+
+ try:
+ im = self.resize(size, resample)
+ except ValueError:
+ if resample != ANTIALIAS:
+ raise
+ im = self.resize(size, NEAREST) # fallback
+
+ self.im = im.im
+ self.mode = im.mode
+ self.size = size
+
+ self.readonly = 0
+
+ # 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"
+
+ if isinstance(method, ImageTransformHandler):
+ return method.transform(size, self, resample=resample, fill=fill)
+ if hasattr(method, "getdata"):
+ # compatibility w. old-style transform objects
+ 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
+ for box, quad in data:
+ im.__transformer(box, self, QUAD, quad, resample, fill)
+ else:
+ im.__transformer((0, 0)+size, self, method, data, resample, fill)
+
+ return im
+
+ def __transformer(self, box, image, method, data,
+ resample=NEAREST, fill=1):
+
+ # FIXME: this should be turned into a lazy operation (?)
+
+ w = box[2]-box[0]
+ h = box[3]-box[1]
+
+ if method == AFFINE:
+ # change argument order to match implementation
+ data = (data[2], data[0], data[1],
+ data[5], data[3], data[4])
+ elif method == EXTENT:
+ # convert extent to an affine transform
+ x0, y0, x1, y1 = data
+ xs = float(x1 - x0) / w
+ ys = float(y1 - y0) / h
+ method = AFFINE
+ data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys)
+ elif method == PERSPECTIVE:
+ # change argument order to match implementation
+ data = (data[2], data[0], data[1],
+ data[5], data[3], data[4],
+ data[6], data[7])
+ elif method == QUAD:
+ # quadrilateral warp. data specifies the four corners
+ # given as NW, SW, SE, and NE.
+ nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8]
+ x0, y0 = nw; As = 1.0 / w; At = 1.0 / h
+ data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At,
+ (se[0]-sw[0]-ne[0]+x0)*As*At,
+ y0, (ne[1]-y0)*As, (sw[1]-y0)*At,
+ (se[1]-sw[1]-ne[1]+y0)*As*At)
+ else:
+ raise ValueError("unknown transformation method")
+
+ if resample not in (NEAREST, BILINEAR, BICUBIC):
+ raise ValueError("unknown resampling filter")
+
+ image.load()
+
+ self.load()
+
+ if image.mode in ("1", "P"):
+ resample = NEAREST
+
+ 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)"
+
+ self.load()
+ im = self.im.transpose(method)
+ return self._new(im)
+
+# --------------------------------------------------------------------
+# Lazy operations
+
+class _ImageCrop(Image):
+
+ def __init__(self, im, box):
+
+ Image.__init__(self)
+
+ x0, y0, x1, y1 = box
+ if x1 < x0:
+ x1 = x0
+ if y1 < y0:
+ y1 = y0
+
+ self.mode = im.mode
+ self.size = x1-x0, y1-y0
+
+ self.__crop = x0, y0, x1, y1
+
+ self.im = im.im
+
+ def load(self):
+
+ # lazy evaluation!
+ if self.__crop:
+ self.im = self.im.crop(self.__crop)
+ self.__crop = None
+
+ if self.im:
+ return self.im.pixel_access(self.readonly)
+
+ # FIXME: future versions should optimize crop/paste
+ # sequences!
+
+# --------------------------------------------------------------------
+# Abstract handlers.
+
+class ImagePointHandler:
+ # used as a mixin by point transforms (for use with im.point)
+ pass
+
+class ImageTransformHandler:
+ # used as a mixin by geometry transforms (for use with im.transform)
+ pass
+
+# --------------------------------------------------------------------
+# Factories
+
+#
+# Debugging
+
+def _wedge():
+ "Create greyscale wedge (for debugging only)"
+
+ 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"
+
+ if color is None:
+ # don't initialize
+ return Image()._new(core.new(mode, size))
+
+ if isStringType(color):
+ # css3-style specifier
+
+ 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"
+
+ # may pass tuple instead of argument list
+ if len(args) == 1 and isTupleType(args[0]):
+ args = args[0]
+
+ if decoder_name == "raw" and args == ():
+ args = mode
+
+ im = new(mode, size)
+ im.fromstring(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 frombuffer(mode, size, data, decoder_name="raw", *args):
+ "Load image from string or buffer"
+
+ # may pass tuple instead of argument list
+ if len(args) == 1 and isTupleType(args[0]):
+ args = args[0]
+
+ if decoder_name == "raw":
+ if args == ():
+ if warnings:
+ warnings.warn(
+ "the frombuffer defaults may change in a future release; "
+ "for portability, change the call to read:\n"
+ " frombuffer(mode, size, data, 'raw', mode, 0, 1)",
+ RuntimeWarning, stacklevel=2
+ )
+ args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6
+ if args[0] in _MAPMODES:
+ im = new(mode, (1,1))
+ im = im._new(
+ core.map_buffer(data, size, decoder_name, None, 0, args)
+ )
+ im.readonly = 1
+ return im
+
+ return fromstring(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):
+ arr = obj.__array_interface__
+ shape = arr['shape']
+ ndim = len(shape)
+ try:
+ strides = arr['strides']
+ except KeyError:
+ strides = None
+ if mode is None:
+ try:
+ typekey = (1, 1) + shape[2:], arr['typestr']
+ mode, rawmode = _fromarray_typemap[typekey]
+ except KeyError:
+ # print typekey
+ raise TypeError("Cannot handle this data type")
+ else:
+ rawmode = mode
+ if mode in ["1", "L", "I", "P", "F"]:
+ ndmax = 2
+ elif mode == "RGB":
+ ndmax = 3
+ else:
+ ndmax = 4
+ if ndim > ndmax:
+ raise ValueError("Too many dimensions.")
+
+ size = shape[1], shape[0]
+ if strides is not None:
+ obj = obj.tostring()
+
+ return frombuffer(mode, size, obj, "raw", rawmode, 0, 1)
+
+_fromarray_typemap = {
+ # (shape, typestr) => mode, rawmode
+ # first two members of shape are set to one
+ # ((1, 1), "|b1"): ("1", "1"), # broken
+ ((1, 1), "|u1"): ("L", "L"),
+ ((1, 1), "|i1"): ("I", "I;8"),
+ ((1, 1), "i2"): ("I", "I;16B"),
+ ((1, 1), "i4"): ("I", "I;32B"),
+ ((1, 1), "f4"): ("F", "F;32BF"),
+ ((1, 1), "f8"): ("F", "F;64BF"),
+ ((1, 1, 3), "|u1"): ("RGB", "RGB"),
+ ((1, 1, 4), "|u1"): ("RGBA", "RGBA"),
+ }
+
+# shortcuts
+_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"
+
+ if mode != "r":
+ raise ValueError("bad mode")
+
+ if isStringType(fp):
+ import __builtin__
+ filename = fp
+ fp = __builtin__.open(fp, "rb")
+ else:
+ filename = ""
+
+ prefix = fp.read(16)
+
+ preinit()
+
+ for i in ID:
+ try:
+ factory, accept = OPEN[i]
+ if not accept or accept(prefix):
+ fp.seek(0)
+ return factory(fp, filename)
+ except (SyntaxError, IndexError, TypeError):
+ pass
+
+ if init():
+
+ for i in ID:
+ try:
+ factory, accept = OPEN[i]
+ if not accept or accept(prefix):
+ fp.seek(0)
+ return factory(fp, filename)
+ except (SyntaxError, IndexError, TypeError):
+ pass
+
+ raise IOError("cannot identify image file")
+
+#
+# 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 blend(im1, im2, alpha):
+ "Interpolate between images."
+
+ 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"
+
+ 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"
+
+ 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."
+
+ if getmodebands(mode) != len(bands) or "*" in mode:
+ raise ValueError("wrong number of bands")
+ for im in bands[1:]:
+ if im.mode != getmodetype(mode):
+ raise ValueError("mode mismatch")
+ if im.size != bands[0].size:
+ raise ValueError("size mismatch")
+ im = core.new(mode, bands[0].size)
+ for i in range(getmodebands(mode)):
+ bands[i].load()
+ im.putband(bands[i].im, i)
+ return bands[0]._new(im)
+
+# --------------------------------------------------------------------
+# 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)
+ 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 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 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)
+
+
+# --------------------------------------------------------------------
+# Simple display support. User code may override this.
+
+def _show(image, **options):
+ # override me, as necessary
+ apply(_showxv, (image,), options)
+
+def _showxv(image, title=None, **options):
+ import ImageShow
+ apply(ImageShow.show, (image, title), options)
diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py
new file mode 100644
index 000000000..82861fc7a
--- /dev/null
+++ b/PIL/ImageChops.py
@@ -0,0 +1,302 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# standard channel operations
+#
+# History:
+# 1996-03-24 fl Created
+# 1996-08-13 fl Added logical operations (for "1" images)
+# 2000-10-12 fl Added offset method (from Image.py)
+#
+# Copyright (c) 1997-2000 by Secret Labs AB
+# Copyright (c) 1996-2000 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+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"
+
+ 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"
+
+ return image.copy()
+
+##
+# Inverts an image
+# (MAX - image).
+#
+# @param image Source image.
+# @return An image object.
+
+def invert(image):
+ "Invert a channel"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+
+ 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"
+ if yoffset is None:
+ yoffset = xoffset
+ image.load()
+ return image._new(image.im.offset(xoffset, yoffset))
diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py
new file mode 100644
index 000000000..b8a6dca71
--- /dev/null
+++ b/PIL/ImageCms.py
@@ -0,0 +1,786 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# optional color managment support, based on Kevin Cazabon's PyCMS
+# library.
+#
+# History:
+# 2009-03-08 fl Added to PIL.
+#
+# Copyright (C) 2002-2003 Kevin Cazabon
+# Copyright (c) 2009 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution. See
+# below for the original description.
+#
+
+DESCRIPTION = """
+pyCMS
+
+ a Python / PIL interface to the littleCMS ICC Color Management System
+ Copyright (C) 2002-2003 Kevin Cazabon
+ kevin@cazabon.com
+ http://www.cazabon.com
+
+ 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
+
+ The pyCMS.py module provides a "clean" interface between Python/PIL and
+ pyCMSdll, taking care of some of the more complex handling of the direct
+ pyCMSdll functions, as well as error-checking and making sure that all
+ relevant data is kept together.
+
+ While it is possible to call pyCMSdll functions directly, it's not highly
+ recommended.
+
+ Version History:
+
+ 0.1.0 pil mod March 10, 2009
+
+ Renamed display profile to proof profile. The proof
+ profile is the profile of the device that is being
+ simulated, not the profile of the device which is
+ actually used to display/print the final simulation
+ (that'd be the output profile) - also see LCMSAPI.txt
+ input colorspace -> using 'renderingIntent' -> proof
+ colorspace -> using 'proofRenderingIntent' -> output
+ colorspace
+
+ Added LCMS FLAGS support.
+ Added FLAGS["SOFTPROOFING"] as default flag for
+ buildProofTransform (otherwise the proof profile/intent
+ would be ignored).
+
+ 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms
+
+ 0.0.2 alpha Jan 6, 2002
+
+ Added try/except statements arount type() checks of
+ potential CObjects... Python won't let you use type()
+ on them, and raises a TypeError (stupid, if you ask me!)
+
+ Added buildProofTransformFromOpenProfiles() function.
+ Additional fixes in DLL, see DLL code for details.
+
+ 0.0.1 alpha first public release, Dec. 26, 2002
+
+ Known to-do list with current version (of Python interface, not pyCMSdll):
+
+ none
+
+"""
+
+VERSION = "0.1.0 pil"
+
+# --------------------------------------------------------------------.
+
+import Image
+import _imagingcms
+
+core = _imagingcms
+
+#
+# intent/direction values
+
+INTENT_PERCEPTUAL = 0
+INTENT_RELATIVE_COLORIMETRIC = 1
+INTENT_SATURATION = 2
+INTENT_ABSOLUTE_COLORIMETRIC = 3
+
+DIRECTION_INPUT = 0
+DIRECTION_OUTPUT = 1
+DIRECTION_PROOF = 2
+
+#
+# flags
+
+FLAGS = {
+ "MATRIXINPUT": 1,
+ "MATRIXOUTPUT": 2,
+ "MATRIXONLY": (1|2),
+ "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
+ "NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use)
+ "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
+ "NOTCACHE": 64, # Inhibit 1-pixel cache
+ "NOTPRECALC": 256,
+ "NULLTRANSFORM": 512, # Don't transform anyway
+ "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy
+ "LOWRESPRECALC": 2048, # Use less memory to minimize resouces
+ "WHITEBLACKCOMPENSATION": 8192,
+ "BLACKPOINTCOMPENSATION": 8192,
+ "GAMUTCHECK": 4096, # Out of Gamut alarm
+ "SOFTPROOFING": 16384, # Do softproofing
+ "PRESERVEBLACK": 32768, # Black preservation
+ "NODEFAULTRESOURCEDEF": 16777216, # CRD special
+ "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints
+}
+
+_MAX_FLAG = 0
+for flag in FLAGS.values():
+ if isinstance(flag, type(0)):
+ _MAX_FLAG = _MAX_FLAG | flag
+
+# --------------------------------------------------------------------.
+# Experimental PIL-level API
+# --------------------------------------------------------------------.
+
+##
+# Profile.
+
+class ImageCmsProfile:
+
+ def __init__(self, profile):
+ # accepts a string (filename), a file-like object, or a low-level
+ # profile object
+ if Image.isStringType(profile):
+ self._set(core.profile_open(profile), profile)
+ elif hasattr(profile, "read"):
+ self._set(core.profile_fromstring(profile.read()))
+ else:
+ self._set(profile) # assume it's already a profile
+
+ def _set(self, profile, filename=None):
+ self.profile = profile
+ self.filename = filename
+ if profile:
+ self.product_name = profile.product_name
+ self.product_info = profile.product_info
+ else:
+ self.product_name = None
+ self.product_info = None
+
+##
+# Transform. This can be used with the procedural API, or with the
+# standard {@link Image.point} method.
+
+class ImageCmsTransform(Image.ImagePointHandler):
+
+ def __init__(self, input, output, input_mode, output_mode,
+ intent=INTENT_PERCEPTUAL,
+ proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0):
+ if proof is None:
+ self.transform = core.buildTransform(
+ input.profile, output.profile,
+ input_mode, output_mode,
+ intent,
+ flags
+ )
+ else:
+ self.transform = core.buildProofTransform(
+ input.profile, output.profile, proof.profile,
+ input_mode, output_mode,
+ intent, proof_intent,
+ flags
+ )
+ # Note: inputMode and outputMode are for pyCMS compatibility only
+ self.input_mode = self.inputMode = input_mode
+ self.output_mode = self.outputMode = output_mode
+
+ def point(self, im):
+ return self.apply(im)
+
+ def apply(self, im, imOut=None):
+ im.load()
+ if imOut is None:
+ imOut = Image.new(self.output_mode, im.size, None)
+ result = self.transform.apply(im.im.id, imOut.im.id)
+ return imOut
+
+ def apply_in_place(self, im):
+ im.load()
+ if im.mode != self.output_mode:
+ raise ValueError("mode mismatch") # wrong output mode
+ result = self.transform.apply(im.im.id, im.im.id)
+ return im
+
+##
+# (experimental) Fetches the profile for the current display device.
+# Returns None if the profile is not known.
+
+def get_display_profile(handle=None):
+ import sys
+ if sys.platform == "win32":
+ import ImageWin
+ if isinstance(handle, ImageWin.HDC):
+ profile = core.get_display_profile_win32(handle, 1)
+ else:
+ profile = core.get_display_profile_win32(handle or 0)
+ else:
+ try:
+ get = _imagingcms.get_display_profile
+ except AttributeError:
+ return None
+ else:
+ profile = get()
+ return ImageCmsProfile(profile)
+
+# --------------------------------------------------------------------.
+# pyCMS compatible layer
+# --------------------------------------------------------------------.
+
+##
+# (pyCMS) Exception class. This is used for all errors in the pyCMS API.
+
+class PyCMSError(Exception):
+ pass
+
+##
+# (pyCMS) Applies an ICC transformation to a given image, mapping from
+# inputProfile to outputProfile.
+
+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):
+ raise PyCMSError("renderingIntent must be an integer between 0 and 3")
+
+ if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
+
+ try:
+ if not isinstance(inputProfile, ImageCmsProfile):
+ inputProfile = ImageCmsProfile(inputProfile)
+ if not isinstance(outputProfile, ImageCmsProfile):
+ outputProfile = ImageCmsProfile(outputProfile)
+ transform = ImageCmsTransform(
+ inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags
+ )
+ if inPlace:
+ transform.apply_in_place(im)
+ imOut = None
+ else:
+ imOut = transform.apply(im)
+ except (IOError, TypeError, ValueError), v:
+ raise PyCMSError(v)
+
+ return imOut
+
+##
+# (pyCMS) Opens an ICC profile file.
+
+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:
+ 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.
+
+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):
+ raise PyCMSError("renderingIntent must be an integer between 0 and 3")
+
+ if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
+
+ try:
+ if not isinstance(inputProfile, ImageCmsProfile):
+ inputProfile = ImageCmsProfile(inputProfile)
+ if not isinstance(outputProfile, ImageCmsProfile):
+ outputProfile = ImageCmsProfile(outputProfile)
+ return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags)
+ except (IOError, TypeError, ValueError), 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.
+
+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):
+ raise PyCMSError("renderingIntent must be an integer between 0 and 3")
+
+ if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG):
+ raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG)
+
+ try:
+ if not isinstance(inputProfile, ImageCmsProfile):
+ inputProfile = ImageCmsProfile(inputProfile)
+ if not isinstance(outputProfile, ImageCmsProfile):
+ outputProfile = ImageCmsProfile(outputProfile)
+ if not isinstance(proofProfile, ImageCmsProfile):
+ proofProfile = ImageCmsProfile(proofProfile)
+ return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags)
+ except (IOError, TypeError, ValueError), v:
+ raise PyCMSError(v)
+
+buildTransformFromOpenProfiles = buildTransform
+buildProofTransformFromOpenProfiles = buildProofTransform
+
+##
+# (pyCMS) Applies a transform to a given image.
+
+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:
+ raise PyCMSError(v)
+
+ return imOut
+
+##
+# (pyCMS) Creates a profile.
+
+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:
+ return core.createProfile(colorSpace, colorTemp)
+ except (TypeError, ValueError), v:
+ raise PyCMSError(v)
+
+##
+# (pyCMS) Gets the internal product name for the given profile.
+
+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:
+ raise PyCMSError(v)
+
+##
+# (pyCMS) Gets the internal product information for the given profile.
+
+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:
+ raise PyCMSError(v)
+
+##
+# (pyCMS) Gets the default intent name for the given profile.
+
+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:
+ raise PyCMSError(v)
+
+##
+# (pyCMS) Checks if a given intent is supported.
+
+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)
+ # FIXME: I get different results for the same data w. different
+ # compilers. Bug in LittleCMS or in the binding?
+ if profile.profile.is_intent_supported(intent, direction):
+ return 1
+ else:
+ return -1
+ except (AttributeError, IOError, TypeError, ValueError), v:
+ raise PyCMSError(v)
+
+##
+# (pyCMS) Fetches versions.
+
+def versions():
+ import sys
+ return (
+ VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION
+ )
+
+# --------------------------------------------------------------------
+
+if __name__ == "__main__":
+ # create a cheap manual from the __doc__ strings for the functions above
+
+ import ImageCms
+ import string
+ print __doc__
+
+ for f in dir(pyCMS):
+ print "="*80
+ print "%s" %f
+
+ try:
+ exec ("doc = ImageCms.%s.__doc__" %(f))
+ if string.find(doc, "pyCMS") >= 0:
+ # so we don't get the __doc__ string for imported modules
+ print doc
+ except AttributeError:
+ pass
diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py
new file mode 100644
index 000000000..c3cca46db
--- /dev/null
+++ b/PIL/ImageColor.py
@@ -0,0 +1,263 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# map CSS3-style colour description strings to RGB
+#
+# History:
+# 2002-10-24 fl Added support for CSS-style color strings
+# 2002-12-15 fl Added RGBA support
+# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2
+# 2004-07-19 fl Fixed gray/grey spelling issues
+# 2009-03-05 fl Fixed rounding error in grayscale calculation
+#
+# Copyright (c) 2002-2004 by Secret Labs AB
+# Copyright (c) 2002-2004 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import re, string
+
+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.
+#
+# @param color A CSS3-style colour string.
+# @return An RGB-tuple.
+# @exception ValueError If the color string could not be interpreted
+# as an RGB value.
+
+def getrgb(color):
+ # FIXME: add RGBA support
+ try:
+ rgb = colormap[color]
+ except KeyError:
+ try:
+ # fall back on case-insensitive lookup
+ rgb = colormap[string.lower(color)]
+ except KeyError:
+ rgb = None
+ # found color in cache
+ if rgb:
+ if isinstance(rgb, type(())):
+ return rgb
+ colormap[color] = rgb = getrgb(rgb)
+ return rgb
+ # check for known string formats
+ 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)
+ )
+ 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)
+ )
+ 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))
+ )
+ 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)
+ )
+ m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
+ if m:
+ from colorsys import hls_to_rgb
+ rgb = hls_to_rgb(
+ float(m.group(1)) / 360.0,
+ float(m.group(3)) / 100.0,
+ float(m.group(2)) / 100.0,
+ )
+ return (
+ int(rgb[0] * 255 + 0.5),
+ int(rgb[1] * 255 + 0.5),
+ int(rgb[2] * 255 + 0.5)
+ )
+ raise ValueError("unknown color specifier: %r" % color)
+
+def getcolor(color, mode):
+ # 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 Image.getmodebase(mode) == "L":
+ r, g, b = color
+ return (r*299 + g*587 + b*114)/1000
+ return color
+
+colormap = {
+ # X11 colour table (from "CSS3 module: Color working draft"), with
+ # gray/grey spelling issues fixed. This is a superset of HTML 4.0
+ # colour names used in CSS 1.
+ "aliceblue": "#f0f8ff",
+ "antiquewhite": "#faebd7",
+ "aqua": "#00ffff",
+ "aquamarine": "#7fffd4",
+ "azure": "#f0ffff",
+ "beige": "#f5f5dc",
+ "bisque": "#ffe4c4",
+ "black": "#000000",
+ "blanchedalmond": "#ffebcd",
+ "blue": "#0000ff",
+ "blueviolet": "#8a2be2",
+ "brown": "#a52a2a",
+ "burlywood": "#deb887",
+ "cadetblue": "#5f9ea0",
+ "chartreuse": "#7fff00",
+ "chocolate": "#d2691e",
+ "coral": "#ff7f50",
+ "cornflowerblue": "#6495ed",
+ "cornsilk": "#fff8dc",
+ "crimson": "#dc143c",
+ "cyan": "#00ffff",
+ "darkblue": "#00008b",
+ "darkcyan": "#008b8b",
+ "darkgoldenrod": "#b8860b",
+ "darkgray": "#a9a9a9",
+ "darkgrey": "#a9a9a9",
+ "darkgreen": "#006400",
+ "darkkhaki": "#bdb76b",
+ "darkmagenta": "#8b008b",
+ "darkolivegreen": "#556b2f",
+ "darkorange": "#ff8c00",
+ "darkorchid": "#9932cc",
+ "darkred": "#8b0000",
+ "darksalmon": "#e9967a",
+ "darkseagreen": "#8fbc8f",
+ "darkslateblue": "#483d8b",
+ "darkslategray": "#2f4f4f",
+ "darkslategrey": "#2f4f4f",
+ "darkturquoise": "#00ced1",
+ "darkviolet": "#9400d3",
+ "deeppink": "#ff1493",
+ "deepskyblue": "#00bfff",
+ "dimgray": "#696969",
+ "dimgrey": "#696969",
+ "dodgerblue": "#1e90ff",
+ "firebrick": "#b22222",
+ "floralwhite": "#fffaf0",
+ "forestgreen": "#228b22",
+ "fuchsia": "#ff00ff",
+ "gainsboro": "#dcdcdc",
+ "ghostwhite": "#f8f8ff",
+ "gold": "#ffd700",
+ "goldenrod": "#daa520",
+ "gray": "#808080",
+ "grey": "#808080",
+ "green": "#008000",
+ "greenyellow": "#adff2f",
+ "honeydew": "#f0fff0",
+ "hotpink": "#ff69b4",
+ "indianred": "#cd5c5c",
+ "indigo": "#4b0082",
+ "ivory": "#fffff0",
+ "khaki": "#f0e68c",
+ "lavender": "#e6e6fa",
+ "lavenderblush": "#fff0f5",
+ "lawngreen": "#7cfc00",
+ "lemonchiffon": "#fffacd",
+ "lightblue": "#add8e6",
+ "lightcoral": "#f08080",
+ "lightcyan": "#e0ffff",
+ "lightgoldenrodyellow": "#fafad2",
+ "lightgreen": "#90ee90",
+ "lightgray": "#d3d3d3",
+ "lightgrey": "#d3d3d3",
+ "lightpink": "#ffb6c1",
+ "lightsalmon": "#ffa07a",
+ "lightseagreen": "#20b2aa",
+ "lightskyblue": "#87cefa",
+ "lightslategray": "#778899",
+ "lightslategrey": "#778899",
+ "lightsteelblue": "#b0c4de",
+ "lightyellow": "#ffffe0",
+ "lime": "#00ff00",
+ "limegreen": "#32cd32",
+ "linen": "#faf0e6",
+ "magenta": "#ff00ff",
+ "maroon": "#800000",
+ "mediumaquamarine": "#66cdaa",
+ "mediumblue": "#0000cd",
+ "mediumorchid": "#ba55d3",
+ "mediumpurple": "#9370db",
+ "mediumseagreen": "#3cb371",
+ "mediumslateblue": "#7b68ee",
+ "mediumspringgreen": "#00fa9a",
+ "mediumturquoise": "#48d1cc",
+ "mediumvioletred": "#c71585",
+ "midnightblue": "#191970",
+ "mintcream": "#f5fffa",
+ "mistyrose": "#ffe4e1",
+ "moccasin": "#ffe4b5",
+ "navajowhite": "#ffdead",
+ "navy": "#000080",
+ "oldlace": "#fdf5e6",
+ "olive": "#808000",
+ "olivedrab": "#6b8e23",
+ "orange": "#ffa500",
+ "orangered": "#ff4500",
+ "orchid": "#da70d6",
+ "palegoldenrod": "#eee8aa",
+ "palegreen": "#98fb98",
+ "paleturquoise": "#afeeee",
+ "palevioletred": "#db7093",
+ "papayawhip": "#ffefd5",
+ "peachpuff": "#ffdab9",
+ "peru": "#cd853f",
+ "pink": "#ffc0cb",
+ "plum": "#dda0dd",
+ "powderblue": "#b0e0e6",
+ "purple": "#800080",
+ "red": "#ff0000",
+ "rosybrown": "#bc8f8f",
+ "royalblue": "#4169e1",
+ "saddlebrown": "#8b4513",
+ "salmon": "#fa8072",
+ "sandybrown": "#f4a460",
+ "seagreen": "#2e8b57",
+ "seashell": "#fff5ee",
+ "sienna": "#a0522d",
+ "silver": "#c0c0c0",
+ "skyblue": "#87ceeb",
+ "slateblue": "#6a5acd",
+ "slategray": "#708090",
+ "slategrey": "#708090",
+ "snow": "#fffafa",
+ "springgreen": "#00ff7f",
+ "steelblue": "#4682b4",
+ "tan": "#d2b48c",
+ "teal": "#008080",
+ "thistle": "#d8bfd8",
+ "tomato": "#ff6347",
+ "turquoise": "#40e0d0",
+ "violet": "#ee82ee",
+ "wheat": "#f5deb3",
+ "white": "#ffffff",
+ "whitesmoke": "#f5f5f5",
+ "yellow": "#ffff00",
+ "yellowgreen": "#9acd32",
+}
diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py
new file mode 100644
index 000000000..5217a7366
--- /dev/null
+++ b/PIL/ImageDraw.py
@@ -0,0 +1,378 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# drawing interface operations
+#
+# History:
+# 1996-04-13 fl Created (experimental)
+# 1996-08-07 fl Filled polygons, ellipses.
+# 1996-08-13 fl Added text support
+# 1998-06-28 fl Handle I and F images
+# 1998-12-29 fl Added arc; use arc primitive to draw ellipses
+# 1999-01-10 fl Added shape stuff (experimental)
+# 1999-02-06 fl Added bitmap support
+# 1999-02-11 fl Changed all primitives to take options
+# 1999-02-20 fl Fixed backwards compatibility
+# 2000-10-12 fl Copy on write, when necessary
+# 2001-02-18 fl Use default ink for bitmap/text also in fill mode
+# 2002-10-24 fl Added support for CSS-style color strings
+# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing
+# 2002-12-11 fl Refactored low-level drawing API (work in progress)
+# 2004-08-26 fl Made Draw() a factory function, added getdraw() support
+# 2004-09-04 fl Added width support to line primitive
+# 2004-09-10 fl Added font mode handling
+# 2006-06-19 fl Added font bearing support (getmask2)
+#
+# Copyright (c) 1997-2006 by Secret Labs AB
+# Copyright (c) 1996-2006 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image, ImageColor
+
+try:
+ import warnings
+except ImportError:
+ warnings = None
+
+##
+# A simple 2D drawing interface for PIL images.
+#
+# Application code should use the Draw factory, instead of
+# directly.
+
+class ImageDraw:
+
+ ##
+ # Create a drawing instance.
+ #
+ # @param im The image to draw in.
+ # @param mode Optional mode to use for color values. For RGB
+ # images, this argument can be RGB or RGBA (to blend the
+ # drawing into the image). For all other modes, this argument
+ # must be the same as the image mode. If omitted, the mode
+ # defaults to the mode of the image.
+
+ def __init__(self, im, mode=None):
+ im.load()
+ if im.readonly:
+ im._copy() # make it writable
+ blend = 0
+ if mode is None:
+ mode = im.mode
+ if mode != im.mode:
+ if mode == "RGBA" and im.mode == "RGB":
+ blend = 1
+ else:
+ raise ValueError("mode mismatch")
+ if mode == "P":
+ self.palette = im.palette
+ else:
+ self.palette = None
+ self.im = im.im
+ self.draw = Image.core.draw(self.im, blend)
+ self.mode = mode
+ if mode in ("I", "F"):
+ self.ink = self.draw.draw_ink(1, mode)
+ else:
+ self.ink = self.draw.draw_ink(-1, mode)
+ if mode in ("1", "P", "I", "F"):
+ # FIXME: fix Fill2 to properly support matte for I+F images
+ self.fontmode = "1"
+ else:
+ self.fontmode = "L" # aliasing is okay for other modes
+ self.fill = 0
+ self.font = None
+
+ ##
+ # Set the default pen color.
+
+ def setink(self, ink):
+ # compatibility
+ if warnings:
+ warnings.warn(
+ "'setink' is deprecated; use keyword arguments instead",
+ DeprecationWarning, stacklevel=2
+ )
+ if Image.isStringType(ink):
+ ink = ImageColor.getcolor(ink, self.mode)
+ if self.palette and not Image.isNumberType(ink):
+ ink = self.palette.getcolor(ink)
+ self.ink = self.draw.draw_ink(ink, self.mode)
+
+ ##
+ # Set the default background color.
+
+ def setfill(self, onoff):
+ # compatibility
+ if warnings:
+ warnings.warn(
+ "'setfill' is deprecated; use keyword arguments instead",
+ DeprecationWarning, stacklevel=2
+ )
+ self.fill = onoff
+
+ ##
+ # Set the default font.
+
+ def setfont(self, font):
+ # compatibility
+ self.font = font
+
+ ##
+ # Get the current default font.
+
+ def getfont(self):
+ if not self.font:
+ # FIXME: should add a font repository
+ import ImageFont
+ self.font = ImageFont.load_default()
+ return self.font
+
+ def _getink(self, ink, fill=None):
+ if ink is None and fill is None:
+ if self.fill:
+ fill = self.ink
+ else:
+ ink = self.ink
+ else:
+ if ink is not None:
+ if Image.isStringType(ink):
+ ink = ImageColor.getcolor(ink, self.mode)
+ if self.palette and not Image.isNumberType(ink):
+ ink = self.palette.getcolor(ink)
+ ink = self.draw.draw_ink(ink, self.mode)
+ if fill is not None:
+ if Image.isStringType(fill):
+ fill = ImageColor.getcolor(fill, self.mode)
+ if self.palette and not Image.isNumberType(fill):
+ fill = self.palette.getcolor(fill)
+ fill = self.draw.draw_ink(fill, self.mode)
+ return ink, fill
+
+ ##
+ # Draw an arc.
+
+ def arc(self, xy, start, end, fill=None):
+ ink, fill = self._getink(fill)
+ if ink is not None:
+ self.draw.draw_arc(xy, start, end, ink)
+
+ ##
+ # Draw a bitmap.
+
+ def bitmap(self, xy, bitmap, fill=None):
+ bitmap.load()
+ ink, fill = self._getink(fill)
+ if ink is None:
+ ink = fill
+ if ink is not None:
+ self.draw.draw_bitmap(xy, bitmap.im, ink)
+
+ ##
+ # Draw a chord.
+
+ def chord(self, xy, start, end, fill=None, outline=None):
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_chord(xy, start, end, fill, 1)
+ if ink is not None:
+ self.draw.draw_chord(xy, start, end, ink, 0)
+
+ ##
+ # Draw an ellipse.
+
+ def ellipse(self, xy, fill=None, outline=None):
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_ellipse(xy, fill, 1)
+ if ink is not None:
+ self.draw.draw_ellipse(xy, ink, 0)
+
+ ##
+ # Draw a line, or a connected sequence of line segments.
+
+ def line(self, xy, fill=None, width=0):
+ ink, fill = self._getink(fill)
+ if ink is not None:
+ self.draw.draw_lines(xy, ink, width)
+
+ ##
+ # (Experimental) Draw a shape.
+
+ def shape(self, shape, fill=None, outline=None):
+ # experimental
+ shape.close()
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_outline(shape, fill, 1)
+ if ink is not None:
+ self.draw.draw_outline(shape, ink, 0)
+
+ ##
+ # Draw a pieslice.
+
+ def pieslice(self, xy, start, end, fill=None, outline=None):
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_pieslice(xy, start, end, fill, 1)
+ if ink is not None:
+ self.draw.draw_pieslice(xy, start, end, ink, 0)
+
+ ##
+ # Draw one or more individual pixels.
+
+ def point(self, xy, fill=None):
+ ink, fill = self._getink(fill)
+ if ink is not None:
+ self.draw.draw_points(xy, ink)
+
+ ##
+ # Draw a polygon.
+
+ def polygon(self, xy, fill=None, outline=None):
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_polygon(xy, fill, 1)
+ if ink is not None:
+ self.draw.draw_polygon(xy, ink, 0)
+
+ ##
+ # Draw a rectangle.
+
+ def rectangle(self, xy, fill=None, outline=None):
+ ink, fill = self._getink(outline, fill)
+ if fill is not None:
+ self.draw.draw_rectangle(xy, fill, 1)
+ if ink is not None:
+ self.draw.draw_rectangle(xy, ink, 0)
+
+ ##
+ # Draw text.
+
+ def text(self, xy, text, fill=None, font=None, anchor=None):
+ ink, fill = self._getink(fill)
+ if font is None:
+ font = self.getfont()
+ if ink is None:
+ ink = fill
+ if ink is not None:
+ try:
+ mask, offset = font.getmask2(text, self.fontmode)
+ xy = xy[0] + offset[0], xy[1] + offset[1]
+ except AttributeError:
+ try:
+ mask = font.getmask(text, self.fontmode)
+ except TypeError:
+ mask = font.getmask(text)
+ self.draw.draw_bitmap(xy, mask, ink)
+
+ ##
+ # Get the size of a given string, in pixels.
+
+ def textsize(self, text, font=None):
+ if font is None:
+ font = self.getfont()
+ return font.getsize(text)
+
+##
+# A simple 2D drawing interface for PIL images.
+#
+# @param im The image to draw in.
+# @param mode Optional mode to use for color values. For RGB
+# images, this argument can be RGB or RGBA (to blend the
+# drawing into the image). For all other modes, this argument
+# must be the same as the image mode. If omitted, the mode
+# defaults to the mode of the image.
+
+def Draw(im, mode=None):
+ try:
+ return im.getdraw(mode)
+ except AttributeError:
+ return ImageDraw(im, mode)
+
+# experimental access to the outline API
+try:
+ Outline = Image.core.outline
+except:
+ Outline = None
+
+##
+# (Experimental) A more advanced 2D drawing interface for PIL images,
+# based on the WCK interface.
+#
+# @param im The image to draw in.
+# @param hints An optional list of hints.
+# @return A (drawing context, drawing resource factory) tuple.
+
+def getdraw(im=None, hints=None):
+ # FIXME: this needs more work!
+ # FIXME: come up with a better 'hints' scheme.
+ handler = None
+ if not hints or "nicest" in hints:
+ try:
+ import _imagingagg
+ handler = _imagingagg
+ except ImportError:
+ pass
+ if handler is None:
+ import ImageDraw2
+ handler = ImageDraw2
+ if im:
+ im = handler.Draw(im)
+ return im, handler
+
+##
+# (experimental) Fills a bounded region with a given color.
+#
+# @param image Target image.
+# @param xy Seed position (a 2-item coordinate tuple).
+# @param value Fill color.
+# @param border Optional border value. If given, the region consists of
+# pixels with a color different from the border color. If not given,
+# the region consists of pixels having the same color as the seed
+# pixel.
+
+def floodfill(image, xy, value, border=None):
+ "Fill bounded region."
+ # based on an implementation by Eric S. Raymond
+ pixel = image.load()
+ x, y = xy
+ try:
+ background = pixel[x, y]
+ if background == value:
+ return # seed point already has fill color
+ pixel[x, y] = value
+ except IndexError:
+ return # seed point outside image
+ edge = [(x, y)]
+ if border is None:
+ while edge:
+ newedge = []
+ for (x, y) in edge:
+ for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
+ try:
+ p = pixel[s, t]
+ except IndexError:
+ pass
+ else:
+ if p == background:
+ pixel[s, t] = value
+ newedge.append((s, t))
+ edge = newedge
+ else:
+ while edge:
+ newedge = []
+ for (x, y) in edge:
+ for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)):
+ try:
+ p = pixel[s, t]
+ except IndexError:
+ pass
+ else:
+ if p != value and p != border:
+ pixel[s, t] = value
+ newedge.append((s, t))
+ edge = newedge
diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py
new file mode 100644
index 000000000..dbf1a1f88
--- /dev/null
+++ b/PIL/ImageDraw2.py
@@ -0,0 +1,105 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# WCK-style drawing interface operations
+#
+# History:
+# 2003-12-07 fl created
+# 2005-05-15 fl updated; added to PIL as ImageDraw2
+# 2005-05-15 fl added text support
+# 2005-05-20 fl added arc/chord/pieslice support
+#
+# Copyright (c) 2003-2005 by Secret Labs AB
+# Copyright (c) 2003-2005 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image, ImageColor, ImageDraw, ImageFont, ImagePath
+
+class Pen:
+ def __init__(self, color, width=1, opacity=255):
+ self.color = ImageColor.getrgb(color)
+ self.width = width
+
+class Brush:
+ def __init__(self, color, opacity=255):
+ self.color = ImageColor.getrgb(color)
+
+class Font:
+ def __init__(self, color, file, size=12):
+ # FIXME: add support for bitmap fonts
+ self.color = ImageColor.getrgb(color)
+ self.font = ImageFont.truetype(file, size)
+
+class Draw:
+
+ def __init__(self, image, size=None, color=None):
+ if not hasattr(image, "im"):
+ image = Image.new(image, size, color)
+ self.draw = ImageDraw.Draw(image)
+ self.image = image
+ self.transform = None
+
+ def flush(self):
+ return self.image
+
+ def render(self, op, xy, pen, brush=None):
+ # handle color arguments
+ outline = fill = None; width = 1
+ if isinstance(pen, Pen):
+ outline = pen.color
+ width = pen.width
+ elif isinstance(brush, Pen):
+ outline = brush.color
+ width = brush.width
+ if isinstance(brush, Brush):
+ fill = brush.color
+ elif isinstance(pen, Brush):
+ fill = pen.color
+ # handle transformation
+ if self.transform:
+ xy = ImagePath.Path(xy)
+ xy.transform(self.transform)
+ # render the item
+ if op == "line":
+ self.draw.line(xy, fill=outline, width=width)
+ else:
+ getattr(self.draw, op)(xy, fill=fill, outline=outline)
+
+ def settransform(self, (xoffset, yoffset)):
+ self.transform = (1, 0, xoffset, 0, 1, yoffset)
+
+ def arc(self, xy, start, end, *options):
+ self.render("arc", xy, start, end, *options)
+
+ def chord(self, xy, start, end, *options):
+ self.render("chord", xy, start, end, *options)
+
+ def ellipse(self, xy, *options):
+ self.render("ellipse", xy, *options)
+
+ def line(self, xy, *options):
+ self.render("line", xy, *options)
+
+ def pieslice(self, xy, start, end, *options):
+ self.render("pieslice", xy, start, end, *options)
+
+ def polygon(self, xy, *options):
+ self.render("polygon", xy, *options)
+
+ def rectangle(self, xy, *options):
+ self.render("rectangle", xy, *options)
+
+ def symbol(self, xy, symbol, *options):
+ raise NotImplementedError("not in this version")
+
+ def text(self, xy, text, font):
+ if self.transform:
+ xy = ImagePath.Path(xy)
+ xy.transform(self.transform)
+ self.draw.text(xy, text, font=font.font, fill=font.color)
+
+ def textsize(self, text, font):
+ return self.draw.textsize(text, font=font.font)
diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py
new file mode 100644
index 000000000..86a19e650
--- /dev/null
+++ b/PIL/ImageEnhance.py
@@ -0,0 +1,90 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# image enhancement classes
+#
+# For a background, see "Image Processing By Interpolation and
+# Extrapolation", Paul Haeberli and Douglas Voorhies. Available
+# at http://www.sgi.com/grafica/interp/index.html
+#
+# History:
+# 1996-03-23 fl Created
+# 2009-06-16 fl Fixed mean calculation
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+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):
+ 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"
+ 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"
+ 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"
+ 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"
+ def __init__(self, image):
+ self.image = image
+ self.degenerate = image.filter(ImageFilter.SMOOTH)
diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py
new file mode 100644
index 000000000..8a97c1b5b
--- /dev/null
+++ b/PIL/ImageFile.py
@@ -0,0 +1,528 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# base class for image file handlers
+#
+# history:
+# 1995-09-09 fl Created
+# 1996-03-11 fl Fixed load mechanism.
+# 1996-04-15 fl Added pcx/xbm decoders.
+# 1996-04-30 fl Added encoders.
+# 1996-12-14 fl Added load helpers
+# 1997-01-11 fl Use encode_to_file where possible
+# 1997-08-27 fl Flush output in _save
+# 1998-03-05 fl Use memory mapping for some modes
+# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B"
+# 1999-05-31 fl Added image parser
+# 2000-10-12 fl Set readonly flag on memory-mapped images
+# 2002-03-20 fl Use better messages for common decoder errors
+# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available
+# 2003-10-30 fl Added StubImageFile class
+# 2004-02-25 fl Made incremental parser more robust
+#
+# Copyright (c) 1997-2004 by Secret Labs AB
+# Copyright (c) 1995-2004 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import traceback, string, os
+
+MAXBLOCK = 65536
+
+SAFEBLOCK = 1024*1024
+
+ERRORS = {
+ -1: "image buffer overrun error",
+ -2: "decoding error",
+ -3: "unknown error",
+ -8: "bad configuration",
+ -9: "out of memory error"
+}
+
+def raise_ioerror(error):
+ try:
+ message = Image.core.getcodecstatus(error)
+ except AttributeError:
+ message = ERRORS.get(error)
+ if not message:
+ message = "decoder error %d" % error
+ raise IOError(message + " when reading image file")
+
+#
+# --------------------------------------------------------------------
+# Helpers
+
+def _tilesort(t1, t2):
+ # sort on offset
+ return cmp(t1[2], t2[2])
+
+#
+# --------------------------------------------------------------------
+# ImageFile base class
+
+##
+# Base class for image file handlers.
+
+class ImageFile(Image.Image):
+ "Base class for image file format handlers."
+
+ def __init__(self, fp=None, filename=None):
+ Image.Image.__init__(self)
+
+ self.tile = None
+ self.readonly = 1 # until we know better
+
+ self.decoderconfig = ()
+ self.decodermaxblock = MAXBLOCK
+
+ if Image.isStringType(fp):
+ # filename
+ self.fp = open(fp, "rb")
+ self.filename = fp
+ else:
+ # stream
+ self.fp = fp
+ self.filename = filename
+
+ try:
+ self._open()
+ except IndexError, v: # end of data
+ if Image.DEBUG > 1:
+ traceback.print_exc()
+ raise SyntaxError, v
+ except TypeError, v: # end of data (ord)
+ if Image.DEBUG > 1:
+ traceback.print_exc()
+ raise SyntaxError, v
+ except KeyError, v: # unsupported mode
+ if Image.DEBUG > 1:
+ traceback.print_exc()
+ raise SyntaxError, v
+ except EOFError, v: # got header but not the first frame
+ if Image.DEBUG > 1:
+ traceback.print_exc()
+ raise SyntaxError, v
+
+ if not self.mode or self.size[0] <= 0:
+ raise SyntaxError, "not identified by this driver"
+
+ def draft(self, mode, size):
+ "Set draft mode"
+
+ pass
+
+ def verify(self):
+ "Check file integrity"
+
+ # raise exception if something's wrong. must be called
+ # directly after open, and closes file when finished.
+ self.fp = None
+
+ def load(self):
+ "Load image data based on tile list"
+
+ pixel = Image.Image.load(self)
+
+ if self.tile is None:
+ raise IOError("cannot load this image")
+ if not self.tile:
+ return pixel
+
+ self.map = None
+
+ readonly = 0
+
+ if self.filename and len(self.tile) == 1:
+ # try memory mapping
+ d, e, o, a = self.tile[0]
+ if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
+ try:
+ if hasattr(Image.core, "map"):
+ # use built-in mapper
+ self.map = Image.core.map(self.filename)
+ self.map.seek(o)
+ self.im = self.map.readimage(
+ self.mode, self.size, a[1], a[2]
+ )
+ else:
+ # use mmap, if possible
+ import mmap
+ file = open(self.filename, "r+")
+ size = os.path.getsize(self.filename)
+ # FIXME: on Unix, use PROT_READ etc
+ self.map = mmap.mmap(file.fileno(), size)
+ self.im = Image.core.map_buffer(
+ self.map, self.size, d, e, o, a
+ )
+ readonly = 1
+ except (AttributeError, EnvironmentError, ImportError):
+ self.map = None
+
+ self.load_prepare()
+
+ # look for read/seek overrides
+ try:
+ read = self.load_read
+ except AttributeError:
+ read = self.fp.read
+
+ try:
+ seek = self.load_seek
+ except AttributeError:
+ seek = self.fp.seek
+
+ if not self.map:
+
+ # sort tiles in file order
+ self.tile.sort(_tilesort)
+
+ try:
+ # FIXME: This is a hack to handle TIFF's JpegTables tag.
+ prefix = self.tile_prefix
+ except AttributeError:
+ prefix = ""
+
+ for d, e, o, a in self.tile:
+ d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
+ seek(o)
+ try:
+ d.setimage(self.im, e)
+ except ValueError:
+ continue
+ b = prefix
+ t = len(b)
+ while 1:
+ s = read(self.decodermaxblock)
+ if not s:
+ self.tile = []
+ raise IOError("image file is truncated (%d bytes not processed)" % len(b))
+ b = b + s
+ n, e = d.decode(b)
+ if n < 0:
+ break
+ b = b[n:]
+ t = t + n
+
+ self.tile = []
+ self.readonly = readonly
+
+ self.fp = None # might be shared
+
+ if not self.map and e < 0:
+ raise_ioerror(e)
+
+ # post processing
+ if hasattr(self, "tile_post_rotate"):
+ # FIXME: This is a hack to handle rotated PCD's
+ self.im = self.im.rotate(self.tile_post_rotate)
+ self.size = self.im.size
+
+ self.load_end()
+
+ return Image.Image.load(self)
+
+ def load_prepare(self):
+ # create image memory if necessary
+ if not self.im or\
+ self.im.mode != self.mode or self.im.size != self.size:
+ self.im = Image.core.new(self.mode, self.size)
+ # create palette (optional)
+ if self.mode == "P":
+ Image.Image.load(self)
+
+ def load_end(self):
+ # may be overridden
+ pass
+
+ # may be defined for contained formats
+ # def load_seek(self, pos):
+ # pass
+
+ # may be defined for blocked formats (e.g. PNG)
+ # 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."
+
+ def _open(self):
+ raise NotImplementedError(
+ "StubImageFile subclass must implement _open"
+ )
+
+ def load(self):
+ loader = self._load()
+ if loader is None:
+ raise IOError("cannot find loader for this %s file" % self.format)
+ image = loader.load(self)
+ assert image is not None
+ # become the other object (!)
+ self.__class__ = image.__class__
+ self.__dict__ = image.__dict__
+
+ ##
+ # (Hook) Find actual image loader.
+
+ def _load(self):
+ 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 = 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):
+ 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):
+ # collect data
+
+ if self.finished:
+ return
+
+ if self.data is None:
+ self.data = data
+ else:
+ self.data = self.data + data
+
+ # parse what we have
+ if self.decoder:
+
+ if self.offset > 0:
+ # skip header
+ skip = min(len(self.data), self.offset)
+ self.data = self.data[skip:]
+ self.offset = self.offset - skip
+ if self.offset > 0 or not self.data:
+ return
+
+ n, e = self.decoder.decode(self.data)
+
+ if n < 0:
+ # end of stream
+ self.data = None
+ self.finished = 1
+ if e < 0:
+ # decoding error
+ self.image = None
+ raise_ioerror(e)
+ else:
+ # end of image
+ return
+ self.data = self.data[n:]
+
+ elif self.image:
+
+ # if we end up here with no decoder, this file cannot
+ # be incrementally parsed. wait until we've gotten all
+ # available data
+ pass
+
+ else:
+
+ # attempt to open this file
+ try:
+ try:
+ fp = _ParserFile(self.data)
+ im = Image.open(fp)
+ finally:
+ fp.close() # explicitly close the virtual file
+ except IOError:
+ pass # not enough data
+ else:
+ flag = hasattr(im, "load_seek") or hasattr(im, "load_read")
+ if flag or len(im.tile) != 1:
+ # custom load code, or multiple tiles
+ self.decode = None
+ else:
+ # initialize decoder
+ im.load_prepare()
+ d, e, o, a = im.tile[0]
+ im.tile = []
+ self.decoder = Image._getdecoder(
+ im.mode, d, a, im.decoderconfig
+ )
+ self.decoder.setimage(im.im, e)
+
+ # calculate decoder offset
+ self.offset = o
+ if self.offset <= len(self.data):
+ self.data = self.data[self.offset:]
+ self.offset = 0
+
+ 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):
+ # finish decoding
+ if self.decoder:
+ # get rid of what's left in the buffers
+ self.feed("")
+ self.data = self.decoder = None
+ if not self.finished:
+ raise IOError("image was incomplete")
+ if not self.image:
+ raise IOError("cannot parse this image")
+ if self.data:
+ # incremental parsing not possible; reopen the file
+ # not that we have all data
+ try:
+ fp = _ParserFile(self.data)
+ self.image = Image.open(fp)
+ finally:
+ self.image.load()
+ fp.close() # explicitly close the virtual file
+ return self.image
+
+# --------------------------------------------------------------------
+
+##
+# (Helper) Save image body to file.
+#
+# @param im Image object.
+# @param fp File object.
+# @param tile Tile list.
+
+def _save(im, fp, tile):
+ "Helper to save image based on tile list"
+
+ im.load()
+ if not hasattr(im, "encoderconfig"):
+ im.encoderconfig = ()
+ tile.sort(_tilesort)
+ # FIXME: make MAXBLOCK a configuration parameter
+ bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c
+ try:
+ fh = fp.fileno()
+ fp.flush()
+ except AttributeError:
+ # 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:
+ l, s, d = e.encode(bufsize)
+ fp.write(d)
+ if s:
+ break
+ if s < 0:
+ raise IOError("encoder error %d when writing image file" % s)
+ else:
+ # slight speedup: compress to real file 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)
+ s = e.encode_to_file(fh, bufsize)
+ if s < 0:
+ raise IOError("encoder error %d when writing image file" % s)
+ try:
+ fp.flush()
+ 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):
+ if size <= 0:
+ return ""
+ if size <= SAFEBLOCK:
+ return fp.read(size)
+ data = []
+ while size > 0:
+ block = fp.read(min(size, SAFEBLOCK))
+ if not block:
+ break
+ data.append(block)
+ size = size - len(block)
+ return string.join(data, "")
diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py
new file mode 100644
index 000000000..c12a25738
--- /dev/null
+++ b/PIL/ImageFileIO.py
@@ -0,0 +1,39 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# kludge to get basic ImageFileIO functionality
+#
+# History:
+# 1998-08-06 fl Recreated
+#
+# Copyright (c) Secret Labs AB 1998-2002.
+#
+# See the README file for information on usage and redistribution.
+#
+
+from StringIO import StringIO
+
+##
+# 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
+
+class ImageFileIO(StringIO):
+
+ ##
+ # 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
+
+ def __init__(self, fp):
+ data = fp.read()
+ StringIO.__init__(self, data)
diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py
new file mode 100644
index 000000000..918bab177
--- /dev/null
+++ b/PIL/ImageFilter.py
@@ -0,0 +1,289 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# standard filters
+#
+# History:
+# 1995-11-27 fl Created
+# 2002-06-08 fl Added rank and mode filters
+# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call
+#
+# Copyright (c) 1997-2003 by Secret Labs AB.
+# Copyright (c) 1995-2002 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+class Filter:
+ pass
+
+##
+# Convolution filter kernel.
+
+class Kernel(Filter):
+
+ ##
+ # 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.
+
+ def __init__(self, size, kernel, scale=None, offset=0):
+ if scale is None:
+ # default scale is sum of kernel
+ scale = reduce(lambda a,b: a+b, kernel)
+ if size[0] * size[1] != len(kernel):
+ raise ValueError("not enough coefficients in kernel")
+ self.filterargs = size, scale, offset, kernel
+
+ def filter(self, image):
+ if image.mode == "P":
+ raise ValueError("cannot filter palette images")
+ return apply(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.
+ #
+ # @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.
+
+ def __init__(self, size, rank):
+ self.size = size
+ self.rank = rank
+
+ def filter(self, image):
+ if image.mode == "P":
+ raise ValueError("cannot filter palette images")
+ 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.
+ #
+ # @param size The kernel size, in pixels.
+
+ def __init__(self, size=3):
+ self.size = size
+ self.rank = size*size/2
+
+##
+# Min filter. Picks the lowest pixel value in a window with the given
+# size.
+
+class MinFilter(RankFilter):
+ name = "Min"
+
+ ##
+ # Create a min filter.
+ #
+ # @param size The kernel size, in pixels.
+
+ 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.
+ #
+ # @param size The kernel size, in pixels.
+
+ 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.
+
+ def __init__(self, size=3):
+ self.size = size
+ def filter(self, image):
+ return image.modefilter(self.size)
+
+##
+# Gaussian blur filter.
+
+class GaussianBlur(Filter):
+ name = "GaussianBlur"
+
+ def __init__(self, radius=2):
+ self.radius = 2
+ def filter(self, image):
+ return image.gaussian_blur(self.radius)
+
+##
+# Unsharp mask filter.
+
+class UnsharpMask(Filter):
+ name = "UnsharpMask"
+
+ def __init__(self, radius=2, percent=150, threshold=3):
+ self.radius = 2
+ 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"
+ filterargs = (5, 5), 16, 0, (
+ 1, 1, 1, 1, 1,
+ 1, 0, 0, 0, 1,
+ 1, 0, 0, 0, 1,
+ 1, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1
+ )
+
+##
+# Simple contour filter.
+
+class CONTOUR(BuiltinFilter):
+ name = "Contour"
+ filterargs = (3, 3), 1, 255, (
+ -1, -1, -1,
+ -1, 8, -1,
+ -1, -1, -1
+ )
+
+##
+# Simple detail filter.
+
+class DETAIL(BuiltinFilter):
+ name = "Detail"
+ filterargs = (3, 3), 6, 0, (
+ 0, -1, 0,
+ -1, 10, -1,
+ 0, -1, 0
+ )
+
+##
+# Simple edge enhancement filter.
+
+class EDGE_ENHANCE(BuiltinFilter):
+ name = "Edge-enhance"
+ filterargs = (3, 3), 2, 0, (
+ -1, -1, -1,
+ -1, 10, -1,
+ -1, -1, -1
+ )
+
+##
+# Simple stronger edge enhancement filter.
+
+class EDGE_ENHANCE_MORE(BuiltinFilter):
+ name = "Edge-enhance More"
+ filterargs = (3, 3), 1, 0, (
+ -1, -1, -1,
+ -1, 9, -1,
+ -1, -1, -1
+ )
+
+##
+# Simple embossing filter.
+
+class EMBOSS(BuiltinFilter):
+ name = "Emboss"
+ filterargs = (3, 3), 1, 128, (
+ -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 0
+ )
+
+##
+# Simple edge-finding filter.
+
+class FIND_EDGES(BuiltinFilter):
+ name = "Find Edges"
+ filterargs = (3, 3), 1, 0, (
+ -1, -1, -1,
+ -1, 8, -1,
+ -1, -1, -1
+ )
+
+##
+# Simple smoothing filter.
+
+class SMOOTH(BuiltinFilter):
+ name = "Smooth"
+ filterargs = (3, 3), 13, 0, (
+ 1, 1, 1,
+ 1, 5, 1,
+ 1, 1, 1
+ )
+
+##
+# Simple stronger smoothing filter.
+
+class SMOOTH_MORE(BuiltinFilter):
+ name = "Smooth More"
+ filterargs = (5, 5), 100, 0, (
+ 1, 1, 1, 1, 1,
+ 1, 5, 5, 5, 1,
+ 1, 5, 44, 5, 1,
+ 1, 5, 5, 5, 1,
+ 1, 1, 1, 1, 1
+ )
+
+##
+# Simple sharpening filter.
+
+class SHARPEN(BuiltinFilter):
+ name = "Sharpen"
+ filterargs = (3, 3), 16, 0, (
+ -2, -2, -2,
+ -2, 32, -2,
+ -2, -2, -2
+ )
diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py
new file mode 100644
index 000000000..3ea2f2f8e
--- /dev/null
+++ b/PIL/ImageFont.py
@@ -0,0 +1,390 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PIL raster font management
+#
+# History:
+# 1996-08-07 fl created (experimental)
+# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3
+# 1999-02-06 fl rewrote most font management stuff in C
+# 1999-03-17 fl take pth files into account in load_path (from Richard Jones)
+# 2001-02-17 fl added freetype support
+# 2001-05-09 fl added TransposedFont wrapper class
+# 2002-03-04 fl make sure we have a "L" or "1" font
+# 2002-12-04 fl skip non-directory entries in the system path
+# 2003-04-29 fl add embedded default font
+# 2003-09-27 fl added support for truetype charmap encodings
+#
+# Todo:
+# Adapt to PILFONT2 format (16-bit fonts, compressed, single file)
+#
+# Copyright (c) 1997-2003 by Secret Labs AB
+# Copyright (c) 1996-2003 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import os, string, sys
+
+class _imagingft_not_installed:
+ # module placeholder
+ def __getattr__(self, id):
+ raise ImportError("The _imagingft C module is not installed")
+
+try:
+ import _imagingft
+ core = _imagingft
+ del _imagingft
+except ImportError:
+ core = _imagingft_not_installed()
+
+# FIXME: add support for pilfont2 format (see FontFile.py)
+
+# --------------------------------------------------------------------
+# Font metrics format:
+# "PILfont" LF
+# fontdescriptor LF
+# (optional) key=value... LF
+# "DATA" LF
+# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox)
+#
+# To place a character, cut out srcbox and paste at dstbox,
+# relative to the character position. Then move the character
+# 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"
+
+ def _load_pilfont(self, filename):
+
+ file = open(filename, "rb")
+
+ for ext in (".png", ".gif", ".pbm"):
+ try:
+ fullname = os.path.splitext(filename)[0] + ext
+ image = Image.open(fullname)
+ except:
+ pass
+ else:
+ if image and image.mode in ("1", "L"):
+ break
+ else:
+ raise IOError("cannot find glyph data file")
+
+ self.file = fullname
+
+ return self._load_pilfont_data(file, image)
+
+ def _load_pilfont_data(self, file, image):
+
+ # read PILfont header
+ if file.readline() != "PILfont\n":
+ raise SyntaxError("Not a PILfont file")
+ d = string.split(file.readline(), ";")
+ self.info = [] # FIXME: should be a dictionary
+ while True:
+ s = file.readline()
+ if not s or s == "DATA\n":
+ break
+ self.info.append(s)
+
+ # read PILfont metrics
+ data = file.read(256*20)
+
+ # check image
+ if image.mode not in ("1", "L"):
+ raise TypeError("invalid font image mode")
+
+ image.load()
+
+ self.font = Image.core.font(image.im, data)
+
+ # delegate critical operations to internal type
+ self.getsize = self.font.getsize
+ self.getmask = self.font.getmask
+
+##
+# Wrapper for FreeType fonts. Application code should use the
+# truetype factory function to create font objects.
+
+class FreeTypeFont:
+ "FreeType font wrapper (requires _imagingft service)"
+
+ def __init__(self, file, size, index=0, encoding=""):
+ # FIXME: use service provider instead
+ self.font = core.getfont(file, size, index, encoding)
+
+ def getname(self):
+ return self.font.family, self.font.style
+
+ def getmetrics(self):
+ return self.font.ascent, self.font.descent
+
+ def getsize(self, text):
+ return self.font.getsize(text)[0]
+
+ def getmask(self, text, mode=""):
+ return self.getmask2(text, mode)[0]
+
+ def getmask2(self, text, mode="", fill=Image.core.fill):
+ size, offset = self.font.getsize(text)
+ im = fill("L", size, 0)
+ self.font.render(text, im.id, mode=="1")
+ return im, offset
+
+##
+# Wrapper that creates a transposed font from any existing font
+# object.
+#
+# @param font A font object.
+# @param orientation An optional orientation. If given, this should
+# be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
+# Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270.
+
+class TransposedFont:
+ "Wrapper for writing rotated or mirrored text"
+
+ def __init__(self, font, orientation=None):
+ self.font = font
+ self.orientation = orientation # any 'transpose' argument, or None
+
+ def getsize(self, text):
+ w, h = self.font.getsize(text)
+ if self.orientation in (Image.ROTATE_90, Image.ROTATE_270):
+ return h, w
+ return w, h
+
+ def getmask(self, text, mode=""):
+ im = self.font.getmask(text, mode)
+ if self.orientation is not None:
+ 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."
+ 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."
+ try:
+ return FreeTypeFont(filename, size, index, encoding)
+ except IOError:
+ if sys.platform == "win32":
+ # check the windows font repository
+ # NOTE: must use uppercase WINDIR, to work around bugs in
+ # 1.5.2's os.environ.get()
+ windir = os.environ.get("WINDIR")
+ if windir:
+ filename = os.path.join(windir, "fonts", filename)
+ 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."
+ for dir in sys.path:
+ if Image.isDirectory(dir):
+ 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
+ import base64
+ f = ImageFont()
+ f._load_pilfont_data(
+ # courB08
+ StringIO(base64.decodestring('''
+UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA
+BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL
+AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA
+AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB
+ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A
+BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB
+//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA
+AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH
+AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA
+ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv
+AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/
+/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5
+AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA
+AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG
+AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA
+BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA
+AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA
+2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF
+AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA////
++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA
+////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA
+BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv
+AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA
+AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA
+AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA
+BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP//
+//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA
+AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF
+AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB
+mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn
+AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA
+AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7
+AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA
+Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB
+//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA
+AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ
+AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC
+DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ
+AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/
++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5
+AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/
+///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG
+AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA
+BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA
+Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC
+eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG
+AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA////
++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA
+////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA
+BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT
+AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A
+AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA
+Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA
+Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP//
+//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA
+AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ
+AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA
+LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5
+AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA
+AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5
+AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA
+AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG
+AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA
+EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK
+AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA
+pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
+AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
++QAGAAIAzgAKANUAEw==
+''')), Image.open(StringIO(base64.decodestring('''
+iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
+Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
+M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
+LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F
+IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA
+Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791
+NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx
+in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9
+SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY
+AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt
+y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG
+ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY
+lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H
+/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3
+AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47
+c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/
+/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw
+pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv
+oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR
+evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA
+AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v//
+Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
+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('''"
+ base64.encode(open(font + ".pil", "rb"), sys.stdout)
+ print "''')), Image.open(StringIO(base64.decodestring('''"
+ base64.encode(open(font + ".pbm", "rb"), sys.stdout)
+ print "'''))))"
diff --git a/PIL/ImageGL.py b/PIL/ImageGL.py
new file mode 100644
index 000000000..5c58b6ca9
--- /dev/null
+++ b/PIL/ImageGL.py
@@ -0,0 +1,28 @@
+#
+# 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
new file mode 100644
index 000000000..9bcd804d3
--- /dev/null
+++ b/PIL/ImageGrab.py
@@ -0,0 +1,71 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# screen grabber (windows only)
+#
+# History:
+# 2001-04-26 fl created
+# 2001-09-17 fl use builtin driver, if present
+# 2002-11-19 fl added grabclipboard support
+#
+# Copyright (c) 2001-2002 by Secret Labs AB
+# Copyright (c) 2001-2002 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+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)
+ grabber = Image.core.grabscreen
+except AttributeError:
+ # stand-alone driver (pil plus)
+ 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(
+ "RGB", size, data,
+ # RGB, 32-bit line padding, origo in lower left corner
+ "raw", "BGR", (size[0]*3 + 3) & -4, -1
+ )
+ if bbox:
+ 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))
+ return data
diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py
new file mode 100644
index 000000000..56c42a45a
--- /dev/null
+++ b/PIL/ImageMath.py
@@ -0,0 +1,207 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# a simple math add-on for the Python Imaging Library
+#
+# History:
+# 1999-02-15 fl Original PIL Plus release
+# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6
+# 2005-09-12 fl Fixed int() and float() for Python 2.4.1
+#
+# Copyright (c) 1999-2005 by Secret Labs AB
+# Copyright (c) 2005 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import _imagingmath
+
+VERBOSE = 0
+
+def _isconstant(v):
+ return isinstance(v, type(0)) or isinstance(v, type(0.0))
+
+class _Operand:
+ # wraps an image operand, providing standard operators
+
+ def __init__(self, im):
+ self.im = im
+
+ def __fixup(self, im1):
+ # convert image to suitable mode
+ if isinstance(im1, _Operand):
+ # argument was an image.
+ if im1.im.mode in ("1", "L"):
+ return im1.im.convert("I")
+ elif im1.im.mode in ("I", "F"):
+ return im1.im
+ else:
+ raise ValueError, "unsupported mode: %s" % im1.im.mode
+ else:
+ # argument was a constant
+ if _isconstant(im1) and self.im.mode in ("1", "L", "I"):
+ return Image.new("I", self.im.size, im1)
+ else:
+ return Image.new("F", self.im.size, im1)
+
+ def apply(self, op, im1, im2=None, mode=None):
+ im1 = self.__fixup(im1)
+ if im2 is None:
+ # unary operation
+ out = Image.new(mode or im1.mode, im1.size, None)
+ im1.load()
+ try:
+ op = getattr(_imagingmath, op+"_"+im1.mode)
+ except AttributeError:
+ raise TypeError, "bad operand type for '%s'" % op
+ _imagingmath.unop(op, out.im.id, im1.im.id)
+ else:
+ # binary operation
+ im2 = self.__fixup(im2)
+ if im1.mode != im2.mode:
+ # convert both arguments to floating point
+ 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"
+ if im1.size != im2.size:
+ # crop both arguments to a common size
+ size = (min(im1.size[0], im2.size[0]),
+ min(im1.size[1], im2.size[1]))
+ if im1.size != size: im1 = im1.crop((0, 0) + size)
+ if im2.size != size: im2 = im2.crop((0, 0) + size)
+ out = Image.new(mode or im1.mode, size, None)
+ else:
+ out = Image.new(mode or im1.mode, im1.size, None)
+ im1.load(); im2.load()
+ try:
+ op = getattr(_imagingmath, op+"_"+im1.mode)
+ except AttributeError:
+ 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):
+ # an image is "true" if it contains at least one non-zero pixel
+ return self.im.getbbox() is not None
+ def __abs__(self):
+ return self.apply("abs", self)
+ def __pos__(self):
+ return self
+ def __neg__(self):
+ return self.apply("neg", self)
+
+ # binary operators
+ def __add__(self, other):
+ return self.apply("add", self, other)
+ def __radd__(self, other):
+ return self.apply("add", other, self)
+ def __sub__(self, other):
+ return self.apply("sub", self, other)
+ def __rsub__(self, other):
+ return self.apply("sub", other, self)
+ def __mul__(self, other):
+ return self.apply("mul", self, other)
+ def __rmul__(self, other):
+ return self.apply("mul", other, self)
+ def __div__(self, other):
+ return self.apply("div", self, other)
+ def __rdiv__(self, other):
+ return self.apply("div", other, self)
+ def __mod__(self, other):
+ return self.apply("mod", self, other)
+ def __rmod__(self, other):
+ return self.apply("mod", other, self)
+ def __pow__(self, other):
+ return self.apply("pow", self, other)
+ def __rpow__(self, other):
+ return self.apply("pow", other, self)
+
+ # bitwise
+ def __invert__(self):
+ return self.apply("invert", self)
+ def __and__(self, other):
+ return self.apply("and", self, other)
+ def __rand__(self, other):
+ return self.apply("and", other, self)
+ def __or__(self, other):
+ return self.apply("or", self, other)
+ def __ror__(self, other):
+ return self.apply("or", other, self)
+ def __xor__(self, other):
+ return self.apply("xor", self, other)
+ def __rxor__(self, other):
+ return self.apply("xor", other, self)
+ def __lshift__(self, other):
+ return self.apply("lshift", self, other)
+ def __rshift__(self, other):
+ return self.apply("rshift", self, other)
+
+ # logical
+ def __eq__(self, other):
+ return self.apply("eq", self, other)
+ def __ne__(self, other):
+ return self.apply("ne", self, other)
+ def __lt__(self, other):
+ return self.apply("lt", self, other)
+ def __le__(self, other):
+ return self.apply("le", self, other)
+ def __gt__(self, other):
+ return self.apply("gt", self, other)
+ def __ge__(self, other):
+ return self.apply("ge", self, other)
+
+# conversions
+def imagemath_int(self):
+ return _Operand(self.im.convert("I"))
+def imagemath_float(self):
+ return _Operand(self.im.convert("F"))
+
+# logical
+def imagemath_equal(self, other):
+ return self.apply("eq", self, other, mode="I")
+def imagemath_notequal(self, other):
+ return self.apply("ne", self, other, mode="I")
+
+def imagemath_min(self, other):
+ return self.apply("min", self, other)
+def imagemath_max(self, other):
+ return self.apply("max", self, other)
+
+def imagemath_convert(self, mode):
+ return _Operand(self.im.convert(mode))
+
+ops = {}
+for k, v in 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):
+
+ # build execution namespace
+ args = ops.copy()
+ args.update(_dict)
+ args.update(kw)
+ for k, v in args.items():
+ if hasattr(v, "im"):
+ args[k] = _Operand(v)
+
+ import __builtin__
+ out =__builtin__.eval(expression, args)
+ try:
+ return out.im
+ except AttributeError:
+ return out
diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py
new file mode 100644
index 000000000..1d5df1c6d
--- /dev/null
+++ b/PIL/ImageMode.py
@@ -0,0 +1,50 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# standard mode descriptors
+#
+# History:
+# 2006-03-20 fl Added
+#
+# Copyright (c) 2006 by Secret Labs AB.
+# Copyright (c) 2006 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+# mode descriptor cache
+_modes = {}
+
+##
+# Wrapper for mode strings.
+
+class ModeDescriptor:
+
+ def __init__(self, mode, bands, basemode, basetype):
+ self.mode = mode
+ self.bands = bands
+ self.basemode = basemode
+ self.basetype = basetype
+
+ def __str__(self):
+ return self.mode
+
+##
+# Gets a mode descriptor for the given mode.
+
+def getmode(mode):
+ if not _modes:
+ # initialize mode cache
+ import Image
+ # core modes
+ for m, (basemode, basetype, bands) in Image._MODEINFO.items():
+ _modes[m] = ModeDescriptor(m, bands, basemode, basetype)
+ # extra experimental modes
+ _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
+ _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
+ # mapping modes
+ _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
+ _modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
+ _modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
+ return _modes[mode]
diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py
new file mode 100644
index 000000000..b51d78e25
--- /dev/null
+++ b/PIL/ImageOps.py
@@ -0,0 +1,439 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# standard image operations
+#
+# History:
+# 2001-10-20 fl Created
+# 2001-10-23 fl Added autocontrast operator
+# 2001-12-18 fl Added Kevin's fit operator
+# 2004-03-14 fl Fixed potential division by zero in equalize
+# 2005-05-05 fl Fixed equalize for low number of values
+#
+# Copyright (c) 2001-2004 by Secret Labs AB
+# Copyright (c) 2001-2004 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+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
+##
+
+#
+# helpers
+
+def _border(border):
+ if type(border) is type(()):
+ if len(border) == 2:
+ left, top = right, bottom = border
+ elif len(border) == 4:
+ left, top, right, bottom = border
+ else:
+ left = top = right = bottom = border
+ return left, top, right, bottom
+
+def _color(color, mode):
+ if Image.isStringType(color):
+ import ImageColor
+ color = ImageColor.getcolor(color, mode)
+ return color
+
+def _lut(image, lut):
+ if image.mode == "P":
+ # FIXME: apply to lookup table, not image data
+ raise NotImplementedError("mode P support coming soon")
+ elif image.mode in ("L", "RGB"):
+ if image.mode == "RGB" and len(lut) == 256:
+ lut = lut + lut + lut
+ return image.point(lut)
+ else:
+ 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"
+ histogram = image.histogram()
+ lut = []
+ for layer in range(0, len(histogram), 256):
+ h = histogram[layer:layer+256]
+ if ignore is not None:
+ # get rid of outliers
+ try:
+ h[ignore] = 0
+ except TypeError:
+ # assume sequence
+ for ix in ignore:
+ h[ix] = 0
+ if cutoff:
+ # cut off pixels from both ends of the histogram
+ # get number of pixels
+ n = 0
+ for ix in range(256):
+ n = n + h[ix]
+ # remove cutoff% pixels from the low end
+ cut = n * cutoff / 100
+ for lo in range(256):
+ if cut > h[lo]:
+ cut = cut - h[lo]
+ h[lo] = 0
+ else:
+ h[lo] = h[lo] - cut
+ cut = 0
+ if cut <= 0:
+ break
+ # remove cutoff% samples from the hi end
+ cut = n * cutoff / 100
+ for hi in range(255, -1, -1):
+ if cut > h[hi]:
+ cut = cut - h[hi]
+ h[hi] = 0
+ else:
+ h[hi] = h[hi] - cut
+ cut = 0
+ if cut <= 0:
+ break
+ # find lowest/highest samples after preprocessing
+ for lo in range(256):
+ if h[lo]:
+ break
+ for hi in range(255, -1, -1):
+ if h[hi]:
+ break
+ if hi <= lo:
+ # don't bother
+ lut.extend(range(256))
+ else:
+ scale = 255.0 / (hi - lo)
+ offset = -lo * scale
+ for ix in range(256):
+ ix = int(ix * scale + offset)
+ if ix < 0:
+ ix = 0
+ elif ix > 255:
+ ix = 255
+ 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"
+ 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)
+ 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"
+ 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"
+ 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"
+ 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])
+ if len(histo) <= 1:
+ lut.extend(range(256))
+ else:
+ step = (reduce(operator.add, histo) - histo[-1]) / 255
+ if not step:
+ lut.extend(range(256))
+ else:
+ n = step / 2
+ for i in range(256):
+ 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 image"
+ left, top, right, bottom = _border(border)
+ width = left + image.size[0] + right
+ height = top + image.size[1] + bottom
+ out = Image.new(image.mode, (width, height), _color(fill, image.mode))
+ 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.
+ """
+
+ # by Kevin Cazabon, Feb 17/2000
+ # kevin@cazabon.com
+ # http://www.cazabon.com
+
+ # ensure inputs are valid
+ if type(centering) != type([]):
+ centering = [centering[0], centering[1]]
+
+ if centering[0] > 1.0 or centering[0] < 0.0:
+ centering [0] = 0.50
+ if centering[1] > 1.0 or centering[1] < 0.0:
+ centering[1] = 0.50
+
+ if bleed > 0.49999 or bleed < 0.0:
+ bleed = 0.0
+
+ # calculate the area to use for resizing and cropping, subtracting
+ # the 'bleed' around the edges
+
+ # number of pixels to trim off on Top and Bottom, Left and Right
+ bleedPixels = (
+ int((float(bleed) * float(image.size[0])) + 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
+ )
+
+ liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1])
+
+ # calculate the aspect ratio of the liveArea
+ liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1])
+
+ # calculate the aspect ratio of the output image
+ aspectRatio = float(size[0]) / float(size[1])
+
+ # figure out if the sides or top/bottom will be cropped off
+ if liveAreaAspectRatio >= aspectRatio:
+ # liveArea is wider than what's needed, crop the sides
+ cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5)
+ cropHeight = liveSize[1]
+ else:
+ # liveArea is taller than what's needed, crop the top and bottom
+ cropWidth = liveSize[0]
+ cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5)
+
+ # make the crop
+ leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0]))
+ if leftSide < 0:
+ leftSide = 0
+ topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1]))
+ if topSide < 0:
+ topSide = 0
+
+ out = image.crop(
+ (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight)
+ )
+
+ # 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"
+ 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"
+ return image.convert("L")
+
+##
+# Invert (negate) the image.
+#
+# @param image The image to invert.
+# @return An image.
+
+def invert(image):
+ "Invert image (negate)"
+ 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"
+ 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"
+ 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"
+ lut = []
+ for i in range(256):
+ if i < threshold:
+ lut.append(i)
+ else:
+ lut.append(255-i)
+ return _lut(image, lut)
+
+# --------------------------------------------------------------------
+# PIL USM components, from Kevin Cazabon.
+
+def gaussian_blur(im, radius=None):
+ """ PIL_usm.gblur(im, [radius])"""
+
+ if radius is None:
+ radius = 5.0
+
+ im.load()
+
+ return im.im.gaussian_blur(radius)
+
+gblur = gaussian_blur
+
+def unsharp_mask(im, radius=None, percent=None, threshold=None):
+ """ PIL_usm.usm(im, [radius, percent, threshold])"""
+
+ if radius is None:
+ radius = 5.0
+ if percent is None:
+ percent = 150
+ if threshold is None:
+ threshold = 3
+
+ im.load()
+
+ return im.im.unsharp_mask(radius, percent, threshold)
+
+usm = unsharp_mask
diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py
new file mode 100644
index 000000000..6efee2998
--- /dev/null
+++ b/PIL/ImagePalette.py
@@ -0,0 +1,184 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# image palette object
+#
+# History:
+# 1996-03-11 fl Rewritten.
+# 1997-01-03 fl Up and running.
+# 1997-08-23 fl Added load hack
+# 2001-04-16 fl Fixed randint shadow bug in random()
+#
+# Copyright (c) 1997-2001 by Secret Labs AB
+# Copyright (c) 1996-1997 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import array
+import Image, ImageColor
+
+##
+# Colour palette wrapper for palette mapped images.
+
+class ImagePalette:
+ "Colour 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.colors = {}
+ self.dirty = None
+ if len(self.mode)*256 != len(self.palette):
+ raise ValueError, "wrong palette size"
+
+ def getdata(self):
+ # experimental: get palette contents in format suitable
+ # for the low-level im.putpalette primitive
+ if self.rawmode:
+ return self.rawmode, self.palette
+ return self.mode + ";L", self.tostring()
+
+ def tostring(self):
+ # experimental: convert palette to string
+ if self.rawmode:
+ raise ValueError("palette contains raw palette data")
+ if Image.isStringType(self.palette):
+ return self.palette
+ return array.array("B", self.palette).tostring()
+
+ def getcolor(self, color):
+ # experimental: given an rgb tuple, allocate palette entry
+ if self.rawmode:
+ raise ValueError("palette contains raw palette data")
+ if Image.isTupleType(color):
+ try:
+ return self.colors[color]
+ except KeyError:
+ # allocate new color slot
+ if Image.isStringType(self.palette):
+ self.palette = map(int, self.palette)
+ index = len(self.colors)
+ if index >= 256:
+ raise ValueError("cannot allocate more than 256 colors")
+ self.colors[color] = index
+ self.palette[index] = color[0]
+ self.palette[index+256] = color[1]
+ self.palette[index+512] = color[2]
+ self.dirty = 1
+ return index
+ else:
+ raise ValueError("unknown color specifier: %r" % color)
+
+ def save(self, fp):
+ # (experimental) save palette to text file
+ if self.rawmode:
+ raise ValueError("palette contains raw palette data")
+ if type(fp) == type(""):
+ fp = open(fp, "w")
+ fp.write("# Palette\n")
+ fp.write("# Mode: %s\n" % self.mode)
+ for i in range(256):
+ fp.write("%d" % i)
+ for j in range(i, len(self.palette), 256):
+ fp.write(" %d" % self.palette[j])
+ fp.write("\n")
+ fp.close()
+
+# --------------------------------------------------------------------
+# Internal
+
+def raw(rawmode, data):
+ palette = ImagePalette()
+ palette.rawmode = rawmode
+ palette.palette = data
+ palette.dirty = 1
+ return palette
+
+# --------------------------------------------------------------------
+# Factories
+
+def _make_linear_lut(black, white):
+ lut = []
+ if black == 0:
+ for i in range(256):
+ lut.append(white*i/255)
+ else:
+ raise NotImplementedError # FIXME
+ return lut
+
+def _make_gamma_lut(exp, mode="RGB"):
+ lut = []
+ for i in range(256):
+ lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5))
+ return lut
+
+def new(mode, data):
+ return Image.core.new_palette(mode, data)
+
+def negative(mode="RGB"):
+ palette = range(256)
+ palette.reverse()
+ return ImagePalette(mode, palette * len(mode))
+
+def random(mode="RGB"):
+ from random import randint
+ palette = []
+ for i in range(256*len(mode)):
+ palette.append(randint(0, 255))
+ return ImagePalette(mode, palette)
+
+def sepia(white="#fff0c0"):
+ r, g, b = ImageColor.getrgb(white)
+ r = _make_linear_lut(0, r)
+ g = _make_linear_lut(0, g)
+ b = _make_linear_lut(0, b)
+ return ImagePalette("RGB", r + g + b)
+
+def wedge(mode="RGB"):
+ return ImagePalette(mode, range(256) * len(mode))
+
+def load(filename):
+
+ # FIXME: supports GIMP gradients only
+
+ fp = open(filename, "rb")
+
+ lut = None
+
+ if not lut:
+ try:
+ import GimpPaletteFile
+ fp.seek(0)
+ p = GimpPaletteFile.GimpPaletteFile(fp)
+ lut = p.getpalette()
+ except (SyntaxError, ValueError):
+ pass
+
+ if not lut:
+ try:
+ import GimpGradientFile
+ fp.seek(0)
+ p = GimpGradientFile.GimpGradientFile(fp)
+ lut = p.getpalette()
+ except (SyntaxError, ValueError):
+ pass
+
+ if not lut:
+ try:
+ import PaletteFile
+ fp.seek(0)
+ p = PaletteFile.PaletteFile(fp)
+ lut = p.getpalette()
+ except (SyntaxError, ValueError):
+ pass
+
+ if not lut:
+ 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
new file mode 100644
index 000000000..721fd9449
--- /dev/null
+++ b/PIL/ImagePath.py
@@ -0,0 +1,71 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# path interface
+#
+# History:
+# 1996-11-04 fl Created
+# 2002-04-14 fl Added documentation stub class
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+
+##
+# 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
+
+ ##
+ # Compacts the path, by removing points that are close to each
+ # other. This method modifies the path in place.
+
+ def compact(self, distance=2):
+ pass
+
+ ##
+ # Gets the bounding box.
+
+ def getbbox(self):
+ pass
+
+ ##
+ # Maps the path through a function.
+
+ def map(self, function):
+ pass
+
+ ##
+ # Converts the path to Python list.
+ #
+ # @param flat By default, this function returns a list of 2-tuples
+ # [(x, y), ...]. If this argument is true, it returns a flat
+ # list [x, y, ...] instead.
+ # @return A list of coordinates.
+
+ def tolist(self, flat=0):
+ pass
+
+ ##
+ # Transforms the path.
+
+ def transform(self, matrix):
+ pass
+
+
+# override with C implementation
+Path = Image.core.path
diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py
new file mode 100644
index 000000000..50ee07d6c
--- /dev/null
+++ b/PIL/ImageQt.py
@@ -0,0 +1,84 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# a simple Qt image interface.
+#
+# history:
+# 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
+#
+# Copyright (c) 2006 by Secret Labs AB
+# Copyright (c) 2006 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+
+from PyQt4.QtGui import QImage, qRgb
+
+##
+# (Internal) Turns an RGB color into a Qt compatible color integer.
+
+def rgb(r, g, b):
+ # 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
+
+##
+# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage
+# class.
+#
+# @param im A PIL Image object, or a file name (given either as Python
+# string or a PyQt string object).
+
+class ImageQt(QImage):
+
+ def __init__(self, im):
+
+ data = None
+ colortable = None
+
+ # handle filename, if given instead of image name
+ if hasattr(im, "toUtf8"):
+ # FIXME - is this really the best way to do this?
+ im = unicode(im.toUtf8(), "utf-8")
+ if Image.isStringType(im):
+ im = Image.open(im)
+
+ if im.mode == "1":
+ format = QImage.Format_Mono
+ elif im.mode == "L":
+ format = QImage.Format_Indexed8
+ colortable = []
+ for i in range(256):
+ colortable.append(rgb(i, i, i))
+ elif im.mode == "P":
+ format = QImage.Format_Indexed8
+ colortable = []
+ palette = im.getpalette()
+ for i in range(0, len(palette), 3):
+ colortable.append(rgb(*palette[i:i+3]))
+ elif im.mode == "RGB":
+ data = im.tostring("raw", "BGRX")
+ format = QImage.Format_RGB32
+ elif im.mode == "RGBA":
+ try:
+ data = im.tostring("raw", "BGRA")
+ except SystemError:
+ # workaround for earlier versions
+ r, g, b, a = im.split()
+ im = Image.merge("RGBA", (b, g, r, a))
+ format = QImage.Format_ARGB32
+ else:
+ raise ValueError("unsupported image mode %r" % im.mode)
+
+ # must keep a reference, or Qt will crash!
+ self.__data = data or im.tostring()
+
+ QImage.__init__(self, self.__data, im.size[0], im.size[1], format)
+
+ if colortable:
+ self.setColorTable(colortable)
diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py
new file mode 100644
index 000000000..e94ca0b1e
--- /dev/null
+++ b/PIL/ImageSequence.py
@@ -0,0 +1,38 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# sequence support classes
+#
+# history:
+# 1997-02-20 fl Created
+#
+# Copyright (c) 1997 by Secret Labs AB.
+# Copyright (c) 1997 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+##
+# This class implements an iterator object that can be used to loop
+# over an image sequence.
+
+class Iterator:
+
+ ##
+ # Create an iterator.
+ #
+ # @param im An image object.
+
+ def __init__(self, im):
+ if not hasattr(im, "seek"):
+ raise AttributeError("im must have seek method")
+ self.im = im
+
+ def __getitem__(self, ix):
+ try:
+ if ix:
+ self.im.seek(ix)
+ return self.im
+ except EOFError:
+ raise IndexError # end of sequence
diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py
new file mode 100644
index 000000000..05270f440
--- /dev/null
+++ b/PIL/ImageShow.py
@@ -0,0 +1,163 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# im.show() drivers
+#
+# History:
+# 2008-04-06 fl Created
+#
+# Copyright (c) Secret Labs AB 2008.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import os, sys
+
+_viewers = []
+
+def register(viewer, order=1):
+ try:
+ if issubclass(viewer, Viewer):
+ viewer = viewer()
+ except TypeError:
+ pass # raised if viewer wasn't a class
+ if order > 0:
+ _viewers.append(viewer)
+ elif order < 0:
+ _viewers.insert(0, viewer)
+
+##
+# Displays a given image.
+#
+# @param image An image object.
+# @param title Optional title. Not all viewers can display the title.
+# @param **options Additional viewer options.
+# @return True if a suitable viewer was found, false otherwise.
+
+def show(image, title=None, **options):
+ for viewer in _viewers:
+ if viewer.show(image, title=title, **options):
+ return 1
+ return 0
+
+##
+# Base class for viewers.
+
+class Viewer:
+
+ # main api
+
+ def show(self, image, **options):
+
+ # save temporary image to disk
+ if image.mode[:4] == "I;16":
+ # @PIL88 @PIL101
+ # "I;16" isn't an 'official' mode, but we still want to
+ # provide a simple way to show 16-bit images.
+ base = "L"
+ # FIXME: auto-contrast if max() > 255?
+ else:
+ base = Image.getmodebase(image.mode)
+ if base != image.mode and image.mode != "1":
+ image = image.convert(base)
+
+ self.show_image(image, **options)
+
+ # hook methods
+
+ format = None
+
+ def get_format(self, image):
+ # return format name, or None to save as PGM/PPM
+ return self.format
+
+ def get_command(self, file, **options):
+ raise NotImplementedError
+
+ def save_image(self, image):
+ # save to temporary file, and return filename
+ return image._dump(format=self.get_format(image))
+
+ def show_image(self, image, **options):
+ # display given image
+ return self.show_file(self.save_image(image), **options)
+
+ def show_file(self, file, **options):
+ # display given file
+ os.system(self.get_command(file, **options))
+ return 1
+
+# --------------------------------------------------------------------
+
+if sys.platform == "win32":
+
+ class WindowsViewer(Viewer):
+ format = "BMP"
+ def get_command(self, file, **options):
+ return "start /wait %s && del /f %s" % (file, file)
+
+ register(WindowsViewer)
+
+elif sys.platform == "darwin":
+
+ class MacViewer(Viewer):
+ format = "BMP"
+ def get_command(self, file, **options):
+ # 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)
+ return command
+
+ register(MacViewer)
+
+else:
+
+ # unixoids
+
+ def which(executable):
+ path = os.environ.get("PATH")
+ if not path:
+ return None
+ for dirname in path.split(os.pathsep):
+ filename = os.path.join(dirname, executable)
+ if os.path.isfile(filename):
+ # FIXME: make sure it's executable
+ return filename
+ return None
+
+ 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)
+ os.system(command)
+ return 1
+
+ # implementations
+
+ class DisplayViewer(UnixViewer):
+ def get_command_ex(self, file, **options):
+ command = executable = "display"
+ return command, executable
+
+ if which("display"):
+ register(DisplayViewer)
+
+ class XVViewer(UnixViewer):
+ def get_command_ex(self, file, title=None, **options):
+ # note: xv is pretty outdated. most modern systems have
+ # imagemagick's display command instead.
+ command = executable = "xv"
+ if title:
+ # FIXME: do full escaping
+ command = command + " -name \"%s\"" % title
+ return command, executable
+
+ if which("xv"):
+ register(XVViewer)
+
+if __name__ == "__main__":
+ # usage: python ImageShow.py imagefile [title]
+ print show(Image.open(sys.argv[1]), *sys.argv[2:])
diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py
new file mode 100644
index 000000000..9ebdab030
--- /dev/null
+++ b/PIL/ImageStat.py
@@ -0,0 +1,164 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# global image statistics
+#
+# History:
+# 1996-04-05 fl Created
+# 1997-05-21 fl Added mask; added rms, var, stddev attributes
+# 1997-08-05 fl Added median
+# 1998-07-05 hk Fixed integer overflow error
+#
+# Notes:
+# This class shows how to implement delayed evaluation of attributes.
+# To get a certain value, simply access the corresponding attribute.
+# The __getattr__ dispatcher takes care of the rest.
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1996-97.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import operator, math
+
+##
+# 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:
+ if mask:
+ self.h = image_or_list.histogram(mask)
+ else:
+ 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)
+
+ def __getattr__(self, id):
+ "Calculate missing attribute"
+ if id[:4] == "_get":
+ raise AttributeError, id
+ # calculate missing attribute
+ v = getattr(self, "_get" + id)()
+ setattr(self, id, v)
+ return v
+
+ def _getextrema(self):
+ "Get min/max values for each band in the image"
+
+ def minmax(histogram):
+ n = 255
+ x = 0
+ for i in range(256):
+ if histogram[i]:
+ n = min(n, i)
+ x = max(x, i)
+ return n, x # returns (255, 0) if there's no data in the histogram
+
+ v = []
+ for i in range(0, len(self.h), 256):
+ v.append(minmax(self.h[i:]))
+ return v
+
+ def _getcount(self):
+ "Get total number of pixels in each layer"
+
+ v = []
+ for i in range(0, len(self.h), 256):
+ v.append(reduce(operator.add, self.h[i:i+256]))
+ return v
+
+ def _getsum(self):
+ "Get sum of all pixels in each layer"
+
+ v = []
+ for i in range(0, len(self.h), 256):
+ sum = 0.0
+ for j in range(256):
+ sum = sum + j * self.h[i+j]
+ v.append(sum)
+ return v
+
+ def _getsum2(self):
+ "Get squared sum of all pixels in each layer"
+
+ v = []
+ for i in range(0, len(self.h), 256):
+ sum2 = 0.0
+ for j in range(256):
+ sum2 = sum2 + (j ** 2) * float(self.h[i+j])
+ v.append(sum2)
+ return v
+
+ def _getmean(self):
+ "Get average pixel level for each layer"
+
+ v = []
+ for i in self.bands:
+ v.append(self.sum[i] / self.count[i])
+ return v
+
+ def _getmedian(self):
+ "Get median pixel level for each layer"
+
+ v = []
+ for i in self.bands:
+ s = 0
+ l = self.count[i]/2
+ b = i * 256
+ for j in range(256):
+ s = s + self.h[b+j]
+ if s > l:
+ break
+ v.append(j)
+ return v
+
+ def _getrms(self):
+ "Get RMS for each layer"
+
+ v = []
+ for i in self.bands:
+ v.append(math.sqrt(self.sum2[i] / self.count[i]))
+ return v
+
+
+ def _getvar(self):
+ "Get variance for each layer"
+
+ v = []
+ for i in self.bands:
+ n = self.count[i]
+ v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n)
+ return v
+
+ def _getstddev(self):
+ "Get standard deviation for each layer"
+
+ v = []
+ for i in self.bands:
+ v.append(math.sqrt(self.var[i]))
+ return v
+
+Global = Stat # compatibility
diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py
new file mode 100644
index 000000000..8618139a5
--- /dev/null
+++ b/PIL/ImageTk.py
@@ -0,0 +1,296 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# a Tk display interface
+#
+# History:
+# 96-04-08 fl Created
+# 96-09-06 fl Added getimage method
+# 96-11-01 fl Rewritten, removed image attribute and crop method
+# 97-05-09 fl Use PyImagingPaste method instead of image type
+# 97-05-12 fl Minor tweaks to match the IFUNC95 interface
+# 97-05-17 fl Support the "pilbitmap" booster patch
+# 97-06-05 fl Added file= and data= argument to image constructors
+# 98-03-09 fl Added width and height methods to Image classes
+# 98-07-02 fl Use default mode for "P" images without palette attribute
+# 98-07-02 fl Explicitly destroy Tkinter image objects
+# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch)
+# 99-07-26 fl Automatically hook into Tkinter (if possible)
+# 99-08-15 fl Hook uses _imagingtk instead of _imaging
+#
+# Copyright (c) 1997-1999 by Secret Labs AB
+# Copyright (c) 1996-1997 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Tkinter, 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
+
+_pilbitmap_ok = None
+
+def _pilbitmap_check():
+ global _pilbitmap_ok
+ if _pilbitmap_ok is None:
+ try:
+ im = Image.new("1", (1,1))
+ Tkinter.BitmapImage(data="PIL:%d" % im.im.id)
+ _pilbitmap_ok = 1
+ 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:
+
+ ##
+ # 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).
+
+ def __init__(self, image=None, size=None, **kw):
+
+ # Tk compatibility: file or data
+ if image is None:
+ if kw.has_key("file"):
+ image = Image.open(kw["file"])
+ del kw["file"]
+ elif kw.has_key("data"):
+ from StringIO import StringIO
+ image = Image.open(StringIO(kw["data"]))
+ del kw["data"]
+
+ if hasattr(image, "mode") and hasattr(image, "size"):
+ # got an image instead of a mode
+ mode = image.mode
+ if mode == "P":
+ # palette mapped data
+ image.load()
+ try:
+ mode = image.palette.mode
+ except AttributeError:
+ mode = "RGB" # default
+ size = image.size
+ kw["width"], kw["height"] = size
+ else:
+ mode = image
+ image = None
+
+ if mode not in ["1", "L", "RGB", "RGBA"]:
+ mode = Image.getmodebase(mode)
+
+ self.__mode = mode
+ self.__size = size
+ self.__photo = apply(Tkinter.PhotoImage, (), kw)
+ self.tk = self.__photo.tk
+ if image:
+ self.paste(image)
+
+ def __del__(self):
+ name = self.__photo.name
+ self.__photo.name = None
+ try:
+ self.__photo.tk.call("image", "delete", name)
+ 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):
+ return str(self.__photo)
+
+ ##
+ # Get the width of the image.
+ #
+ # @return The width, in pixels.
+
+ def width(self):
+ return self.__size[0]
+
+ ##
+ # Get the height of the image.
+ #
+ # @return The height, in pixels.
+
+ def height(self):
+ 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):
+
+ # convert to blittable
+ im.load()
+ image = im.im
+ if image.isblock() and im.mode == self.__mode:
+ block = image
+ else:
+ block = image.new_block(self.__mode, im.size)
+ image.convert2(block, image) # convert directly between buffers
+
+ tk = self.__photo.tk
+
+ try:
+ tk.call("PyImagingPhoto", self.__photo, block.id)
+ except Tkinter.TclError, v:
+ # activate Tkinter hook
+ try:
+ 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):
+ 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.
+
+ def __init__(self, image=None, **kw):
+
+ # Tk compatibility: file or data
+ if image is None:
+ if kw.has_key("file"):
+ image = Image.open(kw["file"])
+ del kw["file"]
+ elif kw.has_key("data"):
+ from StringIO import StringIO
+ image = Image.open(StringIO(kw["data"]))
+ del kw["data"]
+
+ self.__mode = image.mode
+ self.__size = image.size
+
+ if _pilbitmap_check():
+ # fast way (requires the pilbitmap booster patch)
+ image.load()
+ kw["data"] = "PIL:%d" % image.im.id
+ self.__im = image # must keep a reference
+ else:
+ # slow but safe way
+ kw["data"] = image.tobitmap()
+ self.__photo = apply(Tkinter.BitmapImage, (), kw)
+
+ def __del__(self):
+ name = self.__photo.name
+ self.__photo.name = None
+ try:
+ self.__photo.tk.call("image", "delete", name)
+ except:
+ pass # ignore internal errors
+
+ ##
+ # Get the width of the image.
+ #
+ # @return The width, in pixels.
+
+ def width(self):
+ return self.__size[0]
+
+ ##
+ # Get the height of the image.
+ #
+ # @return The height, in pixels.
+
+ def height(self):
+ 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):
+ return str(self.__photo)
+
+##
+# Copies the contents of a PhotoImage to a PIL image memory.
+
+def getimage(photo):
+ photo.tk.call("PyImagingPhotoGet", photo)
+
+# --------------------------------------------------------------------
+# Helper for the Image.show method.
+
+def _show(image, title):
+
+ 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,
+ bg="black", bd=0)
+
+ 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
new file mode 100644
index 000000000..cc323d3b9
--- /dev/null
+++ b/PIL/ImageTransform.py
@@ -0,0 +1,95 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# transform wrappers
+#
+# History:
+# 2002-04-08 fl Created
+#
+# Copyright (c) 2002 by Secret Labs AB
+# Copyright (c) 2002 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+
+class Transform(Image.ImageTransformHandler):
+ def __init__(self, data):
+ self.data = data
+ def getdata(self):
+ return self.method, self.data
+ def transform(self, size, image, **options):
+ # can be overridden
+ method, data = self.getdata()
+ return image.transform(size, method, data, **options)
+
+##
+# Define an affine image transform.
+#
+# This function takes a 6-tuple (a, b, c, d, e, f) which
+# contain the first two rows from an affine transform matrix. For
+# each pixel (x, y) in the output image, the new value is
+# taken from a position (a x + b y + c,
+# d x + e y + f) in the input image, rounded to
+# nearest pixel.
+#
+# This function can be used to scale, translate, rotate, and shear the
+# original image.
+#
+# @def AffineTransform(matrix)
+# @param matrix A 6-tuple (a, b, c, d, e, f) containing
+# the first two rows from an affine transform matrix.
+# @see Image#Image.transform
+
+class AffineTransform(Transform):
+ method = Image.AFFINE
+
+##
+# Define a transform to extract a subregion from an image.
+#
+# Maps a rectangle (defined by two corners) from the image to a
+# rectangle of the given size. The resulting image will contain
+# data sampled from between the corners, such that (x0, y0)
+# in the input image will end up at (0,0) in the output image,
+# and (x1, y1) at size.
+#
+# This method can be used to crop, stretch, shrink, or mirror an
+# arbitrary rectangle in the current image. It is slightly slower than
+# crop, but about as fast as a corresponding resize
+# operation.
+#
+# @def ExtentTransform(bbox)
+# @param bbox A 4-tuple (x0, y0, x1, y1) which specifies
+# two points in the input image's coordinate system.
+# @see Image#Image.transform
+
+class ExtentTransform(Transform):
+ method = Image.EXTENT
+
+##
+# Define an quad image transform.
+#
+# Maps a quadrilateral (a region defined by four corners) from the
+# image to a rectangle of the given size.
+#
+# @def QuadTransform(xy)
+# @param xy An 8-tuple (x0, y0, x1, y1, x2, y2, y3, y3) which
+# contain the upper left, lower left, lower right, and upper right
+# corner of the source quadrilateral.
+# @see Image#Image.transform
+
+class QuadTransform(Transform):
+ method = Image.QUAD
+
+##
+# Define an mesh image transform. A mesh transform consists of one
+# or more individual quad transforms.
+#
+# @def MeshTransform(data)
+# @param data A list of (bbox, quad) tuples.
+# @see Image#Image.transform
+
+class MeshTransform(Transform):
+ method = Image.MESH
diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py
new file mode 100644
index 000000000..f98725f74
--- /dev/null
+++ b/PIL/ImageWin.py
@@ -0,0 +1,215 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# a Windows DIB display interface
+#
+# History:
+# 1996-05-20 fl Created
+# 1996-09-20 fl Fixed subregion exposure
+# 1997-09-21 fl Added draw primitive (for tzPrint)
+# 2003-05-21 fl Added experimental Window/ImageWindow classes
+# 2003-09-05 fl Added fromstring/tostring methods
+#
+# Copyright (c) Secret Labs AB 1997-2003.
+# Copyright (c) Fredrik Lundh 1996-2003.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+
+##
+# The ImageWin module contains support to create and display
+# images under Windows 95/98, NT, 2000 and later.
+
+class HDC:
+ def __init__(self, dc):
+ self.dc = dc
+ def __int__(self):
+ return self.dc
+
+class HWND:
+ 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:
+
+ ##
+ # 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.
+
+ def __init__(self, image, size=None):
+ if hasattr(image, "mode") and hasattr(image, "size"):
+ mode = image.mode
+ size = image.size
+ else:
+ mode = image
+ image = None
+ if mode not in ["1", "L", "P", "RGB"]:
+ mode = Image.getmodebase(mode)
+ self.image = Image.core.display(mode, size)
+ self.mode = mode
+ self.size = size
+ 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):
+ if isinstance(handle, HWND):
+ dc = self.image.getdc(handle)
+ try:
+ result = self.image.expose(dc)
+ finally:
+ self.image.releasedc(handle, dc)
+ else:
+ result = self.image.expose(handle)
+ return result
+
+ def draw(self, handle, dst, src=None):
+ if not src:
+ src = (0,0) + self.size
+ if isinstance(handle, HWND):
+ dc = self.image.getdc(handle)
+ try:
+ result = self.image.draw(dc, dst, src)
+ finally:
+ self.image.releasedc(handle, dc)
+ else:
+ 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):
+ if isinstance(handle, HWND):
+ handle = self.image.getdc(handle)
+ try:
+ result = self.image.query_palette(handle)
+ finally:
+ self.image.releasedc(handle, handle)
+ else:
+ 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):
+ im.load()
+ if self.mode != im.mode:
+ im = im.convert(self.mode)
+ if box:
+ self.image.paste(im.im, box)
+ 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)
+
+ ##
+ # Copy display memory contents to string buffer.
+ #
+ # @return A string buffer containing display data.
+
+ def tostring(self):
+ return self.image.tostring()
+
+
+##
+# Create a Window with the given title size.
+
+class Window:
+
+ def __init__(self, title="PIL", width=None, height=None):
+ self.hwnd = Image.core.createwindow(
+ title, self.__dispatcher, width or 0, height or 0
+ )
+
+ def __dispatcher(self, action, *args):
+ return apply(getattr(self, "ui_handle_" + action), args)
+
+ def ui_handle_clear(self, dc, x0, y0, x1, y1):
+ pass
+
+ def ui_handle_damage(self, x0, y0, x1, y1):
+ pass
+
+ def ui_handle_destroy(self):
+ pass
+
+ def ui_handle_repair(self, dc, x0, y0, x1, y1):
+ pass
+
+ def ui_handle_resize(self, width, height):
+ pass
+
+ def mainloop(self):
+ Image.core.eventloop()
+
+##
+# Create an image window which displays the given image.
+
+class ImageWindow(Window):
+
+ def __init__(self, image, title="PIL"):
+ if not isinstance(image, Dib):
+ image = Dib(image)
+ self.image = image
+ width, height = image.size
+ Window.__init__(self, title, width=width, height=height)
+
+ def ui_handle_repair(self, dc, x0, y0, x1, y1):
+ self.image.draw(dc, (x0, y0, x1, y1))
diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py
new file mode 100644
index 000000000..bf5611b8a
--- /dev/null
+++ b/PIL/ImtImagePlugin.py
@@ -0,0 +1,93 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# IM Tools support for PIL
+#
+# history:
+# 1996-05-27 fl Created (read 8-bit images only)
+# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2)
+#
+# Copyright (c) Secret Labs AB 1997-2001.
+# Copyright (c) Fredrik Lundh 1996-2001.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.2"
+
+import re
+
+import Image, ImageFile
+
+#
+# --------------------------------------------------------------------
+
+field = re.compile(r"([a-z]*) ([^ \r\n]*)")
+
+##
+# Image plugin for IM Tools images.
+
+class ImtImageFile(ImageFile.ImageFile):
+
+ format = "IMT"
+ format_description = "IM Tools"
+
+ def _open(self):
+
+ # 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"
+ self.fp.seek(0)
+
+ xsize = ysize = 0
+
+ while 1:
+
+ s = self.fp.read(1)
+ if not s:
+ break
+
+ if s == chr(12):
+
+ # image data begins
+ self.tile = [("raw", (0,0)+self.size,
+ self.fp.tell(),
+ (self.mode, 0, 1))]
+
+ break
+
+ else:
+
+ # read key/value pair
+ # FIXME: dangerous, may read whole file
+ s = s + self.fp.readline()
+ if len(s) == 1 or len(s) > 100:
+ break
+ if s[0] == "*":
+ continue # comment
+
+ m = field.match(s)
+ if not m:
+ break
+ k, v = m.group(1,2)
+ if k == "width":
+ xsize = int(v)
+ self.size = xsize, ysize
+ elif k == "height":
+ ysize = int(v)
+ self.size = xsize, ysize
+ elif k == "pixel" and v == "n8":
+ self.mode = "L"
+
+
+#
+# --------------------------------------------------------------------
+
+Image.register_open("IMT", ImtImageFile)
+
+#
+# no extension registered (".im" is simply too common)
diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py
new file mode 100644
index 000000000..acea5d18b
--- /dev/null
+++ b/PIL/IptcImagePlugin.py
@@ -0,0 +1,288 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# IPTC/NAA file handling
+#
+# history:
+# 1995-10-01 fl Created
+# 1998-03-09 fl Cleaned up and added to PIL
+# 2002-06-18 fl Added getiptcinfo helper
+#
+# Copyright (c) Secret Labs AB 1997-2002.
+# Copyright (c) Fredrik Lundh 1995.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.3"
+
+
+import Image, ImageFile
+import os, tempfile
+
+
+COMPRESSION = {
+ 1: "raw",
+ 5: "jpeg"
+}
+
+PAD = chr(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
+
+##
+# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields
+# from TIFF and JPEG files, use the getiptcinfo function.
+
+class IptcImageFile(ImageFile.ImageFile):
+
+ format = "IPTC"
+ format_description = "IPTC/NAA"
+
+ def getint(self, key):
+ return i(self.info[key])
+
+ def field(self):
+ #
+ # get a IPTC field header
+ s = self.fp.read(5)
+ if not len(s):
+ return None, 0
+
+ tag = ord(s[1]), ord(s[2])
+
+ # syntax
+ if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9:
+ raise SyntaxError, "invalid IPTC/NAA file"
+
+ # field size
+ size = ord(s[3])
+ if size > 132:
+ raise IOError, "illegal field length in IPTC/NAA file"
+ elif size == 128:
+ size = 0
+ elif size > 128:
+ size = i(self.fp.read(size-128))
+ else:
+ size = i16(s[3:])
+
+ return tag, size
+
+ def _is_raw(self, offset, size):
+ #
+ # check if the file can be mapped
+
+ # DISABLED: the following only slows things down...
+ return 0
+
+ self.fp.seek(offset)
+ t, sz = self.field()
+ if sz != size[0]:
+ return 0
+ y = 1
+ while 1:
+ self.fp.seek(sz, 1)
+ t, s = self.field()
+ if t != (8, 10):
+ break
+ if s != sz:
+ return 0
+ y = y + 1
+ return y == size[1]
+
+ def _open(self):
+
+ # load descriptive fields
+ while 1:
+ offset = self.fp.tell()
+ tag, size = self.field()
+ if not tag or tag == (8,10):
+ break
+ if size:
+ tagdata = self.fp.read(size)
+ else:
+ tagdata = None
+ if tag in self.info.keys():
+ if isinstance(self.info[tag], list):
+ self.info[tag].append(tagdata)
+ else:
+ self.info[tag] = [self.info[tag], tagdata]
+ else:
+ self.info[tag] = tagdata
+
+ # 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
+ else:
+ id = 0
+ if layers == 1 and not component:
+ self.mode = "L"
+ elif layers == 3 and component:
+ self.mode = "RGB"[id]
+ elif layers == 4 and component:
+ self.mode = "CMYK"[id]
+
+ # size
+ self.size = self.getint((3,20)), self.getint((3,30))
+
+ # compression
+ try:
+ compression = COMPRESSION[self.getint((3,120))]
+ except KeyError:
+ raise IOError, "Unknown IPTC image compression"
+
+ # tile
+ if tag == (8,10):
+ if compression == "raw" and self._is_raw(offset, self.size):
+ self.tile = [(compression, (offset, size + 5, -1),
+ (0, 0, self.size[0], self.size[1]))]
+ else:
+ self.tile = [("iptc", (compression, offset),
+ (0, 0, self.size[0], self.size[1]))]
+
+ def load(self):
+
+ if len(self.tile) != 1 or self.tile[0][0] != "iptc":
+ return ImageFile.ImageFile.load(self)
+
+ type, tile, box = self.tile[0]
+
+ encoding, offset = tile
+
+ self.fp.seek(offset)
+
+ # Copy image data to temporary file
+ outfile = tempfile.mktemp()
+ o = open(outfile, "wb")
+ if encoding == "raw":
+ # To simplify access to the extracted file,
+ # prepend a PPM header
+ o.write("P5\n%d %d\n255\n" % self.size)
+ while 1:
+ type, size = self.field()
+ if type != (8, 10):
+ break
+ while size > 0:
+ s = self.fp.read(min(size, 8192))
+ if not s:
+ break
+ o.write(s)
+ size = size - len(s)
+ o.close()
+
+ try:
+ try:
+ # fast
+ self.im = Image.core.open_ppm(outfile)
+ except:
+ # slightly slower
+ im = Image.open(outfile)
+ im.load()
+ self.im = im.im
+ finally:
+ try: os.unlink(outfile)
+ except: pass
+
+
+Image.register_open("IPTC", IptcImageFile)
+
+Image.register_extension("IPTC", ".iim")
+
+##
+# Get IPTC information from TIFF, JPEG, or IPTC file.
+#
+# @param im An image containing IPTC data.
+# @return A dictionary containing IPTC information, or None if
+# no IPTC information block was found.
+
+def getiptcinfo(im):
+
+ import TiffImagePlugin, JpegImagePlugin
+ import StringIO
+
+ data = None
+
+ if isinstance(im, IptcImageFile):
+ # return info dictionary right away
+ return im.info
+
+ elif isinstance(im, JpegImagePlugin.JpegImageFile):
+ # extract the IPTC/NAA resource
+ try:
+ app = im.app["APP13"]
+ if app[:14] == "Photoshop 3.0\x00":
+ app = app[14:]
+ # parse the image resource block
+ offset = 0
+ while app[offset:offset+4] == "8BIM":
+ offset = offset + 4
+ # resource code
+ code = JpegImagePlugin.i16(app, offset)
+ offset = offset + 2
+ # resource name (usually empty)
+ name_len = ord(app[offset])
+ name = app[offset+1:offset+1+name_len]
+ offset = 1 + offset + name_len
+ if offset & 1:
+ offset = offset + 1
+ # resource data block
+ size = JpegImagePlugin.i32(app, offset)
+ offset = offset + 4
+ if code == 0x0404:
+ # 0x0404 contains IPTC/NAA data
+ data = app[offset:offset+size]
+ break
+ offset = offset + size
+ if offset & 1:
+ offset = offset + 1
+ except (AttributeError, KeyError):
+ pass
+
+ elif isinstance(im, TiffImagePlugin.TiffImageFile):
+ # 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]
+ except (AttributeError, KeyError):
+ pass
+
+ if data is None:
+ return None # no properties
+
+ # create an IptcImagePlugin object without initializing it
+ class FakeImage:
+ pass
+ im = FakeImage()
+ im.__class__ = IptcImageFile
+
+ # parse the IPTC information chunk
+ im.info = {}
+ im.fp = StringIO.StringIO(data)
+
+ try:
+ im._open()
+ except (IndexError, KeyError):
+ pass # expected failure
+
+ return im.info
diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py
new file mode 100644
index 000000000..933abf3c2
--- /dev/null
+++ b/PIL/JpegImagePlugin.py
@@ -0,0 +1,492 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# JPEG (JFIF) file handling
+#
+# See "Digital Compression and Coding of Continous-Tone Still Images,
+# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1)
+#
+# History:
+# 1995-09-09 fl Created
+# 1995-09-13 fl Added full parser
+# 1996-03-25 fl Added hack to use the IJG command line utilities
+# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug
+# 1996-05-28 fl Added draft support, JFIF version (0.1)
+# 1996-12-30 fl Added encoder options, added progression property (0.2)
+# 1997-08-27 fl Save mode 1 images as BW (0.3)
+# 1998-07-12 fl Added YCbCr to draft and save methods (0.4)
+# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1)
+# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2)
+# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3)
+# 2003-04-25 fl Added experimental EXIF decoder (0.5)
+# 2003-06-06 fl Added experimental EXIF GPSinfo decoder
+# 2003-09-13 fl Extract COM markers
+# 2009-09-06 fl Added icc_profile support (from Florian Hoech)
+# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6)
+# 2009-03-08 fl Added subsampling support (from Justin Huff).
+#
+# Copyright (c) 1997-2003 by Secret Labs AB.
+# Copyright (c) 1995-1996 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.6"
+
+import array, struct
+import string
+import Image, ImageFile
+
+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)
+
+#
+# Parser
+
+def Skip(self, marker):
+ n = i16(self.fp.read(2))-2
+ ImageFile._safe_read(self.fp, n)
+
+def APP(self, marker):
+ #
+ # Application marker. Store these in the APP dictionary.
+ # Also look for well-known application markers.
+
+ n = i16(self.fp.read(2))-2
+ s = ImageFile._safe_read(self.fp, n)
+
+ app = "APP%d" % (marker&15)
+
+ self.app[app] = s # compatibility
+ self.applist.append((app, s))
+
+ if marker == 0xFFE0 and s[:4] == "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_density = i16(s, 8), i16(s, 10)
+ except:
+ pass
+ else:
+ if jfif_unit == 1:
+ 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":
+ # extract Exif information (incomplete)
+ self.info["exif"] = s # FIXME: value will change
+ elif marker == 0xFFE2 and s[:5] == "FPXR\0":
+ # extract FlashPix information (incomplete)
+ self.info["flashpix"] = s # FIXME: value will change
+ elif marker == 0xFFE2 and s[:12] == "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
+ # one or more APP2 markers containing the following data:
+ # Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
+ # Marker sequence number 1, 2, etc (1 byte)
+ # Number of markers Total of APP2's used (1 byte)
+ # Profile data (remainder of APP2 data)
+ # Decoders should use the marker sequence numbers to
+ # 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":
+ self.info["adobe"] = i16(s, 5)
+ # extract Adobe custom properties
+ try:
+ adobe_transform = ord(s[1])
+ except:
+ pass
+ else:
+ self.info["adobe_transform"] = adobe_transform
+
+def COM(self, marker):
+ #
+ # Comment marker. Store these in the APP dictionary.
+
+ n = i16(self.fp.read(2))-2
+ s = ImageFile._safe_read(self.fp, n)
+
+ self.app["COM"] = s # compatibility
+ self.applist.append(("COM", s))
+
+def SOF(self, marker):
+ #
+ # Start of frame marker. Defines the size and mode of the
+ # image. JPEG is colour blind, so we use some simple
+ # heuristics to map the number of layers to an appropriate
+ # mode. Note that this could be made a bit brighter, by
+ # looking for JFIF and Adobe APP markers.
+
+ n = i16(self.fp.read(2))-2
+ s = ImageFile._safe_read(self.fp, n)
+ self.size = i16(s[3:]), i16(s[1:])
+
+ self.bits = ord(s[0])
+ if self.bits != 8:
+ raise SyntaxError("cannot handle %d-bit layers" % self.bits)
+
+ self.layers = ord(s[5])
+ if self.layers == 1:
+ self.mode = "L"
+ elif self.layers == 3:
+ self.mode = "RGB"
+ elif self.layers == 4:
+ self.mode = "CMYK"
+ else:
+ raise SyntaxError("cannot handle %d-layer images" % self.layers)
+
+ if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]:
+ self.info["progressive"] = self.info["progression"] = 1
+
+ if self.icclist:
+ # fixup icc profile
+ self.icclist.sort() # sort by sequence number
+ if ord(self.icclist[0][13]) == len(self.icclist):
+ profile = []
+ for p in self.icclist:
+ profile.append(p[14:])
+ icc_profile = string.join(profile, "")
+ else:
+ icc_profile = None # wrong number of fragments
+ self.info["icc_profile"] = icc_profile
+ self.icclist = None
+
+ for i in range(6, len(s), 3):
+ t = s[i:i+3]
+ # 4-tuples: id, vsamp, hsamp, qtable
+ self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2])))
+
+def DQT(self, marker):
+ #
+ # Define quantization table. Support baseline 8-bit tables
+ # only. Note that there might be more than one table in
+ # each marker.
+
+ # FIXME: The quantization tables can be used to estimate the
+ # compression quality.
+
+ n = i16(self.fp.read(2))-2
+ s = ImageFile._safe_read(self.fp, n)
+ while len(s):
+ if len(s) < 65:
+ raise SyntaxError("bad quantization table marker")
+ v = ord(s[0])
+ if v/16 == 0:
+ self.quantization[v&15] = array.array("b", s[1:65])
+ s = s[65:]
+ else:
+ return # FIXME: add code to read 16-bit tables!
+ # raise SyntaxError, "bad quantization table element size"
+
+
+#
+# JPEG marker table
+
+MARKER = {
+ 0xFFC0: ("SOF0", "Baseline DCT", SOF),
+ 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF),
+ 0xFFC2: ("SOF2", "Progressive DCT", SOF),
+ 0xFFC3: ("SOF3", "Spatial lossless", SOF),
+ 0xFFC4: ("DHT", "Define Huffman table", Skip),
+ 0xFFC5: ("SOF5", "Differential sequential DCT", SOF),
+ 0xFFC6: ("SOF6", "Differential progressive DCT", SOF),
+ 0xFFC7: ("SOF7", "Differential spatial", SOF),
+ 0xFFC8: ("JPG", "Extension", None),
+ 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF),
+ 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF),
+ 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF),
+ 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip),
+ 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF),
+ 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF),
+ 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF),
+ 0xFFD0: ("RST0", "Restart 0", None),
+ 0xFFD1: ("RST1", "Restart 1", None),
+ 0xFFD2: ("RST2", "Restart 2", None),
+ 0xFFD3: ("RST3", "Restart 3", None),
+ 0xFFD4: ("RST4", "Restart 4", None),
+ 0xFFD5: ("RST5", "Restart 5", None),
+ 0xFFD6: ("RST6", "Restart 6", None),
+ 0xFFD7: ("RST7", "Restart 7", None),
+ 0xFFD8: ("SOI", "Start of image", None),
+ 0xFFD9: ("EOI", "End of image", None),
+ 0xFFDA: ("SOS", "Start of scan", Skip),
+ 0xFFDB: ("DQT", "Define quantization table", DQT),
+ 0xFFDC: ("DNL", "Define number of lines", Skip),
+ 0xFFDD: ("DRI", "Define restart interval", Skip),
+ 0xFFDE: ("DHP", "Define hierarchical progression", SOF),
+ 0xFFDF: ("EXP", "Expand reference component", Skip),
+ 0xFFE0: ("APP0", "Application segment 0", APP),
+ 0xFFE1: ("APP1", "Application segment 1", APP),
+ 0xFFE2: ("APP2", "Application segment 2", APP),
+ 0xFFE3: ("APP3", "Application segment 3", APP),
+ 0xFFE4: ("APP4", "Application segment 4", APP),
+ 0xFFE5: ("APP5", "Application segment 5", APP),
+ 0xFFE6: ("APP6", "Application segment 6", APP),
+ 0xFFE7: ("APP7", "Application segment 7", APP),
+ 0xFFE8: ("APP8", "Application segment 8", APP),
+ 0xFFE9: ("APP9", "Application segment 9", APP),
+ 0xFFEA: ("APP10", "Application segment 10", APP),
+ 0xFFEB: ("APP11", "Application segment 11", APP),
+ 0xFFEC: ("APP12", "Application segment 12", APP),
+ 0xFFED: ("APP13", "Application segment 13", APP),
+ 0xFFEE: ("APP14", "Application segment 14", APP),
+ 0xFFEF: ("APP15", "Application segment 15", APP),
+ 0xFFF0: ("JPG0", "Extension 0", None),
+ 0xFFF1: ("JPG1", "Extension 1", None),
+ 0xFFF2: ("JPG2", "Extension 2", None),
+ 0xFFF3: ("JPG3", "Extension 3", None),
+ 0xFFF4: ("JPG4", "Extension 4", None),
+ 0xFFF5: ("JPG5", "Extension 5", None),
+ 0xFFF6: ("JPG6", "Extension 6", None),
+ 0xFFF7: ("JPG7", "Extension 7", None),
+ 0xFFF8: ("JPG8", "Extension 8", None),
+ 0xFFF9: ("JPG9", "Extension 9", None),
+ 0xFFFA: ("JPG10", "Extension 10", None),
+ 0xFFFB: ("JPG11", "Extension 11", None),
+ 0xFFFC: ("JPG12", "Extension 12", None),
+ 0xFFFD: ("JPG13", "Extension 13", None),
+ 0xFFFE: ("COM", "Comment", COM)
+}
+
+
+def _accept(prefix):
+ return prefix[0] == "\377"
+
+##
+# Image plugin for JPEG and JFIF images.
+
+class JpegImageFile(ImageFile.ImageFile):
+
+ format = "JPEG"
+ format_description = "JPEG (ISO 10918)"
+
+ def _open(self):
+
+ s = self.fp.read(1)
+
+ if ord(s[0]) != 255:
+ raise SyntaxError("not a JPEG file")
+
+ # Create attributes
+ self.bits = self.layers = 0
+
+ # JPEG specifics (internal)
+ self.layer = []
+ self.huffman_dc = {}
+ self.huffman_ac = {}
+ self.quantization = {}
+ self.app = {} # compatibility
+ self.applist = []
+ self.icclist = []
+
+ while 1:
+
+ s = s + self.fp.read(1)
+
+ i = i16(s)
+
+ if MARKER.has_key(i):
+ name, description, handler = MARKER[i]
+ # print hex(i), name, description
+ if handler is not None:
+ handler(self, i)
+ if i == 0xFFDA: # start of scan
+ rawmode = self.mode
+ if self.mode == "CMYK":
+ rawmode = "CMYK;I" # assume adobe conventions
+ self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))]
+ # self.__offset = self.fp.tell()
+ break
+ s = self.fp.read(1)
+ elif i == 0 or i == 65535:
+ # padded marker or junk; move on
+ s = "\xff"
+ else:
+ raise SyntaxError("no marker found")
+
+ def draft(self, mode, size):
+
+ if len(self.tile) != 1:
+ return
+
+ d, e, o, a = self.tile[0]
+ scale = 0
+
+ if a[0] == "RGB" and mode in ["L", "YCbCr"]:
+ self.mode = mode
+ a = mode, ""
+
+ if size:
+ 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)
+ scale = s
+
+ self.tile = [(d, e, o, a)]
+ self.decoderconfig = (scale, 1)
+
+ return self
+
+ def load_djpeg(self):
+
+ # ALTERNATIVE: handle JPEGs via the IJG command line utilities
+
+ import tempfile, os
+ file = tempfile.mktemp()
+ os.system("djpeg %s >%s" % (self.filename, file))
+
+ try:
+ self.im = Image.core.open_ppm(file)
+ finally:
+ try: os.unlink(file)
+ except: pass
+
+ self.mode = self.im.mode
+ self.size = self.im.size
+
+ 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
+ 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
+
+# --------------------------------------------------------------------
+# stuff to save JPEG files
+
+RAWMODE = {
+ "1": "L",
+ "L": "L",
+ "RGB": "RGB",
+ "RGBA": "RGB",
+ "RGBX": "RGB",
+ "CMYK": "CMYK;I", # assume adobe conventions
+ "YCbCr": "YCbCr",
+}
+
+def _save(im, fp, filename):
+
+ try:
+ rawmode = RAWMODE[im.mode]
+ except KeyError:
+ raise IOError("cannot write mode %s as JPEG" % im.mode)
+
+ info = im.encoderinfo
+
+ dpi = info.get("dpi", (0, 0))
+
+ subsampling = info.get("subsampling", -1)
+ if subsampling == "4:4:4":
+ subsampling = 0
+ elif subsampling == "4:2:2":
+ subsampling = 1
+ elif subsampling == "4:1:1":
+ subsampling = 2
+
+ extra = ""
+
+ icc_profile = info.get("icc_profile")
+ if icc_profile:
+ ICC_OVERHEAD_LEN = 14
+ MAX_BYTES_IN_MARKER = 65533
+ MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN
+ markers = []
+ while icc_profile:
+ markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER])
+ icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:]
+ 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)
+ i = i + 1
+
+ # get keyword arguments
+ im.encoderconfig = (
+ info.get("quality", 0),
+ # "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"),
+ info.get("smooth", 0),
+ info.has_key("optimize"),
+ info.get("streamtype", 0),
+ dpi[0], dpi[1],
+ subsampling,
+ extra,
+ )
+
+ ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)])
+
+def _save_cjpeg(im, fp, filename):
+ # ALTERNATIVE: handle JPEGs via the IJG command line utilities.
+ import os
+ file = im._dump()
+ os.system("cjpeg %s >%s" % (file, filename))
+ try: os.unlink(file)
+ except: pass
+
+# -------------------------------------------------------------------q-
+# Registry stuff
+
+Image.register_open("JPEG", JpegImageFile, _accept)
+Image.register_save("JPEG", _save)
+
+Image.register_extension("JPEG", ".jfif")
+Image.register_extension("JPEG", ".jpe")
+Image.register_extension("JPEG", ".jpg")
+Image.register_extension("JPEG", ".jpeg")
+
+Image.register_mime("JPEG", "image/jpeg")
diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py
new file mode 100644
index 000000000..62ee52cf8
--- /dev/null
+++ b/PIL/McIdasImagePlugin.py
@@ -0,0 +1,70 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Basic McIdas support for PIL
+#
+# History:
+# 1997-05-05 fl Created (8-bit images only)
+# 2009-03-08 fl Added 16/32-bit support.
+#
+# Thanks to Richard Jones and Craig Swank for specs and samples.
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.2"
+
+import struct
+import Image, ImageFile
+
+def _accept(s):
+ return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04"
+
+##
+# Image plugin for McIdas area images.
+
+class McIdasImageFile(ImageFile.ImageFile):
+
+ format = "MCIDAS"
+ format_description = "McIdas area file"
+
+ def _open(self):
+
+ # parse area file directory
+ s = self.fp.read(256)
+ if not _accept(s) or len(s) != 256:
+ raise SyntaxError("not an McIdas area file")
+
+ self.area_descriptor_raw = s
+ self.area_descriptor = w = [0] + list(struct.unpack("!64i", s))
+
+ # get mode
+ if w[11] == 1:
+ mode = rawmode = "L"
+ elif w[11] == 2:
+ # FIXME: add memory map support
+ mode = "I"; rawmode = "I;16B"
+ elif w[11] == 4:
+ # FIXME: add memory map support
+ mode = "I"; rawmode = "I;32B"
+ else:
+ raise SyntaxError("unsupported McIdas format")
+
+ self.mode = mode
+ self.size = w[10], w[9]
+
+ offset = w[34] + w[15]
+ stride = w[15] + w[10]*w[11]*w[14]
+
+ self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))]
+
+# --------------------------------------------------------------------
+# registry
+
+Image.register_open("MCIDAS", McIdasImageFile, _accept)
+
+# no default extension
diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py
new file mode 100644
index 000000000..b1a3bba79
--- /dev/null
+++ b/PIL/MicImagePlugin.py
@@ -0,0 +1,95 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Microsoft Image Composer support for PIL
+#
+# Notes:
+# uses TiffImagePlugin.py to read the actual image streams
+#
+# History:
+# 97-01-20 fl Created
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.1"
+
+
+import Image, TiffImagePlugin
+from OleFileIO import *
+
+
+#
+# --------------------------------------------------------------------
+
+
+def _accept(prefix):
+ return prefix[:8] == MAGIC
+
+##
+# Image plugin for Microsoft's Image Composer file format.
+
+class MicImageFile(TiffImagePlugin.TiffImageFile):
+
+ format = "MIC"
+ format_description = "Microsoft Image Composer"
+
+ def _open(self):
+
+ # read the OLE directory and see if this is a likely
+ # to be a Microsoft Image Composer file
+
+ try:
+ self.ole = OleFileIO(self.fp)
+ except IOError:
+ 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... ;-)
+
+ self.images = []
+ for file in self.ole.listdir():
+ if file[1:] and file[0][-4:] == ".ACI" and file[1] == "Image":
+ self.images.append(file)
+
+ # 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"
+
+ self.__fp = self.fp
+ self.frame = 0
+
+ if len(self.images) > 1:
+ self.category = Image.CONTAINER
+
+ self.seek(0)
+
+ def seek(self, frame):
+
+ try:
+ filename = self.images[frame]
+ except IndexError:
+ raise EOFError, "no such frame"
+
+ self.fp = self.ole.openstream(filename)
+
+ TiffImagePlugin.TiffImageFile._open(self)
+
+ self.frame = frame
+
+ def tell(self):
+
+ return self.frame
+
+#
+# --------------------------------------------------------------------
+
+Image.register_open("MIC", MicImageFile, _accept)
+
+Image.register_extension("MIC", ".mic")
diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py
new file mode 100644
index 000000000..c02edee82
--- /dev/null
+++ b/PIL/MpegImagePlugin.py
@@ -0,0 +1,82 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# MPEG file handling
+#
+# History:
+# 95-09-09 fl Created
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1995.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.1"
+
+import Image, ImageFile
+
+#
+# Bitstream parser
+
+class BitStream:
+
+ def __init__(self, fp):
+ self.fp = fp
+ self.bits = 0
+ self.bitbuffer = 0
+
+ def next(self):
+ return ord(self.fp.read(1))
+
+ def peek(self, bits):
+ while self.bits < bits:
+ c = self.next()
+ if c < 0:
+ self.bits = 0
+ continue
+ self.bitbuffer = (self.bitbuffer << 8) + c
+ self.bits = self.bits + 8
+ return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1
+
+ def skip(self, bits):
+ while self.bits < bits:
+ self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1))
+ self.bits = self.bits + 8
+ self.bits = self.bits - bits
+
+ def read(self, bits):
+ v = self.peek(bits)
+ self.bits = self.bits - bits
+ return v
+
+##
+# Image plugin for MPEG streams. This plugin can identify a stream,
+# but it cannot read it.
+
+class MpegImageFile(ImageFile.ImageFile):
+
+ format = "MPEG"
+ format_description = "MPEG"
+
+ def _open(self):
+
+ s = BitStream(self.fp)
+
+ if s.read(32) != 0x1B3:
+ raise SyntaxError, "not an MPEG file"
+
+ self.mode = "RGB"
+ self.size = s.read(12), s.read(12)
+
+
+# --------------------------------------------------------------------
+# Registry stuff
+
+Image.register_open("MPEG", MpegImageFile)
+
+Image.register_extension("MPEG", ".mpg")
+Image.register_extension("MPEG", ".mpeg")
+
+Image.register_mime("MPEG", "video/mpeg")
diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py
new file mode 100644
index 000000000..9dac36b47
--- /dev/null
+++ b/PIL/MspImagePlugin.py
@@ -0,0 +1,103 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# MSP file handling
+#
+# This is the format used by the Paint program in Windows 1 and 2.
+#
+# History:
+# 95-09-05 fl Created
+# 97-01-03 fl Read/write MSP images
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1995-97.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.1"
+
+import Image, ImageFile
+
+
+#
+# read MSP files
+
+def i16(c):
+ return ord(c[0]) + (ord(c[1])<<8)
+
+def _accept(prefix):
+ return prefix[:4] in ["DanM", "LinS"]
+
+##
+# Image plugin for Windows MSP images. This plugin supports both
+# uncompressed (Windows 1.0).
+
+class MspImageFile(ImageFile.ImageFile):
+
+ format = "MSP"
+ format_description = "Windows Paint"
+
+ def _open(self):
+
+ # Header
+ s = self.fp.read(32)
+ if s[:4] not in ["DanM", "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"
+
+ self.mode = "1"
+ self.size = i16(s[4:]), i16(s[6:])
+
+ if s[:4] == "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)]
+
+#
+# write MSP files (uncompressed only)
+
+def o16(i):
+ return chr(i&255) + chr(i>>8&255)
+
+def _save(im, fp, filename):
+
+ if im.mode != "1":
+ 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[2], header[3] = im.size
+ header[4], header[5] = 1, 1
+ header[6], header[7] = 1, 1
+ header[8], header[9] = im.size
+
+ sum = 0
+ for h in header:
+ sum = sum ^ h
+ header[12] = sum # FIXME: is this the right field?
+
+ # header
+ for h in header:
+ fp.write(o16(h))
+
+ # image body
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 32, ("1", 0, 1))])
+
+#
+# registry
+
+Image.register_open("MSP", MspImageFile, _accept)
+Image.register_save("MSP", _save)
+
+Image.register_extension("MSP", ".msp")
diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py
new file mode 100644
index 000000000..36e598a9b
--- /dev/null
+++ b/PIL/OleFileIO.py
@@ -0,0 +1,528 @@
+#
+# THIS IS WORK IN PROGRESS
+#
+# The Python Imaging Library
+# $Id$
+#
+# stuff to deal with OLE2 Structured Storage files. this module is
+# used by PIL to read Image Composer and FlashPix files, but can also
+# be used to read other files of this type.
+#
+# History:
+# 1997-01-20 fl Created
+# 1997-01-22 fl Fixed 64-bit portability quirk
+# 2003-09-09 fl Fixed typo in OleFileIO.loadfat (noted by Daniel Haertle)
+# 2004-02-29 fl Changed long hex constants to signed integers
+#
+# Notes:
+# FIXME: sort out sign problem (eliminate long hex constants)
+# FIXME: change filename to use "a/b/c" instead of ["a", "b", "c"]
+# FIXME: provide a glob mechanism function (using fnmatchcase)
+#
+# Literature:
+#
+# "FlashPix Format Specification, Appendix A", Kodak and Microsoft,
+# September 1996.
+#
+# Quotes:
+#
+# "If this document and functionality of the Software conflict,
+# the actual functionality of the Software represents the correct
+# functionality" -- Microsoft, in the OLE format specification
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import string, StringIO
+
+
+def i16(c, o = 0):
+ return ord(c[o])+(ord(c[o+1])<<8)
+
+def i32(c, o = 0):
+ return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)
+
+
+MAGIC = '\320\317\021\340\241\261\032\341'
+
+#
+# --------------------------------------------------------------------
+# property types
+
+VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6;
+VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11;
+VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17;
+VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23;
+VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28;
+VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64;
+VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68;
+VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72;
+VT_VECTOR=0x1000;
+
+# map property id to name (for debugging purposes)
+
+VT = {}
+for k, v in vars().items():
+ if k[:3] == "VT_":
+ VT[v] = k
+
+#
+# --------------------------------------------------------------------
+# Some common document types (root.clsid fields)
+
+WORD_CLSID = "00020900-0000-0000-C000-000000000046"
+
+
+#
+# --------------------------------------------------------------------
+
+class _OleStream(StringIO.StringIO):
+
+ """OLE2 Stream
+
+ Returns a read-only file object which can be used to read
+ the contents of a OLE stream. To open a stream, use the
+ openstream method in the OleFile class.
+
+ This function can be used with either ordinary streams,
+ or ministreams, depending on the offset, sectorsize, and
+ fat table arguments.
+ """
+
+ # FIXME: should store the list of sects obtained by following
+ # the fat chain, and load new sectors on demand instead of
+ # loading it all in one go.
+
+ def __init__(self, fp, sect, size, offset, sectorsize, fat):
+
+ data = []
+
+ while sect != -2: # 0xFFFFFFFEL:
+ fp.seek(offset + sectorsize * sect)
+ data.append(fp.read(sectorsize))
+ sect = fat[sect]
+
+ data = string.join(data, "")
+
+ # print len(data), size
+
+ StringIO.StringIO.__init__(self, data[:size])
+
+#
+# --------------------------------------------------------------------
+
+# FIXME: should add a counter in here to avoid looping forever
+# if the tree is broken.
+
+class _OleDirectoryEntry:
+
+ """OLE2 Directory Entry
+
+ Encapsulates a stream directory entry. Note that the
+ constructor builds a tree of all subentries, so we only
+ have to call it with the root object.
+ """
+
+ def __init__(self, sidlist, sid):
+
+ # store directory parameters. the caller provides
+ # a complete list of directory entries, as read from
+ # the directory stream.
+
+ name, type, sect, size, sids, clsid = sidlist[sid]
+
+ self.sid = sid
+ self.name = name
+ self.type = type # 1=storage 2=stream
+ self.sect = sect
+ self.size = size
+ self.clsid = clsid
+
+ # process child nodes, if any
+
+ self.kids = []
+
+ sid = sidlist[sid][4][2]
+
+ if sid != -1:
+
+ # the directory entries are organized as a red-black tree.
+ # the following piece of code does an ordered traversal of
+ # such a tree (at least that's what I hope ;-)
+
+ stack = [self.sid]
+
+ # start at leftmost position
+
+ left, right, child = sidlist[sid][4]
+
+ while left != -1: # 0xFFFFFFFFL:
+ stack.append(sid)
+ sid = left
+ left, right, child = sidlist[sid][4]
+
+ while sid != self.sid:
+
+ self.kids.append(_OleDirectoryEntry(sidlist, sid))
+
+ # try to move right
+ left, right, child = sidlist[sid][4]
+ if right != -1: # 0xFFFFFFFFL:
+ # and then back to the left
+ sid = right
+ while 1:
+ left, right, child = sidlist[sid][4]
+ if left == -1: # 0xFFFFFFFFL:
+ break
+ stack.append(sid)
+ sid = left
+ else:
+ # couldn't move right; move up instead
+ while 1:
+ ptr = stack[-1]
+ del stack[-1]
+ left, right, child = sidlist[ptr][4]
+ if right != sid:
+ break
+ sid = right
+ left, right, child = sidlist[sid][4]
+ if right != ptr:
+ sid = ptr
+
+ # in the OLE file, entries are sorted on (length, name).
+ # for convenience, we sort them on name instead.
+
+ self.kids.sort()
+
+ def __cmp__(self, other):
+ "Compare entries by name"
+
+ return cmp(self.name, other.name)
+
+ def dump(self, tab = 0):
+ "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.type],
+ if self.type in (2, 5):
+ print self.size, "bytes",
+ print
+ if self.type in (1, 5) and self.clsid:
+ print " "*tab + "{%s}" % self.clsid
+
+ for kid in self.kids:
+ kid.dump(tab + 2)
+
+#
+# --------------------------------------------------------------------
+
+##
+# This class encapsulates the interface to an OLE 2 structured
+# storage file. Use the {@link listdir} and {@link openstream}
+# methods to access the contents of this file.
+
+class OleFileIO:
+ """OLE container object
+
+ This class encapsulates the interface to an OLE 2 structured
+ storage file. Use the listdir and openstream methods to access
+ the contents of this file.
+
+ 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:
+
+ ole = OleFileIO("fan.mic")
+
+ for entry in ole.listdir():
+ if entry[1:2] == "Image":
+ fin = ole.openstream(entry)
+ fout = open(entry[0:1], "wb")
+ while 1:
+ s = fin.read(8192)
+ if not s:
+ break
+ fout.write(s)
+
+ You can use the viewer application provided with the Python Imaging
+ Library to view the resulting files (which happens to be standard
+ TIFF files).
+ """
+
+ def __init__(self, filename = None):
+
+ if filename:
+ self.open(filename)
+
+ ##
+ # Open an OLE2 file.
+
+ def open(self, filename):
+ """Open an OLE2 file"""
+
+ if type(filename) == type(""):
+ self.fp = open(filename, "rb")
+ else:
+ self.fp = filename
+
+ header = self.fp.read(512)
+
+ if len(header) != 512 or header[:8] != MAGIC:
+ raise IOError, "not an OLE2 structured storage file"
+
+ # file clsid (probably never used, so we don't store it)
+ clsid = self._clsid(header[8:24])
+
+ # FIXME: could check version and byte order fields
+
+ self.sectorsize = 1 << i16(header, 30)
+ self.minisectorsize = 1 << i16(header, 32)
+
+ self.minisectorcutoff = i32(header, 56)
+
+ # Load file allocation tables
+ self.loadfat(header)
+
+ # Load direcory. This sets both the sidlist (ordered by id)
+ # and the root (ordered by hierarchy) members.
+ self.loaddirectory(i32(header, 48))
+
+ self.ministream = None
+ self.minifatsect = i32(header, 60)
+
+ def loadfat(self, header):
+ # Load the FAT table. The header contains a sector numbers
+ # for the first 109 FAT sectors. Additional sectors are
+ # described by DIF blocks (FIXME: not yet implemented)
+
+ sect = header[76:512]
+ fat = []
+ for i in range(0, len(sect), 4):
+ ix = i32(sect, i)
+ if ix == -2 or ix == -1: # 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))
+ self.fat = fat
+
+ def loadminifat(self):
+ # Load the MINIFAT table. This is stored in a standard sub-
+ # stream, pointed to by a header field.
+
+ s = self._open(self.minifatsect).read()
+
+ self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4))
+
+ def getsect(self, sect):
+ # Read given sector
+
+ self.fp.seek(512 + self.sectorsize * sect)
+ return self.fp.read(self.sectorsize)
+
+ def _unicode(self, s):
+ # Map unicode string to Latin 1
+
+ # FIXME: some day, Python will provide an official way to handle
+ # Unicode strings, but until then, this will have to do...
+ return filter(ord, s)
+
+ def loaddirectory(self, sect):
+ # Load the directory. The directory is stored in a standard
+ # substream, independent of its size.
+
+ # read directory stream
+ fp = self._open(sect)
+
+ # create list of sid entries
+ self.sidlist = []
+ while 1:
+ entry = fp.read(128)
+ if not entry:
+ break
+ type = ord(entry[66])
+ name = self._unicode(entry[0:0+i16(entry, 64)])
+ ptrs = i32(entry, 68), i32(entry, 72), i32(entry, 76)
+ sect, size = i32(entry, 116), i32(entry, 120)
+ clsid = self._clsid(entry[80:96])
+ self.sidlist.append((name, type, sect, size, ptrs, clsid))
+
+ # create hierarchical list of directory entries
+ self.root = _OleDirectoryEntry(self.sidlist, 0)
+
+ def dumpdirectory(self):
+ # Dump directory (for debugging only)
+
+ self.root.dump()
+
+ def _clsid(self, clsid):
+ if clsid == "\0" * len(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]))))
+
+ def _list(self, files, prefix, node):
+ # listdir helper
+
+ prefix = prefix + [node.name]
+ for entry in node.kids:
+ if entry.kids:
+ self._list(files, prefix, entry)
+ else:
+ files.append(prefix[1:] + [entry.name])
+
+ def _find(self, filename):
+ # openstream helper
+
+ node = self.root
+ for name in filename:
+ for kid in node.kids:
+ if kid.name == name:
+ break
+ else:
+ raise IOError, "file not found"
+ node = kid
+ return node.sid
+
+ def _open(self, start, size = 0x7FFFFFFF):
+ # openstream helper.
+
+ if size < self.minisectorcutoff:
+ # ministream object
+ if not self.ministream:
+ self.loadminifat()
+ self.ministream = self._open(self.sidlist[0][2])
+ return _OleStream(self.ministream, start, size, 0,
+ self.minisectorsize, self.minifat)
+
+ # standard stream
+ return _OleStream(self.fp, start, size, 512,
+ self.sectorsize, self.fat)
+
+ ##
+ # Returns a list of streams stored in this file.
+
+ def listdir(self):
+ """Return a list of streams stored in this file"""
+
+ files = []
+ self._list(files, [], self.root)
+ return files
+
+ ##
+ # Opens a stream as a read-only file object.
+
+ def openstream(self, filename):
+ """Open a stream as a read-only file object"""
+
+ slot = self._find(filename)
+ name, type, sect, size, sids, clsid = self.sidlist[slot]
+ if type != 2:
+ raise IOError, "this file is not a stream"
+ return self._open(sect, size)
+
+ ##
+ # Gets a list of properties described in substream.
+
+ def getproperties(self, filename):
+ """Return properties described in substream"""
+
+ fp = self.openstream(filename)
+
+ data = {}
+
+ # header
+ s = fp.read(28)
+ clsid = self._clsid(s[8:24])
+
+ # format id
+ s = fp.read(20)
+ fmtid = self._clsid(s[:16])
+ fp.seek(i32(s, 16))
+
+ # get section
+ s = "****" + fp.read(i32(fp.read(4))-4)
+
+ for i in range(i32(s, 4)):
+
+ id = i32(s, 8+i*8)
+ offset = i32(s, 12+i*8)
+ type = i32(s, offset)
+
+ # test for common types first (should perhaps use
+ # a dictionary instead?)
+
+ if type == VT_I2:
+ value = i16(s, offset+4)
+ if value >= 32768:
+ value = value - 65536
+ elif type == VT_UI2:
+ value = i16(s, offset+4)
+ elif type in (VT_I4, VT_ERROR):
+ value = i32(s, offset+4)
+ elif type == VT_UI4:
+ value = i32(s, offset+4) # FIXME
+ elif type in (VT_BSTR, VT_LPSTR):
+ count = i32(s, offset+4)
+ value = s[offset+8:offset+8+count-1]
+ elif type == VT_BLOB:
+ count = i32(s, offset+4)
+ value = s[offset+8:offset+8+count]
+ elif type == VT_LPWSTR:
+ count = i32(s, offset+4)
+ value = self._unicode(s[offset+8:offset+8+count*2])
+ elif type == VT_FILETIME:
+ value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32)
+ # FIXME: this is a 64-bit int: "number of 100ns periods
+ # since Jan 1,1601". Should map this to Python time
+ value = value / 10000000L # seconds
+ elif type == VT_UI1:
+ value = ord(s[offset+4])
+ elif type == VT_CLSID:
+ value = self._clsid(s[offset+4:offset+20])
+ elif type == VT_CF:
+ count = i32(s, offset+4)
+ value = s[offset+8:offset+8+count]
+ else:
+ value = None # everything else yields "None"
+
+ # FIXME: add support for VT_VECTOR
+
+ #print "%08x" % id, repr(value),
+ #print "(%s)" % VT[i32(s, offset) & 0xFFF]
+
+ data[id] = value
+
+ return data
+
+#
+# --------------------------------------------------------------------
+# This script can be used to dump the directory of any OLE2 structured
+# storage file.
+
+if __name__ == "__main__":
+
+ import sys
+
+ for file in sys.argv[1:]:
+ try:
+ ole = OleFileIO(file)
+ print "-" * 68
+ print file
+ print "-" * 68
+ ole.dumpdirectory()
+ for file in ole.listdir():
+ if file[-1][0] == "\005":
+ print file
+ props = ole.getproperties(file)
+ props = props.items()
+ props.sort()
+ for k, v in props:
+ print " ", k, v
+ except IOError, v:
+ print "***", "cannot read", file, "-", v
diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py
new file mode 100644
index 000000000..7309e17b3
--- /dev/null
+++ b/PIL/PSDraw.py
@@ -0,0 +1,199 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# simple postscript graphics interface
+#
+# History:
+# 1996-04-20 fl Created
+# 1999-01-10 fl Added gsave/grestore to image method
+# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge)
+#
+# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved.
+# Copyright (c) 1996 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import EpsImagePlugin
+import string
+
+##
+# Simple Postscript graphics interface.
+
+class PSDraw:
+
+ def __init__(self, fp=None):
+ if not fp:
+ import sys
+ fp = sys.stdout
+ self.fp = fp
+
+ def begin_document(self, id = None):
+ "Write Postscript DSC header"
+ # FIXME: incomplete
+ self.fp.write("%!PS-Adobe-3.0\n"
+ "save\n"
+ "/showpage { } def\n"
+ "%%EndComments\n"
+ "%%BeginDocument\n")
+ #self.fp.write(ERROR_PS) # debugging!
+ self.fp.write(EDROFF_PS)
+ self.fp.write(VDI_PS)
+ self.fp.write("%%EndProlog\n")
+ self.isofont = {}
+
+ def end_document(self):
+ "Write Postscript DSC footer"
+ self.fp.write("%%EndDocument\n"
+ "restore showpage\n"
+ "%%End\n")
+ if hasattr(self.fp, "flush"):
+ self.fp.flush()
+
+ def setfont(self, font, size):
+ if not self.isofont.has_key(font):
+ # reencode font
+ self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\
+ (font, font))
+ self.isofont[font] = 1
+ # rough
+ self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font))
+
+ def setink(self, ink):
+ print "*** NOT YET IMPLEMENTED ***"
+
+ def line(self, xy0, xy1):
+ xy = xy0 + xy1
+ self.fp.write("%d %d %d %d Vl\n" % xy)
+
+ def rectangle(self, box):
+ 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, ")"), "\\)")
+ xy = xy + (text,)
+ self.fp.write("%d %d M (%s) S\n" % xy)
+
+ def image(self, box, im, dpi = None):
+ "Write an PIL image"
+ # default resolution depends on mode
+ if not dpi:
+ if im.mode == "1":
+ dpi = 200 # fax
+ else:
+ dpi = 100 # greyscale
+ # image size (on paper)
+ x = float(im.size[0] * 72) / dpi
+ y = float(im.size[1] * 72) / dpi
+ # max allowed size
+ xmax = float(box[2] - box[0])
+ ymax = float(box[3] - box[1])
+ if x > xmax:
+ y = y * xmax / x; x = xmax
+ if y > ymax:
+ x = x * ymax / y; y = ymax
+ dx = (xmax - x) / 2 + box[0]
+ dy = (ymax - y) / 2 + box[1]
+ self.fp.write("gsave\n%f %f translate\n" % (dx, dy))
+ if (x, y) != im.size:
+ # EpsImagePlugin._save prints the image at (0,0,xsize,ysize)
+ sx = x / im.size[0]
+ sy = y / im.size[1]
+ self.fp.write("%f %f scale\n" % (sx, sy))
+ EpsImagePlugin._save(im, self.fp, None, 0)
+ self.fp.write("\ngrestore\n")
+
+# --------------------------------------------------------------------
+# Postscript driver
+
+#
+# EDROFF.PS -- Postscript driver for Edroff 2
+#
+# History:
+# 94-01-25 fl: created (edroff 2.04)
+#
+# Copyright (c) Fredrik Lundh 1994.
+#
+
+EDROFF_PS = """\
+/S { show } bind def
+/P { moveto show } bind def
+/M { moveto } bind def
+/X { 0 rmoveto } bind def
+/Y { 0 exch rmoveto } bind def
+/E { findfont
+ dup maxlength dict begin
+ {
+ 1 index /FID ne { def } { pop pop } ifelse
+ } forall
+ /Encoding exch def
+ dup /FontName exch def
+ currentdict end definefont pop
+} bind def
+/F { findfont exch scalefont dup setfont
+ [ exch /setfont cvx ] cvx bind def
+} bind def
+"""
+
+#
+# VDI.PS -- Postscript driver for VDI meta commands
+#
+# History:
+# 94-01-25 fl: created (edroff 2.04)
+#
+# Copyright (c) Fredrik Lundh 1994.
+#
+
+VDI_PS = """\
+/Vm { moveto } bind def
+/Va { newpath arcn stroke } bind def
+/Vl { moveto lineto stroke } bind def
+/Vc { newpath 0 360 arc closepath } bind def
+/Vr { exch dup 0 rlineto
+ exch dup neg 0 exch rlineto
+ exch neg 0 rlineto
+ 0 exch rlineto
+ 100 div setgray fill 0 setgray } bind def
+/Tm matrix def
+/Ve { Tm currentmatrix pop
+ translate scale newpath 0 0 .5 0 360 arc closepath
+ Tm setmatrix
+} bind def
+/Vf { currentgray exch setgray fill setgray } bind def
+"""
+
+#
+# ERROR.PS -- Error handler
+#
+# History:
+# 89-11-21 fl: created (pslist 1.10)
+#
+
+ERROR_PS = """\
+/landscape false def
+/errorBUF 200 string def
+/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def
+errordict begin /handleerror {
+ initmatrix /Courier findfont 10 scalefont setfont
+ newpath 72 720 moveto $error begin /newerror false def
+ (PostScript Error) show errorNL errorNL
+ (Error: ) show
+ /errorname load errorBUF cvs show errorNL errorNL
+ (Command: ) show
+ /command load dup type /stringtype ne { errorBUF cvs } if show
+ errorNL errorNL
+ (VMstatus: ) show
+ vmstatus errorBUF cvs show ( bytes available, ) show
+ errorBUF cvs show ( bytes used at level ) show
+ errorBUF cvs show errorNL errorNL
+ (Operand stargck: ) show errorNL /ostargck load {
+ dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL
+ } forall errorNL
+ (Execution stargck: ) show errorNL /estargck load {
+ dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL
+ } forall
+ end showpage
+} def end
+"""
diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py
new file mode 100644
index 000000000..3bbd91327
--- /dev/null
+++ b/PIL/PaletteFile.py
@@ -0,0 +1,55 @@
+#
+# Python Imaging Library
+# $Id$
+#
+# stuff to read simple, teragon-style palette files
+#
+# History:
+# 97-08-23 fl Created
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import string
+
+##
+# File handler for Teragon-style palette files.
+
+class PaletteFile:
+
+ rawmode = "RGB"
+
+ def __init__(self, fp):
+
+ self.palette = map(lambda i: (i, i, i), range(256))
+
+ while 1:
+
+ s = fp.readline()
+
+ if not s:
+ break
+ if s[0] == "#":
+ continue
+ if len(s) > 100:
+ raise SyntaxError, "bad palette file"
+
+ v = map(int, string.split(s))
+ try:
+ [i, r, g, b] = v
+ except ValueError:
+ [i, r] = v
+ g = b = r
+
+ if 0 <= i <= 255:
+ self.palette[i] = chr(r) + chr(g) + chr(b)
+
+ self.palette = string.join(self.palette, "")
+
+
+ def getpalette(self):
+
+ return self.palette, self.rawmode
diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py
new file mode 100644
index 000000000..785023130
--- /dev/null
+++ b/PIL/PalmImagePlugin.py
@@ -0,0 +1,225 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+
+##
+# Image plugin for Palm pixmap images (output only).
+##
+
+__version__ = "1.0"
+
+import Image, ImageFile
+
+_Palm8BitColormapValues = (
+ ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ),
+ ( 255, 51, 255 ), ( 255, 0, 255 ), ( 255, 255, 204 ), ( 255, 204, 204 ),
+ ( 255, 153, 204 ), ( 255, 102, 204 ), ( 255, 51, 204 ), ( 255, 0, 204 ),
+ ( 255, 255, 153 ), ( 255, 204, 153 ), ( 255, 153, 153 ), ( 255, 102, 153 ),
+ ( 255, 51, 153 ), ( 255, 0, 153 ), ( 204, 255, 255 ), ( 204, 204, 255 ),
+ ( 204, 153, 255 ), ( 204, 102, 255 ), ( 204, 51, 255 ), ( 204, 0, 255 ),
+ ( 204, 255, 204 ), ( 204, 204, 204 ), ( 204, 153, 204 ), ( 204, 102, 204 ),
+ ( 204, 51, 204 ), ( 204, 0, 204 ), ( 204, 255, 153 ), ( 204, 204, 153 ),
+ ( 204, 153, 153 ), ( 204, 102, 153 ), ( 204, 51, 153 ), ( 204, 0, 153 ),
+ ( 153, 255, 255 ), ( 153, 204, 255 ), ( 153, 153, 255 ), ( 153, 102, 255 ),
+ ( 153, 51, 255 ), ( 153, 0, 255 ), ( 153, 255, 204 ), ( 153, 204, 204 ),
+ ( 153, 153, 204 ), ( 153, 102, 204 ), ( 153, 51, 204 ), ( 153, 0, 204 ),
+ ( 153, 255, 153 ), ( 153, 204, 153 ), ( 153, 153, 153 ), ( 153, 102, 153 ),
+ ( 153, 51, 153 ), ( 153, 0, 153 ), ( 102, 255, 255 ), ( 102, 204, 255 ),
+ ( 102, 153, 255 ), ( 102, 102, 255 ), ( 102, 51, 255 ), ( 102, 0, 255 ),
+ ( 102, 255, 204 ), ( 102, 204, 204 ), ( 102, 153, 204 ), ( 102, 102, 204 ),
+ ( 102, 51, 204 ), ( 102, 0, 204 ), ( 102, 255, 153 ), ( 102, 204, 153 ),
+ ( 102, 153, 153 ), ( 102, 102, 153 ), ( 102, 51, 153 ), ( 102, 0, 153 ),
+ ( 51, 255, 255 ), ( 51, 204, 255 ), ( 51, 153, 255 ), ( 51, 102, 255 ),
+ ( 51, 51, 255 ), ( 51, 0, 255 ), ( 51, 255, 204 ), ( 51, 204, 204 ),
+ ( 51, 153, 204 ), ( 51, 102, 204 ), ( 51, 51, 204 ), ( 51, 0, 204 ),
+ ( 51, 255, 153 ), ( 51, 204, 153 ), ( 51, 153, 153 ), ( 51, 102, 153 ),
+ ( 51, 51, 153 ), ( 51, 0, 153 ), ( 0, 255, 255 ), ( 0, 204, 255 ),
+ ( 0, 153, 255 ), ( 0, 102, 255 ), ( 0, 51, 255 ), ( 0, 0, 255 ),
+ ( 0, 255, 204 ), ( 0, 204, 204 ), ( 0, 153, 204 ), ( 0, 102, 204 ),
+ ( 0, 51, 204 ), ( 0, 0, 204 ), ( 0, 255, 153 ), ( 0, 204, 153 ),
+ ( 0, 153, 153 ), ( 0, 102, 153 ), ( 0, 51, 153 ), ( 0, 0, 153 ),
+ ( 255, 255, 102 ), ( 255, 204, 102 ), ( 255, 153, 102 ), ( 255, 102, 102 ),
+ ( 255, 51, 102 ), ( 255, 0, 102 ), ( 255, 255, 51 ), ( 255, 204, 51 ),
+ ( 255, 153, 51 ), ( 255, 102, 51 ), ( 255, 51, 51 ), ( 255, 0, 51 ),
+ ( 255, 255, 0 ), ( 255, 204, 0 ), ( 255, 153, 0 ), ( 255, 102, 0 ),
+ ( 255, 51, 0 ), ( 255, 0, 0 ), ( 204, 255, 102 ), ( 204, 204, 102 ),
+ ( 204, 153, 102 ), ( 204, 102, 102 ), ( 204, 51, 102 ), ( 204, 0, 102 ),
+ ( 204, 255, 51 ), ( 204, 204, 51 ), ( 204, 153, 51 ), ( 204, 102, 51 ),
+ ( 204, 51, 51 ), ( 204, 0, 51 ), ( 204, 255, 0 ), ( 204, 204, 0 ),
+ ( 204, 153, 0 ), ( 204, 102, 0 ), ( 204, 51, 0 ), ( 204, 0, 0 ),
+ ( 153, 255, 102 ), ( 153, 204, 102 ), ( 153, 153, 102 ), ( 153, 102, 102 ),
+ ( 153, 51, 102 ), ( 153, 0, 102 ), ( 153, 255, 51 ), ( 153, 204, 51 ),
+ ( 153, 153, 51 ), ( 153, 102, 51 ), ( 153, 51, 51 ), ( 153, 0, 51 ),
+ ( 153, 255, 0 ), ( 153, 204, 0 ), ( 153, 153, 0 ), ( 153, 102, 0 ),
+ ( 153, 51, 0 ), ( 153, 0, 0 ), ( 102, 255, 102 ), ( 102, 204, 102 ),
+ ( 102, 153, 102 ), ( 102, 102, 102 ), ( 102, 51, 102 ), ( 102, 0, 102 ),
+ ( 102, 255, 51 ), ( 102, 204, 51 ), ( 102, 153, 51 ), ( 102, 102, 51 ),
+ ( 102, 51, 51 ), ( 102, 0, 51 ), ( 102, 255, 0 ), ( 102, 204, 0 ),
+ ( 102, 153, 0 ), ( 102, 102, 0 ), ( 102, 51, 0 ), ( 102, 0, 0 ),
+ ( 51, 255, 102 ), ( 51, 204, 102 ), ( 51, 153, 102 ), ( 51, 102, 102 ),
+ ( 51, 51, 102 ), ( 51, 0, 102 ), ( 51, 255, 51 ), ( 51, 204, 51 ),
+ ( 51, 153, 51 ), ( 51, 102, 51 ), ( 51, 51, 51 ), ( 51, 0, 51 ),
+ ( 51, 255, 0 ), ( 51, 204, 0 ), ( 51, 153, 0 ), ( 51, 102, 0 ),
+ ( 51, 51, 0 ), ( 51, 0, 0 ), ( 0, 255, 102 ), ( 0, 204, 102 ),
+ ( 0, 153, 102 ), ( 0, 102, 102 ), ( 0, 51, 102 ), ( 0, 0, 102 ),
+ ( 0, 255, 51 ), ( 0, 204, 51 ), ( 0, 153, 51 ), ( 0, 102, 51 ),
+ ( 0, 51, 51 ), ( 0, 0, 51 ), ( 0, 255, 0 ), ( 0, 204, 0 ),
+ ( 0, 153, 0 ), ( 0, 102, 0 ), ( 0, 51, 0 ), ( 17, 17, 17 ),
+ ( 34, 34, 34 ), ( 68, 68, 68 ), ( 85, 85, 85 ), ( 119, 119, 119 ),
+ ( 136, 136, 136 ), ( 170, 170, 170 ), ( 187, 187, 187 ), ( 221, 221, 221 ),
+ ( 238, 238, 238 ), ( 192, 192, 192 ), ( 128, 0, 0 ), ( 128, 0, 128 ),
+ ( 0, 128, 0 ), ( 0, 128, 128 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ),
+ ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ))
+
+# 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)))
+ palettedata = ()
+ for i in range(len(_Palm8BitColormapValues)):
+ palettedata = palettedata + _Palm8BitColormapValues[i]
+ for i in range(256 - len(_Palm8BitColormapValues)):
+ palettedata = palettedata + (0, 0, 0)
+ image.putpalette(palettedata)
+ return image
+
+Palm8BitColormapImage = build_prototype_image()
+
+# OK, we now have in Palm8BitColormapImage, a "P"-mode image with the right palette
+#
+# --------------------------------------------------------------------
+
+_FLAGS = {
+ "custom-colormap": 0x4000,
+ "is-compressed": 0x8000,
+ "has-transparent": 0x2000,
+ }
+
+_COMPRESSION_TYPES = {
+ "none": 0xFF,
+ "rle": 0x01,
+ "scanline": 0x00,
+ }
+
+def o16b(i):
+ return chr(i>>8&255) + chr(i&255)
+
+#
+# --------------------------------------------------------------------
+
+##
+# (Internal) Image save plugin for the Palm format.
+
+def _save(im, fp, filename, check=0):
+
+ if im.mode == "P":
+
+ # we assume this is a color Palm image with the standard colormap,
+ # unless the "info" dict has a "custom-colormap" field
+
+ rawmode = "P"
+ bpp = 8
+ version = 1
+
+ elif im.mode == "L" and im.encoderinfo.has_key("bpp") 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)
+ bpp = im.encoderinfo["bpp"]
+ im = im.point(lambda x, shift=8-bpp, maxval=(1 << bpp)-1: maxval - (x >> shift))
+ # we ignore the palette here
+ im.mode = "P"
+ rawmode = "P;" + str(bpp)
+ version = 1
+
+ elif im.mode == "L" and im.info.has_key("bpp") 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.
+ bpp = im.info["bpp"]
+ im = im.point(lambda x, maxval=(1 << bpp)-1: maxval - (x & maxval))
+ # we ignore the palette here
+ im.mode = "P"
+ rawmode = "P;" + str(bpp)
+ version = 1
+
+ elif im.mode == "1":
+
+ # monochrome -- write it inverted, as is the Palm standard
+ rawmode = "1;I"
+ bpp = 1
+ version = 0
+
+ else:
+
+ raise IOError, "cannot write mode %s as Palm" % im.mode
+
+ if check:
+ return check
+
+ #
+ # make sure image data is available
+ im.load()
+
+ # write header
+
+ cols = im.size[0]
+ rows = im.size[1]
+
+ 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"):
+ flags = flags & _FLAGS["custom-colormap"]
+ colormapsize = 4 * 256 + 2;
+ colormapmode = im.palette.mode
+ colormap = im.getdata().getpalette()
+ else:
+ colormapsize = 0
+
+ if im.info.has_key("offset"):
+ 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(o16b(offset))
+ fp.write(chr(transparent_index))
+ fp.write(chr(compression_type))
+ fp.write(o16b(0)) # reserved by Palm
+
+ # now write colormap if necessary
+
+ if colormapsize > 0:
+ fp.write(o16b(256))
+ for i in range(256):
+ fp.write(chr(i))
+ if colormapmode == 'RGB':
+ fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2]))
+ elif colormapmode == 'RGBA':
+ fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2]))
+
+ # now convert data to raw form
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))])
+
+ fp.flush()
+
+
+#
+# --------------------------------------------------------------------
+
+Image.register_save("Palm", _save)
+
+Image.register_extension("Palm", ".palm")
+
+Image.register_mime("Palm", "image/palm")
diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py
new file mode 100644
index 000000000..07bd27e9a
--- /dev/null
+++ b/PIL/PcdImagePlugin.py
@@ -0,0 +1,76 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PCD file handling
+#
+# History:
+# 96-05-10 fl Created
+# 96-05-27 fl Added draft mode (128x192, 256x384)
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.1"
+
+
+import Image, ImageFile
+
+##
+# Image plugin for PhotoCD images. This plugin only reads the 768x512
+# image from the file; higher resolutions are encoded in a proprietary
+# encoding.
+
+class PcdImageFile(ImageFile.ImageFile):
+
+ format = "PCD"
+ format_description = "Kodak PhotoCD"
+
+ def _open(self):
+
+ # rough
+ self.fp.seek(2048)
+ s = self.fp.read(2048)
+
+ if s[:4] != "PCD_":
+ raise SyntaxError, "not a PCD file"
+
+ orientation = ord(s[1538]) & 3
+ if orientation == 1:
+ self.tile_post_rotate = 90 # hack
+ elif orientation == 3:
+ self.tile_post_rotate = -90
+
+ self.mode = "RGB"
+ self.size = 768, 512 # FIXME: not correct for rotated images!
+ self.tile = [("pcd", (0,0)+self.size, 96*2048, None)]
+
+ def draft(self, mode, size):
+
+ if len(self.tile) != 1:
+ return
+
+ d, e, o, a = self.tile[0]
+
+ if size:
+ scale = max(self.size[0] / size[0], self.size[1] / size[1])
+ for s, o in [(4,0*2048), (2,0*2048), (1,96*2048)]:
+ 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)
+
+ self.tile = [(d, e, o, a)]
+
+ return self
+
+#
+# registry
+
+Image.register_open("PCD", PcdImageFile)
+
+Image.register_extension("PCD", ".pcd")
diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py
new file mode 100644
index 000000000..bec39e3a0
--- /dev/null
+++ b/PIL/PcfFontFile.py
@@ -0,0 +1,256 @@
+#
+# THIS IS WORK IN PROGRESS
+#
+# The Python Imaging Library
+# $Id$
+#
+# portable compiled font file parser
+#
+# history:
+# 1997-08-19 fl created
+# 2003-09-13 fl fixed loading of unicode fonts
+#
+# Copyright (c) 1997-2003 by Secret Labs AB.
+# Copyright (c) 1997-2003 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import Image
+import FontFile
+
+import string
+
+# --------------------------------------------------------------------
+# declarations
+
+PCF_MAGIC = 0x70636601 # "\x01fcp"
+
+PCF_PROPERTIES = (1<<0)
+PCF_ACCELERATORS = (1<<1)
+PCF_METRICS = (1<<2)
+PCF_BITMAPS = (1<<3)
+PCF_INK_METRICS = (1<<4)
+PCF_BDF_ENCODINGS = (1<<5)
+PCF_SWIDTHS = (1<<6)
+PCF_GLYPH_NAMES = (1<<7)
+PCF_BDF_ACCELERATORS = (1<<8)
+
+BYTES_PER_ROW = [
+ lambda bits: ((bits+7) >> 3),
+ lambda bits: ((bits+15) >> 3) & ~1,
+ lambda bits: ((bits+31) >> 3) & ~3,
+ 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)
+
+def sz(s, o):
+ return s[o:string.index(s, "\0", o)]
+
+##
+# Font file plugin for the X11 PCF format.
+
+class PcfFontFile(FontFile.FontFile):
+
+ name = "name"
+
+ def __init__(self, fp):
+
+ magic = l32(fp.read(4))
+ if magic != PCF_MAGIC:
+ raise SyntaxError, "not a PCF file"
+
+ FontFile.FontFile.__init__(self)
+
+ count = l32(fp.read(4))
+ self.toc = {}
+ for i in range(count):
+ type = l32(fp.read(4))
+ self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4))
+
+ self.fp = fp
+
+ self.info = self._load_properties()
+
+ metrics = self._load_metrics()
+ bitmaps = self._load_bitmaps(metrics)
+ encoding = self._load_encoding()
+
+ #
+ # create glyph structure
+
+ for ch in range(256):
+ ix = encoding[ch]
+ if ix is not None:
+ x, y, l, r, w, a, d, f = metrics[ix]
+ glyph = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix]
+ self.glyph[ch] = glyph
+
+ def _getformat(self, tag):
+
+ format, size, offset = self.toc[tag]
+
+ fp = self.fp
+ fp.seek(offset)
+
+ format = l32(fp.read(4))
+
+ if format & 4:
+ i16, i32 = b16, b32
+ else:
+ i16, i32 = l16, l32
+
+ return fp, format, i16, i32
+
+ def _load_properties(self):
+
+ #
+ # font properties
+
+ properties = {}
+
+ fp, format, i16, i32 = self._getformat(PCF_PROPERTIES)
+
+ nprops = i32(fp.read(4))
+
+ # read property description
+ p = []
+ for i in range(nprops):
+ p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4))))
+ if nprops & 3:
+ fp.seek(4 - (nprops & 3), 1) # pad
+
+ data = fp.read(i32(fp.read(4)))
+
+ for k, s, v in p:
+ k = sz(data, k)
+ if s:
+ v = sz(data, v)
+ properties[k] = v
+
+ return properties
+
+ def _load_metrics(self):
+
+ #
+ # font metrics
+
+ metrics = []
+
+ fp, format, i16, i32 = self._getformat(PCF_METRICS)
+
+ append = metrics.append
+
+ if (format & 0xff00) == 0x100:
+
+ # "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
+ xsize = right - left
+ ysize = ascent + descent
+ append(
+ (xsize, ysize, left, right, width,
+ ascent, descent, 0)
+ )
+
+ else:
+
+ # "jumbo" metrics
+ for i in range(i32(fp.read(4))):
+ left = i16(fp.read(2))
+ right = i16(fp.read(2))
+ width = i16(fp.read(2))
+ ascent = i16(fp.read(2))
+ descent = i16(fp.read(2))
+ attributes = i16(fp.read(2))
+ xsize = right - left
+ ysize = ascent + descent
+ append(
+ (xsize, ysize, left, right, width,
+ ascent, descent, attributes)
+ )
+
+ return metrics
+
+ def _load_bitmaps(self, metrics):
+
+ #
+ # bitmap data
+
+ bitmaps = []
+
+ fp, format, i16, i32 = self._getformat(PCF_BITMAPS)
+
+ nbitmaps = i32(fp.read(4))
+
+ if nbitmaps != len(metrics):
+ raise IOError, "Wrong number of bitmaps"
+
+ offsets = []
+ for i in range(nbitmaps):
+ offsets.append(i32(fp.read(4)))
+
+ bitmapSizes = []
+ for i in range(4):
+ bitmapSizes.append(i32(fp.read(4)))
+
+ byteorder = format & 4 # non-zero => MSB
+ bitorder = format & 8 # non-zero => MSB
+ padindex = format & 3
+
+ bitmapsize = bitmapSizes[padindex]
+ offsets.append(bitmapsize)
+
+ data = fp.read(bitmapsize)
+
+ pad = BYTES_PER_ROW[padindex]
+ mode = "1;R"
+ if bitorder:
+ mode = "1"
+
+ for i in range(nbitmaps):
+ 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))
+ )
+
+ return bitmaps
+
+ def _load_encoding(self):
+
+ # map character code to bitmap index
+ encoding = [None] * 256
+
+ fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS)
+
+ firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2))
+ firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2))
+
+ default = i16(fp.read(2))
+
+ nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1)
+
+ for i in range(nencoding):
+ encodingOffset = i16(fp.read(2))
+ if encodingOffset != 0xFFFF:
+ try:
+ encoding[i+firstCol] = encodingOffset
+ except IndexError:
+ break # only load ISO-8859-1 glyphs
+
+ return encoding
diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py
new file mode 100644
index 000000000..15012d51c
--- /dev/null
+++ b/PIL/PcxImagePlugin.py
@@ -0,0 +1,167 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PCX file handling
+#
+# This format was originally used by ZSoft's popular PaintBrush
+# program for the IBM PC. It is also supported by many MS-DOS and
+# Windows applications, including the Windows PaintBrush program in
+# Windows 3.
+#
+# history:
+# 1995-09-01 fl Created
+# 1996-05-20 fl Fixed RGB support
+# 1997-01-03 fl Fixed 2-bit and 4-bit support
+# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1)
+# 1999-02-07 fl Added write support
+# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust
+# 2002-07-30 fl Seek from to current position, not beginning of file
+# 2003-06-03 fl Extract DPI settings (info["dpi"])
+#
+# Copyright (c) 1997-2003 by Secret Labs AB.
+# Copyright (c) 1995-2003 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.6"
+
+import Image, ImageFile, ImagePalette
+
+def i16(c,o):
+ return ord(c[o]) + (ord(c[o+1])<<8)
+
+def _accept(prefix):
+ return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5]
+
+##
+# Image plugin for Paintbrush images.
+
+class PcxImageFile(ImageFile.ImageFile):
+
+ format = "PCX"
+ format_description = "Paintbrush"
+
+ def _open(self):
+
+ # header
+ s = self.fp.read(128)
+ if not _accept(s):
+ 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"
+
+ # format
+ version = ord(s[1])
+ bits = ord(s[3])
+ planes = ord(s[65])
+ stride = i16(s,66)
+
+ self.info["dpi"] = i16(s,12), i16(s,14)
+
+ if bits == 1 and planes == 1:
+ mode = rawmode = "1"
+
+ elif bits == 1 and planes in (2, 4):
+ mode = "P"
+ rawmode = "P;%dL" % planes
+ self.palette = ImagePalette.raw("RGB", s[16:64])
+
+ elif version == 5 and bits == 8 and planes == 1:
+ mode = rawmode = "L"
+ # 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:
+ # check if the palette is linear greyscale
+ for i in range(256):
+ if s[i*3+1:i*3+4] != chr(i)*3:
+ mode = rawmode = "P"
+ break
+ if mode == "P":
+ self.palette = ImagePalette.raw("RGB", s[1:])
+ self.fp.seek(128)
+
+ elif version == 5 and bits == 8 and planes == 3:
+ mode = "RGB"
+ rawmode = "RGB;L"
+
+ else:
+ raise IOError, "unknown PCX mode"
+
+ self.mode = mode
+ self.size = bbox[2]-bbox[0], bbox[3]-bbox[1]
+
+ bbox = (0, 0) + self.size
+
+ self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
+
+# --------------------------------------------------------------------
+# save PCX files
+
+SAVE = {
+ # mode: (version, bits, planes, raw mode)
+ "1": (2, 1, 1, "1"),
+ "L": (5, 8, 1, "L"),
+ "P": (5, 8, 1, "P"),
+ "RGB": (5, 8, 3, "RGB;L"),
+}
+
+def o16(i):
+ return chr(i&255) + chr(i>>8&255)
+
+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
+
+ if check:
+ return check
+
+ # bytes per plane
+ 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...
+
+ screen = im.size
+
+ dpi = 100, 100
+
+ # PCX header
+ fp.write(
+ chr(10) + chr(version) + chr(1) + chr(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(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) +
+ chr(0)*54
+ )
+
+ assert fp.tell() == 128
+
+ ImageFile._save(im, fp, [("pcx", (0,0)+im.size, 0,
+ (rawmode, bits*planes))])
+
+ if im.mode == "P":
+ # colour palette
+ fp.write(chr(12))
+ fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes
+ elif im.mode == "L":
+ # greyscale palette
+ fp.write(chr(12))
+ for i in range(256):
+ fp.write(chr(i)*3)
+
+# --------------------------------------------------------------------
+# registry
+
+Image.register_open("PCX", PcxImageFile, _accept)
+Image.register_save("PCX", _save)
+
+Image.register_extension("PCX", ".pcx")
diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py
new file mode 100644
index 000000000..4f957765c
--- /dev/null
+++ b/PIL/PdfImagePlugin.py
@@ -0,0 +1,211 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PDF (Acrobat) file handling
+#
+# History:
+# 1996-07-16 fl Created
+# 1997-01-18 fl Fixed header
+# 2004-02-21 fl Fixes for 1/L/CMYK images, etc.
+# 2004-02-24 fl Fixes for 1 and P images.
+#
+# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved.
+# Copyright (c) 1996-1997 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+##
+# Image plugin for PDF images (output only).
+##
+
+__version__ = "0.4"
+
+import Image, ImageFile
+import StringIO
+
+
+#
+# --------------------------------------------------------------------
+
+# object ids:
+# 1. catalogue
+# 2. pages
+# 3. image
+# 4. page
+# 5. page contents
+
+def _obj(fp, obj, **dict):
+ fp.write("%d 0 obj\n" % obj)
+ if dict:
+ fp.write("<<\n")
+ for k, v in dict.items():
+ if v is not None:
+ fp.write("/%s %s\n" % (k, v))
+ fp.write(">>\n")
+
+def _endobj(fp):
+ fp.write("endobj\n")
+
+##
+# (Internal) Image save plugin for the PDF format.
+
+def _save(im, fp, filename):
+ resolution = im.encoderinfo.get("resolution", 72.0)
+
+ #
+ # make sure image data is available
+ im.load()
+
+ xref = [0]*(5+1) # placeholders
+
+ fp.write("%PDF-1.2\n")
+ fp.write("% created by PIL PDF driver " + __version__ + "\n")
+
+ #
+ # Get image characteristics
+
+ width, height = im.size
+
+ # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits)
+ # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports
+ # Flatedecode (zip compression).
+
+ bits = 8
+ params = None
+
+ if im.mode == "1":
+ filter = "/ASCIIHexDecode"
+ colorspace = "/DeviceGray"
+ procset = "/ImageB" # grayscale
+ bits = 1
+ elif im.mode == "L":
+ filter = "/DCTDecode"
+ # params = "<< /Predictor 15 /Columns %d >>" % (width-2)
+ colorspace = "/DeviceGray"
+ procset = "/ImageB" # grayscale
+ elif im.mode == "P":
+ filter = "/ASCIIHexDecode"
+ 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])
+ colorspace = colorspace + "%02x%02x%02x " % (r, g, b)
+ colorspace = colorspace + "> ]"
+ procset = "/ImageI" # indexed color
+ elif im.mode == "RGB":
+ filter = "/DCTDecode"
+ colorspace = "/DeviceRGB"
+ procset = "/ImageC" # color images
+ elif im.mode == "CMYK":
+ filter = "/DCTDecode"
+ colorspace = "/DeviceCMYK"
+ procset = "/ImageC" # color images
+ else:
+ raise ValueError("cannot save mode %s" % im.mode)
+
+ #
+ # catalogue
+
+ xref[1] = fp.tell()
+ _obj(fp, 1, Type = "/Catalog",
+ Pages = "2 0 R")
+ _endobj(fp)
+
+ #
+ # pages
+
+ xref[2] = fp.tell()
+ _obj(fp, 2, Type = "/Pages",
+ Count = 1,
+ Kids = "[4 0 R]")
+ _endobj(fp)
+
+ #
+ # image
+
+ op = StringIO.StringIO()
+
+ if filter == "/ASCIIHexDecode":
+ if bits == 1:
+ # FIXME: the hex encoder doesn't support packed 1-bit
+ # images; do things the hard way...
+ data = im.tostring("raw", "1")
+ im = Image.new("L", (len(data), 1), None)
+ im.putdata(data)
+ ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)])
+ elif filter == "/DCTDecode":
+ ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)])
+ elif filter == "/FlateDecode":
+ ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)])
+ elif filter == "/RunLengthDecode":
+ ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)])
+ else:
+ raise ValueError("unsupported PDF filter (%s)" % filter)
+
+ xref[3] = fp.tell()
+ _obj(fp, 3, Type = "/XObject",
+ Subtype = "/Image",
+ Width = width, # * 72.0 / resolution,
+ Height = height, # * 72.0 / resolution,
+ Length = len(op.getvalue()),
+ Filter = filter,
+ BitsPerComponent = bits,
+ DecodeParams = params,
+ ColorSpace = colorspace)
+
+ fp.write("stream\n")
+ fp.write(op.getvalue())
+ fp.write("\nendstream\n")
+
+ _endobj(fp)
+
+ #
+ # page
+
+ xref[4] = fp.tell()
+ _obj(fp, 4)
+ fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\
+ "/Resources <<\n/ProcSet [ /PDF %s ]\n"\
+ "/XObject << /image 3 0 R >>\n>>\n"\
+ "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\
+ (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution)))
+ _endobj(fp)
+
+ #
+ # page contents
+
+ op = StringIO.StringIO()
+
+ 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()))
+
+ fp.write("stream\n")
+ fp.write(op.getvalue())
+ fp.write("\nendstream\n")
+
+ _endobj(fp)
+
+ #
+ # trailer
+ startxref = fp.tell()
+ fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref))
+ for x in xref[1:]:
+ fp.write("%010d 00000 n \n" % x)
+ fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref))
+ fp.write("startxref\n%d\n%%%%EOF\n" % startxref)
+ fp.flush()
+
+#
+# --------------------------------------------------------------------
+
+Image.register_save("PDF", _save)
+
+Image.register_extension("PDF", ".pdf")
+
+Image.register_mime("PDF", "application/pdf")
diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py
new file mode 100644
index 000000000..d4c597b61
--- /dev/null
+++ b/PIL/PixarImagePlugin.py
@@ -0,0 +1,71 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PIXAR raster support for PIL
+#
+# history:
+# 97-01-29 fl Created
+#
+# notes:
+# This is incomplete; it is based on a few samples created with
+# Photoshop 2.5 and 3.0, and a summary description provided by
+# Greg Coats . Hopefully, "L" and
+# "RGBA" support will be added in future versions.
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.1"
+
+import Image, ImageFile
+
+#
+# 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)
+
+##
+# Image plugin for PIXAR raster images.
+
+class PixarImageFile(ImageFile.ImageFile):
+
+ format = "PIXAR"
+ format_description = "PIXAR raster image"
+
+ def _open(self):
+
+ # 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"
+
+ # read rest of header
+ s = s + self.fp.read(508)
+
+ self.size = i16(s[418:420]), i16(s[416:418])
+
+ # get channel/depth descriptions
+ mode = i16(s[424:426]), i16(s[426:428])
+
+ if mode == (14, 2):
+ self.mode = "RGB"
+ # FIXME: to be continued...
+
+ # create tile descriptor (assuming "dumped")
+ self.tile = [("raw", (0,0)+self.size, 1024, (self.mode, 0, 1))]
+
+#
+# --------------------------------------------------------------------
+
+Image.register_open("PIXAR", PixarImageFile)
+
+#
+# FIXME: what's the standard extension?
diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py
new file mode 100644
index 000000000..0ee8589a5
--- /dev/null
+++ b/PIL/PngImagePlugin.py
@@ -0,0 +1,620 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PNG support code
+#
+# See "PNG (Portable Network Graphics) Specification, version 1.0;
+# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.).
+#
+# history:
+# 1996-05-06 fl Created (couldn't resist it)
+# 1996-12-14 fl Upgraded, added read and verify support (0.2)
+# 1996-12-15 fl Separate PNG stream parser
+# 1996-12-29 fl Added write support, added getchunks
+# 1996-12-30 fl Eliminated circular references in decoder (0.3)
+# 1998-07-12 fl Read/write 16-bit images as mode I (0.4)
+# 2001-02-08 fl Added transparency support (from Zircon) (0.5)
+# 2001-04-16 fl Don't close data source in "open" method (0.6)
+# 2004-02-24 fl Don't even pretend to support interlaced files (0.7)
+# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8)
+# 2004-09-20 fl Added PngInfo chunk container
+# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev)
+# 2008-08-13 fl Added tRNS support for RGB images
+# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech)
+# 2009-03-08 fl Added zTXT support (from Lowell Alleman)
+# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua)
+#
+# Copyright (c) 1997-2009 by Secret Labs AB
+# Copyright (c) 1996 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.9"
+
+import re, string
+
+import Image, ImageFile, ImagePalette, zlib
+
+
+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"
+
+
+_MODES = {
+ # supported bits/color combinations, and corresponding modes/rawmodes
+ (1, 0): ("1", "1"),
+ (2, 0): ("L", "L;2"),
+ (4, 0): ("L", "L;4"),
+ (8, 0): ("L", "L"),
+ (16,0): ("I", "I;16B"),
+ (8, 2): ("RGB", "RGB"),
+ (16,2): ("RGB", "RGB;16B"),
+ (1, 3): ("P", "P;1"),
+ (2, 3): ("P", "P;2"),
+ (4, 3): ("P", "P;4"),
+ (8, 3): ("P", "P"),
+ (8, 4): ("LA", "LA"),
+ (16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
+ (8, 6): ("RGBA", "RGBA"),
+ (16,6): ("RGBA", "RGBA;16B"),
+}
+
+
+# --------------------------------------------------------------------
+# Support classes. Suitable for PNG and related formats like MNG etc.
+
+class ChunkStream:
+
+ def __init__(self, fp):
+
+ self.fp = fp
+ self.queue = []
+
+ if not hasattr(Image.core, "crc32"):
+ self.crc = self.crc_skip
+
+ def read(self):
+ "Fetch a new chunk. Returns header information."
+
+ if self.queue:
+ cid, pos, len = self.queue[-1]
+ del self.queue[-1]
+ self.fp.seek(pos)
+ else:
+ s = self.fp.read(8)
+ cid = s[4:]
+ pos = self.fp.tell()
+ len = i32(s)
+
+ if not is_cid(cid):
+ raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
+
+ return cid, pos, len
+
+ def close(self):
+ self.queue = self.crc = self.fp = None
+
+ def push(self, cid, pos, len):
+
+ self.queue.append((cid, pos, len))
+
+ def call(self, cid, pos, len):
+ "Call the appropriate chunk handler"
+
+ if Image.DEBUG:
+ print "STREAM", cid, pos, len
+ return getattr(self, "chunk_" + cid)(pos, len)
+
+ def crc(self, cid, data):
+ "Read and verify checksum"
+
+ 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
+
+ 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"):
+
+ # Simple approach; just calculate checksum for all remaining
+ # blocks. Must be called directly after open.
+
+ cids = []
+
+ while 1:
+ cid, pos, len = self.read()
+ if cid == endchunk:
+ break
+ self.crc(cid, ImageFile._safe_read(self.fp, len))
+ cids.append(cid)
+
+ return cids
+
+
+# --------------------------------------------------------------------
+# PNG chunk container (for use with save(pnginfo=))
+
+class PngInfo:
+
+ def __init__(self):
+ self.chunks = []
+
+ def add(self, cid, data):
+ self.chunks.append((cid, data))
+
+ def add_text(self, key, value, zip=0):
+ if zip:
+ import zlib
+ self.add("zTXt", key + "\0\0" + zlib.compress(value))
+ else:
+ self.add("tEXt", key + "\0" + value)
+
+# --------------------------------------------------------------------
+# PNG image stream (IHDR/IEND)
+
+class PngStream(ChunkStream):
+
+ def __init__(self, fp):
+
+ ChunkStream.__init__(self, fp)
+
+ # local copies of Image attributes
+ self.im_info = {}
+ self.im_text = {}
+ self.im_size = (0,0)
+ self.im_mode = None
+ self.im_tile = None
+ self.im_palette = None
+
+ def chunk_iCCP(self, pos, len):
+
+ # ICC profile
+ s = ImageFile._safe_read(self.fp, len)
+ # according to PNG spec, the iCCP chunk contains:
+ # Profile name 1-79 bytes (character string)
+ # 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))
+ if Image.DEBUG:
+ print "iCCP profile name", s[:i]
+ print "Compression method", ord(s[i])
+ comp_method = ord(s[i])
+ if comp_method != 0:
+ raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method)
+ try:
+ icc_profile = zlib.decompress(s[i+2:])
+ except zlib.error:
+ icc_profile = None # FIXME
+ self.im_info["icc_profile"] = icc_profile
+ return s
+
+ def chunk_IHDR(self, pos, len):
+
+ # image header
+ 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]))]
+ except:
+ pass
+ if ord(s[12]):
+ self.im_info["interlace"] = 1
+ if ord(s[11]):
+ raise SyntaxError, "unknown filter category"
+ return s
+
+ def chunk_IDAT(self, pos, len):
+
+ # image data
+ self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
+ self.im_idat = len
+ raise EOFError
+
+ def chunk_IEND(self, pos, len):
+
+ # end of PNG image
+ raise EOFError
+
+ def chunk_PLTE(self, pos, len):
+
+ # palette
+ s = ImageFile._safe_read(self.fp, len)
+ if self.im_mode == "P":
+ self.im_palette = "RGB", s
+ return s
+
+ def chunk_tRNS(self, pos, len):
+
+ # 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
+ elif self.im_mode == "L":
+ self.im_info["transparency"] = i16(s)
+ elif self.im_mode == "RGB":
+ self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
+ return s
+
+ def chunk_gAMA(self, pos, len):
+
+ # gamma setting
+ s = ImageFile._safe_read(self.fp, len)
+ self.im_info["gamma"] = i32(s) / 100000.0
+ return s
+
+ def chunk_pHYs(self, pos, len):
+
+ # pixels per unit
+ s = ImageFile._safe_read(self.fp, len)
+ px, py = i32(s), i32(s[4:])
+ unit = ord(s[8])
+ if unit == 1: # meter
+ dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
+ self.im_info["dpi"] = dpi
+ elif unit == 0:
+ self.im_info["aspect"] = px, py
+ return s
+
+ def chunk_tEXt(self, pos, len):
+
+ # text
+ s = ImageFile._safe_read(self.fp, len)
+ try:
+ k, v = string.split(s, "\0", 1)
+ except ValueError:
+ k = s; v = "" # fallback for broken tEXt tags
+ if k:
+ self.im_info[k] = self.im_text[k] = v
+ return s
+
+ def chunk_zTXt(self, pos, len):
+
+ # compressed text
+ s = ImageFile._safe_read(self.fp, len)
+ k, v = string.split(s, "\0", 1)
+ comp_method = ord(v[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:])
+ return s
+
+# --------------------------------------------------------------------
+# PNG reader
+
+def _accept(prefix):
+ return prefix[:8] == _MAGIC
+
+##
+# Image plugin for PNG images.
+
+class PngImageFile(ImageFile.ImageFile):
+
+ format = "PNG"
+ format_description = "Portable network graphics"
+
+ def _open(self):
+
+ if self.fp.read(8) != _MAGIC:
+ raise SyntaxError, "not a PNG file"
+
+ #
+ # Parse headers up to the first IDAT chunk
+
+ self.png = PngStream(self.fp)
+
+ while 1:
+
+ #
+ # get next chunk
+
+ cid, pos, len = self.png.read()
+
+ try:
+ s = self.png.call(cid, pos, len)
+ except EOFError:
+ break
+ except AttributeError:
+ if Image.DEBUG:
+ print cid, pos, len, "(unknown)"
+ s = ImageFile._safe_read(self.fp, len)
+
+ self.png.crc(cid, s)
+
+ #
+ # Copy relevant attributes from the PngStream. An alternative
+ # would be to let the PngStream class modify these attributes
+ # directly, but that introduces circular references which are
+ # difficult to break if things go wrong in the decoder...
+ # (believe me, I've tried ;-)
+
+ self.mode = self.png.im_mode
+ self.size = self.png.im_size
+ self.info = self.png.im_info
+ self.text = self.png.im_text # experimental
+ self.tile = self.png.im_tile
+
+ if self.png.im_palette:
+ rawmode, data = self.png.im_palette
+ self.palette = ImagePalette.raw(rawmode, data)
+
+ self.__idat = len # used by load_read()
+
+
+ def verify(self):
+ "Verify PNG file"
+
+ if self.fp is None:
+ raise RuntimeError("verify must be called directly after open")
+
+ # back up to beginning of IDAT block
+ self.fp.seek(self.tile[0][2] - 8)
+
+ self.png.verify()
+ self.png.close()
+
+ self.fp = None
+
+ def load_prepare(self):
+ "internal: prepare to read PNG file"
+
+ if self.info.get("interlace"):
+ self.decoderconfig = self.decoderconfig + (1,)
+
+ ImageFile.ImageFile.load_prepare(self)
+
+ def load_read(self, bytes):
+ "internal: read more image data"
+
+ while self.__idat == 0:
+ # end of chunk, skip forward to next one
+
+ self.fp.read(4) # CRC
+
+ cid, pos, len = self.png.read()
+
+ if cid not in ["IDAT", "DDAT"]:
+ self.png.push(cid, pos, len)
+ return ""
+
+ self.__idat = len # empty chunks are allowed
+
+ # read more data from this chunk
+ if bytes <= 0:
+ bytes = self.__idat
+ else:
+ bytes = min(bytes, self.__idat)
+
+ self.__idat = self.__idat - bytes
+
+ return self.fp.read(bytes)
+
+
+ def load_end(self):
+ "internal: finished reading image data"
+
+ self.png.close()
+ self.png = None
+
+
+# --------------------------------------------------------------------
+# 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)
+
+_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)),
+}
+
+def putchunk(fp, cid, *data):
+ "Write a PNG chunk (including CRC field)"
+
+ data = string.join(data, "")
+
+ fp.write(o32(len(data)) + cid)
+ fp.write(data)
+ hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
+ fp.write(o16(hi) + o16(lo))
+
+class _idat:
+ # wrap output from the encoder in IDAT chunks
+
+ def __init__(self, fp, chunk):
+ self.fp = fp
+ self.chunk = chunk
+ def write(self, data):
+ self.chunk(self.fp, "IDAT", data)
+
+def _save(im, fp, filename, chunk=putchunk, check=0):
+ # save an image to disk (called by the save method)
+
+ mode = im.mode
+
+ if mode == "P":
+
+ #
+ # attempt to minimize storage requirements for palette images
+
+ if im.encoderinfo.has_key("bits"):
+
+ # number of bits specified by user
+ n = 1 << im.encoderinfo["bits"]
+
+ else:
+
+ # check palette contents
+ n = 256 # FIXME
+
+ if n <= 2:
+ bits = 1
+ elif n <= 4:
+ bits = 2
+ elif n <= 16:
+ bits = 4
+ else:
+ bits = 8
+
+ if bits != 8:
+ mode = "%s;%d" % (mode, bits)
+
+ # encoder options
+ if im.encoderinfo.has_key("dictionary"):
+ dictionary = im.encoderinfo["dictionary"]
+ else:
+ dictionary = ""
+
+ im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
+
+ # get the corresponding PNG mode
+ try:
+ rawmode, mode = _OUTMODES[mode]
+ except KeyError:
+ raise IOError, "cannot write mode %s as PNG" % mode
+
+ if check:
+ return check
+
+ #
+ # write minimal PNG file
+
+ fp.write(_MAGIC)
+
+ chunk(fp, "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
+
+ if im.mode == "P":
+ chunk(fp, "PLTE", im.im.getpalette("RGB"))
+
+ if im.encoderinfo.has_key("transparency"):
+ if im.mode == "P":
+ transparency = max(0, min(255, im.encoderinfo["transparency"]))
+ chunk(fp, "tRNS", chr(255) * transparency + chr(0))
+ elif im.mode == "L":
+ transparency = max(0, min(65535, im.encoderinfo["transparency"]))
+ chunk(fp, "tRNS", o16(transparency))
+ elif im.mode == "RGB":
+ red, green, blue = im.encoderinfo["transparency"]
+ chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue))
+ else:
+ raise IOError("cannot use transparency for this mode")
+
+ if 0:
+ # FIXME: to be supported some day
+ chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
+
+ dpi = im.encoderinfo.get("dpi")
+ if dpi:
+ chunk(fp, "pHYs",
+ o32(int(dpi[0] / 0.0254 + 0.5)),
+ o32(int(dpi[1] / 0.0254 + 0.5)),
+ chr(1))
+
+ info = im.encoderinfo.get("pnginfo")
+ if info:
+ for cid, data in info.chunks:
+ chunk(fp, cid, data)
+
+ # ICC profile writing support -- 2008-06-06 Florian Hoech
+ if im.info.has_key("icc_profile"):
+ # ICC profile
+ # according to PNG spec, the iCCP chunk contains:
+ # Profile name 1-79 bytes (character string)
+ # Null separator 1 byte (null character)
+ # Compression method 1 byte (0)
+ # Compressed profile n bytes (zlib with deflate compression)
+ try:
+ import ICCProfile
+ 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)
+
+ ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
+
+ chunk(fp, "IEND", "")
+
+ try:
+ fp.flush()
+ except:
+ pass
+
+
+# --------------------------------------------------------------------
+# PNG chunk converter
+
+def getchunks(im, **params):
+ """Return a list of PNG chunks representing this image."""
+
+ class collector:
+ data = []
+ def write(self, data):
+ pass
+ def append(self, chunk):
+ self.data.append(chunk)
+
+ def append(fp, cid, *data):
+ data = string.join(data, "")
+ hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
+ crc = o16(hi) + o16(lo)
+ fp.append((cid, data, crc))
+
+ fp = collector()
+
+ try:
+ im.encoderinfo = params
+ _save(im, fp, None, append)
+ finally:
+ del im.encoderinfo
+
+ return fp.data
+
+
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open("PNG", PngImageFile, _accept)
+Image.register_save("PNG", _save)
+
+Image.register_extension("PNG", ".png")
+
+Image.register_mime("PNG", "image/png")
diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py
new file mode 100644
index 000000000..e86146c10
--- /dev/null
+++ b/PIL/PpmImagePlugin.py
@@ -0,0 +1,131 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# PPM support for PIL
+#
+# History:
+# 96-03-24 fl Created
+# 98-03-06 fl Write RGBA images (as RGB, that is)
+#
+# Copyright (c) Secret Labs AB 1997-98.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.2"
+
+import string
+
+import Image, ImageFile
+
+#
+# --------------------------------------------------------------------
+
+MODES = {
+ # standard
+ "P4": "1",
+ "P5": "L",
+ "P6": "RGB",
+ # extensions
+ "P0CMYK": "CMYK",
+ # PIL extensions (for test purposes only)
+ "PyP": "P",
+ "PyRGBA": "RGBA",
+ "PyCMYK": "CMYK"
+}
+
+def _accept(prefix):
+ return prefix[0] == "P" and prefix[1] in "0456y"
+
+##
+# Image plugin for PBM, PGM, and PPM images.
+
+class PpmImageFile(ImageFile.ImageFile):
+
+ format = "PPM"
+ format_description = "Pbmplus image"
+
+ def _token(self, s = ""):
+ while 1: # read until next whitespace
+ c = self.fp.read(1)
+ if not c or c in string.whitespace:
+ break
+ s = s + c
+ return s
+
+ def _open(self):
+
+ # check magic
+ s = self.fp.read(1)
+ if s != "P":
+ raise SyntaxError, "not a PPM file"
+ mode = MODES[self._token(s)]
+
+ if mode == "1":
+ self.mode = "1"
+ rawmode = "1;I"
+ else:
+ self.mode = rawmode = mode
+
+ for ix in range(3):
+ while 1:
+ while 1:
+ s = self.fp.read(1)
+ if s not in string.whitespace:
+ break
+ if s != "#":
+ break
+ s = self.fp.readline()
+ s = int(self._token(s))
+ if ix == 0:
+ xsize = s
+ elif ix == 1:
+ ysize = s
+ if mode == "1":
+ break
+
+ self.size = xsize, ysize
+ self.tile = [("raw",
+ (0, 0, xsize, ysize),
+ self.fp.tell(),
+ (rawmode, 0, 1))]
+
+ # ALTERNATIVE: load via builtin debug function
+ # self.im = Image.core.open_ppm(self.filename)
+ # self.mode = self.im.mode
+ # self.size = self.im.size
+
+#
+# --------------------------------------------------------------------
+
+def _save(im, fp, filename):
+ if im.mode == "1":
+ rawmode, head = "1;I", "P4"
+ elif im.mode == "L":
+ rawmode, head = "L", "P5"
+ elif im.mode == "RGB":
+ rawmode, head = "RGB", "P6"
+ elif im.mode == "RGBA":
+ rawmode, head = "RGB", "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")
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))])
+
+ # ALTERNATIVE: save via builtin debug function
+ # im._dump(filename)
+
+#
+# --------------------------------------------------------------------
+
+Image.register_open("PPM", PpmImageFile, _accept)
+Image.register_save("PPM", _save)
+
+Image.register_extension("PPM", ".pbm")
+Image.register_extension("PPM", ".pgm")
+Image.register_extension("PPM", ".ppm")
diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py
new file mode 100644
index 000000000..9d1b17a36
--- /dev/null
+++ b/PIL/PsdImagePlugin.py
@@ -0,0 +1,291 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# Adobe PSD 2.5/3.0 file handling
+#
+# History:
+# 1995-09-01 fl Created
+# 1997-01-03 fl Read most PSD images
+# 1997-01-18 fl Fixed P and CMYK support
+# 2001-10-21 fl Added seek/tell support (for layers)
+#
+# Copyright (c) 1997-2001 by Secret Labs AB.
+# Copyright (c) 1995-2001 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.4"
+
+import Image, ImageFile, ImagePalette
+
+MODES = {
+ # (photoshop mode, bits) -> (pil mode, required channels)
+ (0, 1): ("1", 1),
+ (0, 8): ("L", 1),
+ (1, 8): ("L", 1),
+ (2, 8): ("P", 1),
+ (3, 8): ("RGB", 3),
+ (4, 8): ("CMYK", 4),
+ (7, 8): ("L", 1), # FIXME: multilayer
+ (8, 8): ("L", 1), # duotone
+ (9, 8): ("LAB", 3)
+}
+
+#
+# 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)
+
+# --------------------------------------------------------------------.
+# read PSD images
+
+def _accept(prefix):
+ return prefix[:4] == "8BPS"
+
+##
+# Image plugin for Photoshop images.
+
+class PsdImageFile(ImageFile.ImageFile):
+
+ format = "PSD"
+ format_description = "Adobe Photoshop"
+
+ def _open(self):
+
+ read = self.fp.read
+
+ #
+ # header
+
+ s = read(26)
+ if s[:4] != "8BPS" or i16(s[4:]) != 1:
+ raise SyntaxError, "not a PSD file"
+
+ psd_bits = i16(s[22:])
+ psd_channels = i16(s[12:])
+ psd_mode = i16(s[24:])
+
+ mode, channels = MODES[(psd_mode, psd_bits)]
+
+ if channels > psd_channels:
+ raise IOError, "not enough channels"
+
+ self.mode = mode
+ self.size = i32(s[18:]), i32(s[14:])
+
+ #
+ # color mode data
+
+ size = i32(read(4))
+ if size:
+ data = read(size)
+ if mode == "P" and size == 768:
+ self.palette = ImagePalette.raw("RGB;L", data)
+
+ #
+ # image resources
+
+ self.resources = []
+
+ size = i32(read(4))
+ if size:
+ # load resources
+ end = self.fp.tell() + size
+ while self.fp.tell() < end:
+ signature = read(4)
+ id = i16(read(2))
+ name = read(ord(read(1)))
+ if not (len(name) & 1):
+ read(1) # padding
+ data = read(i32(read(4)))
+ if (len(data) & 1):
+ read(1) # padding
+ self.resources.append((id, name, data))
+ if id == 1039: # ICC profile
+ self.info["icc_profile"] = data
+
+ #
+ # layer and mask information
+
+ self.layers = []
+
+ size = i32(read(4))
+ if size:
+ end = self.fp.tell() + size
+ size = i32(read(4))
+ if size:
+ self.layers = _layerinfo(self.fp)
+ self.fp.seek(end)
+
+ #
+ # image descriptor
+
+ self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels)
+
+ # keep the file open
+ self._fp = self.fp
+ self.frame = 0
+
+ def seek(self, layer):
+ # seek to given layer (1..max)
+ if layer == self.frame:
+ return
+ try:
+ if layer <= 0:
+ raise IndexError
+ name, mode, bbox, tile = self.layers[layer-1]
+ self.mode = mode
+ self.tile = tile
+ self.frame = layer
+ self.fp = self._fp
+ return name, bbox
+ except IndexError:
+ raise EOFError, "no such layer"
+
+ def tell(self):
+ # return layer number (0=image, 1..max=layers)
+ return self.frame
+
+ def load_prepare(self):
+ # create image memory if necessary
+ if not self.im or\
+ self.im.mode != self.mode or self.im.size != self.size:
+ self.im = Image.core.fill(self.mode, self.size, 0)
+ # create palette (optional)
+ if self.mode == "P":
+ Image.Image.load(self)
+
+def _layerinfo(file):
+ # read layerinfo block
+ layers = []
+ read = file.read
+
+ for i in range(abs(i16(read(2)))):
+
+ # bounding box
+ y0 = i32(read(4)); x0 = i32(read(4))
+ y1 = i32(read(4)); x1 = i32(read(4))
+
+ # image info
+ info = []
+ mode = []
+ for i in range(i16(read(2))):
+ type = i16(read(2))
+ if type == 65535:
+ m = "A"
+ else:
+ m = "RGB"[type]
+ mode.append(m)
+ size = i32(read(4))
+ info.append((m, size))
+
+ # figure out the image mode
+ mode.sort()
+ if mode == ["R"]:
+ mode = "L"
+ elif mode == ["B", "G", "R"]:
+ mode = "RGB"
+ elif mode == ["A", "B", "G", "R"]:
+ mode = "RGBA"
+ else:
+ mode = None # unknown
+
+ # skip over blend flags and extra information
+ filler = read(12)
+ name = ""
+ size = i32(read(4))
+ combined = 0
+ if size:
+ length = i32(read(4))
+ if length:
+ mask_y = i32(read(4)); mask_x = i32(read(4))
+ mask_h = i32(read(4)) - mask_y; mask_w = i32(read(4)) - mask_x
+ file.seek(length - 16, 1)
+ combined += length + 4
+
+ length = i32(read(4))
+ if length:
+ file.seek(length, 1)
+ combined += length + 4
+
+ length = ord(read(1))
+ if length:
+ name = read(length)
+ combined += length + 1
+
+ file.seek(size - combined, 1)
+ layers.append((name, mode, (x0, y0, x1, y1)))
+
+ # get tiles
+ i = 0
+ for name, mode, bbox in layers:
+ tile = []
+ for m in mode:
+ t = _maketile(file, m, bbox, 1)
+ if t:
+ tile.extend(t)
+ layers[i] = name, mode, bbox, tile
+ i = i + 1
+
+ return layers
+
+def _maketile(file, mode, bbox, channels):
+
+ tile = None
+ read = file.read
+
+ compression = i16(read(2))
+
+ xsize = bbox[2] - bbox[0]
+ ysize = bbox[3] - bbox[1]
+
+ offset = file.tell()
+
+ if compression == 0:
+ #
+ # raw compression
+ tile = []
+ for channel in range(channels):
+ layer = mode[channel]
+ if mode == "CMYK":
+ layer = layer + ";I"
+ tile.append(("raw", bbox, offset, layer))
+ offset = offset + xsize*ysize
+
+ elif compression == 1:
+ #
+ # packbits compression
+ i = 0
+ tile = []
+ bytecount = read(channels * ysize * 2)
+ offset = file.tell()
+ for channel in range(channels):
+ layer = mode[channel]
+ if mode == "CMYK":
+ layer = layer + ";I"
+ tile.append(
+ ("packbits", bbox, offset, layer)
+ )
+ for y in range(ysize):
+ offset = offset + i16(bytecount[i:i+2])
+ i = i + 2
+
+ file.seek(offset)
+
+ if offset & 1:
+ read(1) # padding
+
+ return tile
+
+# --------------------------------------------------------------------
+# registry
+
+Image.register_open("PSD", PsdImageFile, _accept)
+
+Image.register_extension("PSD", ".psd")
diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py
new file mode 100644
index 000000000..c65b91aca
--- /dev/null
+++ b/PIL/SgiImagePlugin.py
@@ -0,0 +1,92 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# SGI image file handling
+#
+# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
+#
+#
+# History:
+# 1995-09-10 fl Created
+#
+# Copyright (c) 2008 by Karsten Hiddemann.
+# Copyright (c) 1997 by Secret Labs AB.
+# Copyright (c) 1995 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.2"
+
+
+import Image, ImageFile
+
+
+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 _accept(prefix):
+ return i16(prefix) == 474
+
+##
+# Image plugin for SGI images.
+
+class SgiImageFile(ImageFile.ImageFile):
+
+ format = "SGI"
+ format_description = "SGI Image File Format"
+
+ def _open(self):
+
+ # HEAD
+ s = self.fp.read(512)
+ if i16(s) != 474:
+ raise SyntaxError("not an SGI image file")
+
+ # relevant header entries
+ compression = ord(s[2])
+
+ # bytes, dimension, zsize
+ layout = ord(s[3]), i16(s[4:]), i16(s[10:])
+
+ # determine mode from bytes/zsize
+ if layout == (1, 2, 1) or layout == (1, 1, 1):
+ self.mode = "L"
+ elif layout == (1, 3, 3):
+ self.mode = "RGB"
+ elif layout == (1, 3, 4):
+ self.mode = "RGBA"
+ else:
+ raise SyntaxError("unsupported SGI image mode")
+
+ # size
+ self.size = i16(s[6:]), i16(s[8:])
+
+
+ # decoder info
+ if compression == 0:
+ offset = 512
+ pagesize = self.size[0]*self.size[1]*layout[0]
+ self.tile = []
+ for layer in self.mode:
+ self.tile.append(("raw", (0,0)+self.size, offset, (layer,0,-1)))
+ offset = offset + pagesize
+ elif compression == 1:
+ self.tile = [("sgi_rle", (0,0)+self.size, 512, (self.mode, 0, -1))]
+
+#
+# registry
+
+Image.register_open("SGI", SgiImageFile, _accept)
+
+Image.register_extension("SGI", ".bw")
+Image.register_extension("SGI", ".rgb")
+Image.register_extension("SGI", ".rgba")
+
+Image.register_extension("SGI", ".sgi") # really?
diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py
new file mode 100644
index 000000000..e73d55918
--- /dev/null
+++ b/PIL/SpiderImagePlugin.py
@@ -0,0 +1,294 @@
+#
+# The Python Imaging Library.
+#
+# SPIDER image file handling
+#
+# History:
+# 2004-08-02 Created BB
+# 2006-03-02 added save method
+# 2006-03-13 added support for stack images
+#
+# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144.
+# Copyright (c) 2004 by William Baxter.
+# Copyright (c) 2004 by Secret Labs AB.
+# Copyright (c) 2004 by Fredrik Lundh.
+#
+
+##
+# Image plugin for the Spider image format. This format is is used
+# by the SPIDER software, in processing image data from electron
+# microscopy and tomography.
+##
+
+#
+# SpiderImagePlugin.py
+#
+# The Spider image format is used by SPIDER software, in processing
+# image data from electron microscopy and tomography.
+#
+# Spider home page:
+# http://www.wadsworth.org/spider_doc/spider/docs/spider.html
+#
+# Details about the Spider image format:
+# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html
+#
+
+import Image, ImageFile
+import os, struct, sys
+
+def isInt(f):
+ try:
+ i = int(f)
+ if f-i == 0: return 1
+ else: return 0
+ except:
+ return 0
+
+iforms = [1,3,-11,-12,-21,-22]
+
+# There is no magic number to identify Spider files, so just check a
+# series of header locations to see if they have reasonable values.
+# Returns no.of bytes in the header, if it is a valid Spider header,
+# otherwise returns 0
+
+def isSpiderHeader(t):
+ h = (99,) + t # add 1 value so can use spider header index start=1
+ # header values 1,2,5,12,13,22,23 should be integers
+ for i in [1,2,5,12,13,22,23]:
+ if not isInt(h[i]): return 0
+ # check iform
+ iform = int(h[5])
+ if not iform in iforms: return 0
+ # check other header values
+ labrec = int(h[13]) # no. records in file header
+ labbyt = int(h[22]) # total no. of bytes in header
+ lenbyt = int(h[23]) # record length in bytes
+ #print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
+ if labbyt != (labrec * lenbyt): return 0
+ # looks like a valid header
+ return labbyt
+
+def isSpiderImage(filename):
+ fp = open(filename,'rb')
+ f = fp.read(92) # read 23 * 4 bytes
+ fp.close()
+ bigendian = 1
+ t = struct.unpack('>23f',f) # try big-endian first
+ hdrlen = isSpiderHeader(t)
+ if hdrlen == 0:
+ bigendian = 0
+ t = struct.unpack('<23f',f) # little-endian
+ hdrlen = isSpiderHeader(t)
+ return hdrlen
+
+
+class SpiderImageFile(ImageFile.ImageFile):
+
+ format = "SPIDER"
+ format_description = "Spider 2D image"
+
+ def _open(self):
+ # check header
+ n = 27 * 4 # read 27 float values
+ f = self.fp.read(n)
+
+ try:
+ self.bigendian = 1
+ t = struct.unpack('>27f',f) # try big-endian first
+ hdrlen = isSpiderHeader(t)
+ if hdrlen == 0:
+ self.bigendian = 0
+ t = struct.unpack('<27f',f) # little-endian
+ hdrlen = isSpiderHeader(t)
+ if hdrlen == 0:
+ raise SyntaxError, "not a valid Spider file"
+ except struct.error:
+ 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"
+
+ self.size = int(h[12]), int(h[2]) # size in pixels (width, height)
+ self.istack = int(h[24])
+ self.imgnumber = int(h[27])
+
+ if self.istack == 0 and self.imgnumber == 0:
+ # stk=0, img=0: a regular 2D image
+ offset = hdrlen
+ self.nimages = 1
+ elif self.istack > 0 and self.imgnumber == 0:
+ # stk>0, img=0: Opening the stack for the first time
+ self.imgbytes = int(h[12]) * int(h[2]) * 4
+ self.hdrlen = hdrlen
+ self.nimages = int(h[26])
+ # Point to the first image in the stack
+ offset = hdrlen * 2
+ self.imgnumber = 1
+ elif self.istack == 0 and self.imgnumber > 0:
+ # stk=0, img>0: an image within the stack
+ offset = hdrlen + self.stkoffset
+ self.istack = 2 # So Image knows it's still a stack
+ else:
+ raise SyntaxError, "inconsistent stack header values"
+
+ if self.bigendian:
+ self.rawmode = "F;32BF"
+ else:
+ self.rawmode = "F;32F"
+ self.mode = "F"
+
+ self.tile = [("raw", (0, 0) + self.size, offset,
+ (self.rawmode, 0, 1))]
+ self.__fp = self.fp # FIXME: hack
+
+ # 1st image index is zero (although SPIDER imgnumber starts at 1)
+ def tell(self):
+ if self.imgnumber < 1:
+ return 0
+ else:
+ return self.imgnumber - 1
+
+ def seek(self, frame):
+ if self.istack == 0:
+ return
+ if frame >= self.nimages:
+ 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)
+ self._open()
+
+ # returns a byte image after rescaling to 0..255
+ def convert2byte(self, depth=255):
+ (min, max) = self.getextrema()
+ m = 1
+ if max != min:
+ m = depth / (max-min)
+ b = -m * min
+ return self.point(lambda i, m=m, b=b: i * m + b).convert("L")
+
+ # returns a ImageTk.PhotoImage object, after rescaling to 0..255
+ def tkPhotoImage(self):
+ import ImageTk
+ return ImageTk.PhotoImage(self.convert2byte(), palette=256)
+
+# --------------------------------------------------------------------
+# Image series
+
+# given a list of filenames, return a list of images
+def loadImageSeries(filelist=None):
+ " create a list of Image.images for use in montage "
+ if filelist == None or len(filelist) < 1:
+ return
+
+ imglist = []
+ for img in filelist:
+ if not os.path.exists(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"
+ continue
+ im.info['filename'] = img
+ imglist.append(im)
+ return imglist
+
+# --------------------------------------------------------------------
+# For saving images in Spider format
+
+def makeSpiderHeader(im):
+ nsam,nrow = im.size
+ lenbyt = nsam * 4 # There are labrec records in the header
+ labrec = 1024 / lenbyt
+ if 1024%lenbyt != 0: labrec += 1
+ labbyt = labrec * lenbyt
+ hdr = []
+ nvalues = labbyt / 4
+ for i in range(nvalues):
+ hdr.append(0.0)
+
+ if len(hdr) < 23:
+ return []
+
+ # NB these are Fortran indices
+ hdr[1] = 1.0 # nslice (=1 for an image)
+ hdr[2] = float(nrow) # number of rows per slice
+ hdr[5] = 1.0 # iform for 2D image
+ hdr[12] = float(nsam) # number of pixels per line
+ hdr[13] = float(labrec) # number of records in file header
+ hdr[22] = float(labbyt) # total number of bytes in header
+ hdr[23] = float(lenbyt) # record length in bytes
+
+ # adjust for Fortran indexing
+ hdr = hdr[1:]
+ hdr.append(0.0)
+ # pack binary data into a string
+ hdrstr = []
+ for v in hdr:
+ hdrstr.append(struct.pack('f',v))
+ return hdrstr
+
+def _save(im, fp, filename):
+ if im.mode[0] != "F":
+ im = im.convert('F')
+
+ hdr = makeSpiderHeader(im)
+ if len(hdr) < 256:
+ 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
+ fp.writelines(hdr)
+
+ rawmode = "F;32NF" #32-bit native floating point
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))])
+
+ fp.close()
+
+def _save_spider(im, fp, filename):
+ # get the filename extension and register it with Image
+ fn, ext = os.path.splitext(filename)
+ Image.register_extension("SPIDER", ext)
+ _save(im, fp, filename)
+
+# --------------------------------------------------------------------
+
+Image.register_open("SPIDER", SpiderImageFile)
+Image.register_save("SPIDER", _save_spider)
+
+if __name__ == "__main__":
+
+ if not sys.argv[1:]:
+ 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"
+ sys.exit()
+
+ outfile = ""
+ if len(sys.argv[1:]) > 1:
+ 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()
+
+ 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)
+ im.save(outfile, "SPIDER")
diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py
new file mode 100644
index 000000000..d189562a4
--- /dev/null
+++ b/PIL/SunImagePlugin.py
@@ -0,0 +1,86 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# Sun image file handling
+#
+# History:
+# 1995-09-10 fl Created
+# 1996-05-28 fl Fixed 32-bit alignment
+# 1998-12-29 fl Import ImagePalette module
+# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault)
+#
+# Copyright (c) 1997-2001 by Secret Labs AB
+# Copyright (c) 1995-1996 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.3"
+
+
+import Image, ImageFile, ImagePalette
+
+
+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 _accept(prefix):
+ return i32(prefix) == 0x59a66a95
+
+##
+# Image plugin for Sun raster files.
+
+class SunImageFile(ImageFile.ImageFile):
+
+ format = "SUN"
+ format_description = "Sun Raster File"
+
+ def _open(self):
+
+ # HEAD
+ s = self.fp.read(32)
+ if i32(s) != 0x59a66a95:
+ raise SyntaxError, "not an SUN raster file"
+
+ offset = 32
+
+ self.size = i32(s[4:8]), i32(s[8:12])
+
+ depth = i32(s[12:16])
+ if depth == 1:
+ self.mode, rawmode = "1", "1;I"
+ elif depth == 8:
+ self.mode = rawmode = "L"
+ elif depth == 24:
+ self.mode, rawmode = "RGB", "BGR"
+ else:
+ raise SyntaxError, "unsupported mode"
+
+ compression = i32(s[20:24])
+
+ if i32(s[24:28]) != 0:
+ length = i32(s[28:32])
+ offset = offset + length
+ self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
+ if self.mode == "L":
+ self.mode = rawmode = "P"
+
+ stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3)
+
+ if compression == 1:
+ self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))]
+ elif compression == 2:
+ self.tile = [("sun_rle", (0,0)+self.size, offset, rawmode)]
+
+#
+# registry
+
+Image.register_open("SUN", SunImageFile, _accept)
+
+Image.register_extension("SUN", ".ras")
diff --git a/PIL/TarIO.py b/PIL/TarIO.py
new file mode 100644
index 000000000..d5de729cd
--- /dev/null
+++ b/PIL/TarIO.py
@@ -0,0 +1,57 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# read files from within a tar file
+#
+# History:
+# 95-06-18 fl Created
+# 96-05-28 fl Open files in binary mode
+#
+# Copyright (c) Secret Labs AB 1997.
+# Copyright (c) Fredrik Lundh 1995-96.
+#
+# See the README file for information on usage and redistribution.
+#
+
+import ContainerIO
+import string
+
+##
+# A file object that provides read access to a given member of a TAR
+# file.
+
+class TarIO(ContainerIO.ContainerIO):
+
+ ##
+ # Create file object.
+ #
+ # @param tarfile Name of TAR file.
+ # @param file Name of member file.
+
+ def __init__(self, tarfile, file):
+
+ fh = open(tarfile, "rb")
+
+ while 1:
+
+ s = fh.read(512)
+ if len(s) != 512:
+ raise IOError, "unexpected end of tar file"
+
+ name = s[:100]
+ i = string.find(name, chr(0))
+ if i == 0:
+ raise IOError, "cannot find subfile"
+ if i > 0:
+ name = name[:i]
+
+ size = string.atoi(s[124:136], 8)
+
+ if file == name:
+ break
+
+ fh.seek((size + 511) & (~511), 1)
+
+ # Open region
+ ContainerIO.ContainerIO.__init__(self, fh, fh.tell(), size)
diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py
new file mode 100644
index 000000000..3375991ca
--- /dev/null
+++ b/PIL/TgaImagePlugin.py
@@ -0,0 +1,201 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# TGA file handling
+#
+# History:
+# 95-09-01 fl created (reads 24-bit files only)
+# 97-01-04 fl support more TGA versions, including compressed images
+# 98-07-04 fl fixed orientation and alpha layer bugs
+# 98-09-11 fl fixed orientation for runlength decoder
+#
+# Copyright (c) Secret Labs AB 1997-98.
+# Copyright (c) Fredrik Lundh 1995-97.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.3"
+
+import Image, ImageFile, ImagePalette
+
+
+#
+# --------------------------------------------------------------------
+# 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)
+
+
+MODES = {
+ # map imagetype/depth to rawmode
+ (1, 8): "P",
+ (3, 1): "1",
+ (3, 8): "L",
+ (2, 16): "BGR;5",
+ (2, 24): "BGR",
+ (2, 32): "BGRA",
+}
+
+
+def _accept(prefix):
+ return prefix[0] == "\0"
+
+##
+# Image plugin for Targa files.
+
+class TgaImageFile(ImageFile.ImageFile):
+
+ format = "TGA"
+ format_description = "Targa"
+
+ def _open(self):
+
+ # process header
+ s = self.fp.read(18)
+
+ id = ord(s[0])
+
+ colormaptype = ord(s[1])
+ imagetype = ord(s[2])
+
+ depth = ord(s[16])
+
+ flags = ord(s[17])
+
+ self.size = i16(s[12:]), i16(s[14:])
+
+ # validate header fields
+ 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"
+
+ # image mode
+ if imagetype in (3, 11):
+ self.mode = "L"
+ if depth == 1:
+ self.mode = "1" # ???
+ elif imagetype in (1, 9):
+ self.mode = "P"
+ elif imagetype in (2, 10):
+ self.mode = "RGB"
+ if depth == 32:
+ self.mode = "RGBA"
+ else:
+ raise SyntaxError, "unknown TGA mode"
+
+ # orientation
+ orientation = flags & 0x30
+ if orientation == 0x20:
+ orientation = 1
+ elif not orientation:
+ orientation = -1
+ else:
+ raise SyntaxError, "unknown TGA orientation"
+
+ self.info["orientation"] = orientation
+
+ if imagetype & 8:
+ self.info["compression"] = "tga_rle"
+
+ if colormaptype:
+ # read palette
+ 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))
+ elif mapdepth == 24:
+ self.palette = ImagePalette.raw("BGR",
+ "\0"*3*start + self.fp.read(3*size))
+ elif mapdepth == 32:
+ self.palette = ImagePalette.raw("BGRA",
+ "\0"*4*start + self.fp.read(4*size))
+
+ # setup tile descriptor
+ try:
+ rawmode = MODES[(imagetype&7, depth)]
+ if imagetype & 8:
+ # compressed
+ self.tile = [("tga_rle", (0, 0)+self.size,
+ self.fp.tell(), (rawmode, orientation, depth))]
+ else:
+ self.tile = [("raw", (0, 0)+self.size,
+ self.fp.tell(), (rawmode, 0, orientation))]
+ except KeyError:
+ pass # cannot decode
+
+#
+# --------------------------------------------------------------------
+# 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)
+
+SAVE = {
+ "1": ("1", 1, 0, 3),
+ "L": ("L", 8, 0, 3),
+ "P": ("P", 8, 1, 1),
+ "RGB": ("BGR", 24, 0, 2),
+ "RGBA": ("BGRA", 32, 0, 2),
+}
+
+def _save(im, fp, filename, check=0):
+
+ try:
+ rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
+ except KeyError:
+ raise IOError("cannot write mode %s as TGA" % im.mode)
+
+ if check:
+ return check
+
+ if colormaptype:
+ colormapfirst, colormaplength, colormapentry = 0, 256, 24
+ else:
+ colormapfirst, colormaplength, colormapentry = 0, 0, 0
+
+ if im.mode == "RGBA":
+ flags = 8
+ else:
+ flags = 0
+
+ orientation = im.info.get("orientation", -1)
+ if orientation > 0:
+ flags = flags | 0x20
+
+ fp.write("\000" +
+ chr(colormaptype) +
+ chr(imagetype) +
+ o16(colormapfirst) +
+ o16(colormaplength) +
+ chr(colormapentry) +
+ o16(0) +
+ o16(0) +
+ o16(im.size[0]) +
+ o16(im.size[1]) +
+ chr(bits) +
+ chr(flags))
+
+ if colormaptype:
+ fp.write(im.im.getpalette("RGB", "BGR"))
+
+ ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, orientation))])
+
+#
+# --------------------------------------------------------------------
+# Registry
+
+Image.register_open("TGA", TgaImageFile, _accept)
+Image.register_save("TGA", _save)
+
+Image.register_extension("TGA", ".tga")
diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py
new file mode 100644
index 000000000..8a0fde9cd
--- /dev/null
+++ b/PIL/TiffImagePlugin.py
@@ -0,0 +1,860 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# TIFF file handling
+#
+# TIFF is a flexible, if somewhat aged, image file format originally
+# defined by Aldus. Although TIFF supports a wide variety of pixel
+# layouts and compression methods, the name doesn't really stand for
+# "thousands of incompatible file formats," it just feels that way.
+#
+# To read TIFF data from a stream, the stream must be seekable. For
+# progressive decoding, make sure to use TIFF files where the tag
+# directory is placed first in the file.
+#
+# History:
+# 1995-09-01 fl Created
+# 1996-05-04 fl Handle JPEGTABLES tag
+# 1996-05-18 fl Fixed COLORMAP support
+# 1997-01-05 fl Fixed PREDICTOR support
+# 1997-08-27 fl Added support for rational tags (from Perry Stoll)
+# 1998-01-10 fl Fixed seek/tell (from Jan Blom)
+# 1998-07-15 fl Use private names for internal variables
+# 1999-06-13 fl Rewritten for PIL 1.0 (1.0)
+# 2000-10-11 fl Additional fixes for Python 2.0 (1.1)
+# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2)
+# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3)
+# 2001-12-18 fl Added workaround for broken Matrox library
+# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart)
+# 2003-05-19 fl Check FILLORDER tag
+# 2003-09-26 fl Added RGBa support
+# 2004-02-24 fl Added DPI support; fixed rational write support
+# 2005-02-07 fl Added workaround for broken Corel Draw 10 files
+# 2006-01-09 fl Added support for float/double tags (from Russell Nelson)
+#
+# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved.
+# Copyright (c) 1995-1997 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "1.3.5"
+
+import Image, ImageFile
+import ImagePalette
+
+import array, string, sys
+
+II = "II" # little-endian (intel-style)
+MM = "MM" # big-endian (motorola-style)
+
+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
+
+#
+# --------------------------------------------------------------------
+# 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)
+
+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)
+
+# a few tag names, just to make the code below a bit more readable
+IMAGEWIDTH = 256
+IMAGELENGTH = 257
+BITSPERSAMPLE = 258
+COMPRESSION = 259
+PHOTOMETRIC_INTERPRETATION = 262
+FILLORDER = 266
+IMAGEDESCRIPTION = 270
+STRIPOFFSETS = 273
+SAMPLESPERPIXEL = 277
+ROWSPERSTRIP = 278
+STRIPBYTECOUNTS = 279
+X_RESOLUTION = 282
+Y_RESOLUTION = 283
+PLANAR_CONFIGURATION = 284
+RESOLUTION_UNIT = 296
+SOFTWARE = 305
+DATE_TIME = 306
+ARTIST = 315
+PREDICTOR = 317
+COLORMAP = 320
+TILEOFFSETS = 324
+EXTRASAMPLES = 338
+SAMPLEFORMAT = 339
+JPEGTABLES = 347
+COPYRIGHT = 33432
+IPTC_NAA_CHUNK = 33723 # newsphoto properties
+PHOTOSHOP_CHUNK = 34377 # photoshop properties
+ICCPROFILE = 34675
+EXIFIFD = 34665
+XMP = 700
+
+COMPRESSION_INFO = {
+ # Compression => pil compression name
+ 1: "raw",
+ 2: "tiff_ccitt",
+ 3: "group3",
+ 4: "group4",
+ 5: "tiff_lzw",
+ 6: "tiff_jpeg", # obsolete
+ 7: "jpeg",
+ 32771: "tiff_raw_16", # 16-bit padding
+ 32773: "packbits"
+}
+
+OPEN_INFO = {
+ # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
+ # ExtraSamples) => mode, rawmode
+ (II, 0, 1, 1, (1,), ()): ("1", "1;I"),
+ (II, 0, 1, 2, (1,), ()): ("1", "1;IR"),
+ (II, 0, 1, 1, (8,), ()): ("L", "L;I"),
+ (II, 0, 1, 2, (8,), ()): ("L", "L;IR"),
+ (II, 1, 1, 1, (1,), ()): ("1", "1"),
+ (II, 1, 1, 2, (1,), ()): ("1", "1;R"),
+ (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, (16,), ()): ("I;16", "I;16"),
+ (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"),
+ (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), (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"),
+ (II, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10
+ (II, 3, 1, 1, (1,), ()): ("P", "P;1"),
+ (II, 3, 1, 2, (1,), ()): ("P", "P;1R"),
+ (II, 3, 1, 1, (2,), ()): ("P", "P;2"),
+ (II, 3, 1, 2, (2,), ()): ("P", "P;2R"),
+ (II, 3, 1, 1, (4,), ()): ("P", "P;4"),
+ (II, 3, 1, 2, (4,), ()): ("P", "P;4R"),
+ (II, 3, 1, 1, (8,), ()): ("P", "P"),
+ (II, 3, 1, 1, (8,8), (2,)): ("PA", "PA"),
+ (II, 3, 1, 2, (8,), ()): ("P", "P;R"),
+ (II, 5, 1, 1, (8,8,8,8), ()): ("CMYK", "CMYK"),
+ (II, 6, 1, 1, (8,8,8), ()): ("YCbCr", "YCbCr"),
+ (II, 8, 1, 1, (8,8,8), ()): ("LAB", "LAB"),
+
+ (MM, 0, 1, 1, (1,), ()): ("1", "1;I"),
+ (MM, 0, 1, 2, (1,), ()): ("1", "1;IR"),
+ (MM, 0, 1, 1, (8,), ()): ("L", "L;I"),
+ (MM, 0, 1, 2, (8,), ()): ("L", "L;IR"),
+ (MM, 1, 1, 1, (1,), ()): ("1", "1"),
+ (MM, 1, 1, 2, (1,), ()): ("1", "1;R"),
+ (MM, 1, 1, 1, (8,), ()): ("L", "L"),
+ (MM, 1, 1, 1, (8,8), (2,)): ("LA", "LA"),
+ (MM, 1, 1, 2, (8,), ()): ("L", "L;R"),
+ (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, 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"),
+ (MM, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"),
+ (MM, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"),
+ (MM, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10
+ (MM, 3, 1, 1, (1,), ()): ("P", "P;1"),
+ (MM, 3, 1, 2, (1,), ()): ("P", "P;1R"),
+ (MM, 3, 1, 1, (2,), ()): ("P", "P;2"),
+ (MM, 3, 1, 2, (2,), ()): ("P", "P;2R"),
+ (MM, 3, 1, 1, (4,), ()): ("P", "P;4"),
+ (MM, 3, 1, 2, (4,), ()): ("P", "P;4R"),
+ (MM, 3, 1, 1, (8,), ()): ("P", "P"),
+ (MM, 3, 1, 1, (8,8), (2,)): ("PA", "PA"),
+ (MM, 3, 1, 2, (8,), ()): ("P", "P;R"),
+ (MM, 5, 1, 1, (8,8,8,8), ()): ("CMYK", "CMYK"),
+ (MM, 6, 1, 1, (8,8,8), ()): ("YCbCr", "YCbCr"),
+ (MM, 8, 1, 1, (8,8,8), ()): ("LAB", "LAB"),
+
+}
+
+PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"]
+
+def _accept(prefix):
+ return prefix[:4] in PREFIXES
+
+##
+# Wrapper for TIFF IFDs.
+
+class ImageFileDirectory:
+
+ # represents a TIFF tag directory. to speed things up,
+ # we don't decode tags unless they're asked for.
+
+ def __init__(self, prefix):
+ self.prefix = prefix[:2]
+ if self.prefix == MM:
+ self.i16, self.i32 = ib16, ib32
+ self.o16, self.o32 = ob16, ob32
+ elif self.prefix == II:
+ self.i16, self.i32 = il16, il32
+ self.o16, self.o32 = ol16, ol32
+ else:
+ raise SyntaxError("not a TIFF IFD")
+ self.reset()
+
+ def reset(self):
+ self.tags = {}
+ self.tagdata = {}
+ self.tagtype = {} # added 2008-06-05 by Florian Hoech
+ self.next = None
+
+ # dictionary API (sort of)
+
+ def keys(self):
+ return self.tagdata.keys() + self.tags.keys()
+
+ def items(self):
+ items = self.tags.items()
+ for tag in self.tagdata.keys():
+ items.append((tag, self[tag]))
+ return items
+
+ def __len__(self):
+ return len(self.tagdata) + len(self.tags)
+
+ def __getitem__(self, tag):
+ try:
+ return self.tags[tag]
+ except KeyError:
+ type, data = self.tagdata[tag] # unpack on the fly
+ 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]
+ if len(value) != 1:
+ if tag == SAMPLEFORMAT:
+ # work around broken (?) matrox library
+ # (from Ted Wright, via Bob Klimek)
+ raise KeyError # use default
+ 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 __setitem__(self, tag, value):
+ if type(value) is not type(()):
+ value = (value,)
+ self.tags[tag] = value
+
+ # load primitives
+
+ load_dispatch = {}
+
+ def load_byte(self, data):
+ l = []
+ for i in range(len(data)):
+ l.append(ord(data[i]))
+ return tuple(l)
+ load_dispatch[1] = (1, load_byte)
+
+ def load_string(self, data):
+ if data[-1:] == '\0':
+ data = data[:-1]
+ return data
+ load_dispatch[2] = (1, load_string)
+
+ def load_short(self, data):
+ l = []
+ for i in range(0, len(data), 2):
+ l.append(self.i16(data, i))
+ return tuple(l)
+ load_dispatch[3] = (2, load_short)
+
+ def load_long(self, data):
+ l = []
+ for i in range(0, len(data), 4):
+ l.append(self.i32(data, i))
+ return tuple(l)
+ load_dispatch[4] = (4, load_long)
+
+ def load_rational(self, data):
+ l = []
+ for i in range(0, len(data), 8):
+ l.append((self.i32(data, i), self.i32(data, i+4)))
+ return tuple(l)
+ load_dispatch[5] = (8, load_rational)
+
+ def load_float(self, data):
+ a = array.array("f", data)
+ if self.prefix != native_prefix:
+ a.byteswap()
+ return tuple(a)
+ load_dispatch[11] = (4, load_float)
+
+ def load_double(self, data):
+ a = array.array("d", data)
+ if self.prefix != native_prefix:
+ a.byteswap()
+ return tuple(a)
+ load_dispatch[12] = (8, load_double)
+
+ def load_undefined(self, data):
+ # Untyped data
+ return data
+ load_dispatch[7] = (1, load_undefined)
+
+ def load(self, fp):
+ # load tag dictionary
+
+ self.reset()
+
+ i16 = self.i16
+ i32 = self.i32
+
+ for i in range(i16(fp.read(2))):
+
+ ifd = fp.read(12)
+
+ tag, typ = i16(ifd), i16(ifd, 2)
+
+ if Image.DEBUG:
+ 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),
+
+ try:
+ dispatch = self.load_dispatch[typ]
+ except KeyError:
+ if Image.DEBUG:
+ print "- unsupported type", typ
+ continue # ignore unsupported type
+
+ size, handler = dispatch
+
+ size = size * i32(ifd, 4)
+
+ # Get and expand tag value
+ if size > 4:
+ here = fp.tell()
+ fp.seek(i32(ifd, 8))
+ data = ImageFile._safe_read(fp, size)
+ fp.seek(here)
+ else:
+ data = ifd[8:8+size]
+
+ if len(data) != size:
+ raise IOError, "not enough data"
+
+ self.tagdata[tag] = typ, data
+ self.tagtype[tag] = typ
+
+ if Image.DEBUG:
+ if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
+ print "- value: " % size
+ else:
+ print "- value:", self[tag]
+
+ self.next = i32(fp.read(4))
+
+ # save primitives
+
+ def save(self, fp):
+
+ o16 = self.o16
+ o32 = self.o32
+
+ fp.write(o16(len(self.tags)))
+
+ # always write in ascending tag order
+ tags = self.tags.items()
+ tags.sort()
+
+ directory = []
+ append = directory.append
+
+ offset = fp.tell() + len(self.tags) * 12 + 4
+
+ stripoffsets = None
+
+ # pass 1: convert tags to binary format
+ for tag, value in tags:
+
+ typ = None
+
+ if self.tagtype.has_key(tag):
+ typ = self.tagtype[tag]
+
+ if typ == 1:
+ # byte data
+ data = value = string.join(map(chr, value), "")
+ elif typ == 7:
+ # untyped data
+ data = value = string.join(value, "")
+ elif type(value[0]) is type(""):
+ # string data
+ typ = 2
+ data = value = string.join(value, "\0") + "\0"
+ else:
+ # integer data
+ if tag == STRIPOFFSETS:
+ stripoffsets = len(directory)
+ typ = 4 # to avoid catch-22
+ elif tag in (X_RESOLUTION, Y_RESOLUTION):
+ # identify rational data fields
+ typ = 5
+ elif not typ:
+ typ = 3
+ for v in value:
+ if v >= 65536:
+ typ = 4
+ if typ == 3:
+ data = string.join(map(o16, value), "")
+ else:
+ data = string.join(map(o32, value), "")
+
+ if Image.DEBUG:
+ 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),
+ if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP):
+ size = len(data)
+ print "- value: " % size
+ else:
+ print "- value:", value
+
+ # figure out if data fits into the directory
+ if len(data) == 4:
+ append((tag, typ, len(value), data, ""))
+ elif len(data) < 4:
+ append((tag, typ, len(value), data + (4-len(data))*"\0", ""))
+ else:
+ count = len(value)
+ if typ == 5:
+ count = count / 2 # adjust for rational data field
+ append((tag, typ, count, o32(offset), data))
+ offset = offset + len(data)
+ if offset & 1:
+ offset = offset + 1 # word padding
+
+ # update strip offset data to point beyond auxiliary data
+ if stripoffsets is not None:
+ tag, typ, count, value, data = directory[stripoffsets]
+ assert not data, "multistrip support not yet implemented"
+ value = o32(self.i32(value) + offset)
+ directory[stripoffsets] = tag, typ, count, value, data
+
+ # 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)
+ fp.write(o16(tag) + o16(typ) + o32(count) + value)
+
+ # -- overwrite here for multi-page --
+ fp.write("\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")
+
+ return offset
+
+##
+# Image plugin for TIFF files.
+
+class TiffImageFile(ImageFile.ImageFile):
+
+ format = "TIFF"
+ format_description = "Adobe TIFF"
+
+ def _open(self):
+ "Open the first image in a TIFF file"
+
+ # Header
+ ifh = self.fp.read(8)
+
+ if ifh[:4] not in PREFIXES:
+ raise SyntaxError, "not a TIFF file"
+
+ # image file directory (tag dictionary)
+ self.tag = self.ifd = ImageFileDirectory(ifh[:2])
+
+ # setup frame pointers
+ self.__first = self.__next = self.ifd.i32(ifh, 4)
+ self.__frame = -1
+ self.__fp = self.fp
+
+ # and load the first frame
+ self._seek(0)
+
+ def seek(self, frame):
+ "Select a given frame as current image"
+
+ if frame < 0:
+ frame = 0
+ self._seek(frame)
+
+ def tell(self):
+ "Return the current frame number"
+
+ return self._tell()
+
+ def _seek(self, frame):
+
+ self.fp = self.__fp
+ if frame < self.__frame:
+ # rewind file
+ self.__frame = -1
+ self.__next = self.__first
+ while self.__frame < frame:
+ if not self.__next:
+ raise EOFError, "no more images in TIFF file"
+ self.fp.seek(self.__next)
+ self.tag.load(self.fp)
+ self.__next = self.tag.next
+ self.__frame = self.__frame + 1
+ self._setup()
+
+ def _tell(self):
+
+ return self.__frame
+
+ def _decoder(self, rawmode, layer):
+ "Setup decoder contexts"
+
+ args = None
+ if rawmode == "RGB" and self._planar_configuration == 2:
+ rawmode = rawmode[layer]
+ compression = self._compression
+ if compression == "raw":
+ args = (rawmode, 0, 1)
+ elif compression == "jpeg":
+ args = rawmode, ""
+ if self.tag.has_key(JPEGTABLES):
+ # 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):
+ # Section 14: Differencing Predictor
+ self.decoderconfig = (self.tag[PREDICTOR][0],)
+
+ if self.tag.has_key(ICCPROFILE):
+ self.info['icc_profile'] = self.tag[ICCPROFILE]
+
+ return args
+
+ 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"
+
+ getscalar = self.tag.getscalar
+
+ # extract relevant tags
+ self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)]
+ self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1)
+
+ # photometric is a required tag, but not everyone is reading
+ # the specification
+ photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0)
+
+ 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
+
+ # size
+ xsize = getscalar(IMAGEWIDTH)
+ ysize = getscalar(IMAGELENGTH)
+ self.size = xsize, ysize
+
+ if Image.DEBUG:
+ print "- size:", self.size
+
+ format = getscalar(SAMPLEFORMAT, 1)
+
+ # mode: check photometric interpretation and bits per pixel
+ key = (
+ self.tag.prefix, photo, format, fillorder,
+ self.tag.get(BITSPERSAMPLE, (1,)),
+ self.tag.get(EXTRASAMPLES, ())
+ )
+ if Image.DEBUG:
+ print "format key:", key
+ try:
+ self.mode, rawmode = OPEN_INFO[key]
+ except KeyError:
+ if Image.DEBUG:
+ print "- unsupported format"
+ raise SyntaxError, "unknown pixel mode"
+
+ if Image.DEBUG:
+ 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 yres:
+ xres = xres[0] / (xres[1] or 1)
+ yres = yres[0] / (yres[1] or 1)
+ resunit = getscalar(RESOLUTION_UNIT, 1)
+ if resunit == 2: # dots per inch
+ self.info["dpi"] = xres, yres
+ elif resunit == 3: # dots per centimeter. convert to dpi
+ self.info["dpi"] = xres * 2.54, yres * 2.54
+ else: # No absolute unit of measurement
+ self.info["resolution"] = xres, yres
+
+ # build tile descriptors
+ x = y = l = 0
+ self.tile = []
+ if self.tag.has_key(STRIPOFFSETS):
+ # striped image
+ h = getscalar(ROWSPERSTRIP, ysize)
+ w = self.size[0]
+ a = None
+ for o in self.tag[STRIPOFFSETS]:
+ if not a:
+ a = self._decoder(rawmode, l)
+ 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
+ a = None
+ elif self.tag.has_key(TILEOFFSETS):
+ # tiled image
+ w = getscalar(322)
+ h = getscalar(323)
+ a = None
+ for o in self.tag[TILEOFFSETS]:
+ if not a:
+ a = self._decoder(rawmode, l)
+ # FIXME: this doesn't work if the image size
+ # is not a multiple of the tile size...
+ self.tile.append(
+ (self._compression,
+ (x, y, x+w, y+h),
+ o, a))
+ x = x + w
+ if x >= self.size[0]:
+ x, y = 0, y + h
+ if y >= self.size[1]:
+ x = y = 0
+ l = l + 1
+ a = None
+ else:
+ if Image.DEBUG:
+ 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, ""))
+#
+# --------------------------------------------------------------------
+# Write TIFF files
+
+# little endian is default except for image modes with explict big endian byte-order
+
+SAVE_INFO = {
+ # mode => rawmode, byteorder, photometrics, sampleformat, bitspersample, extra
+ "1": ("1", II, 1, 1, (1,), None),
+ "L": ("L", II, 1, 1, (8,), None),
+ "LA": ("LA", II, 1, 1, (8,8), 2),
+ "P": ("P", II, 3, 1, (8,), None),
+ "PA": ("PA", II, 3, 1, (8,8), 2),
+ "I": ("I;32S", II, 1, 2, (32,), None),
+ "I;16": ("I;16", II, 1, 1, (16,), None),
+ "I;16S": ("I;16S", II, 1, 2, (16,), None),
+ "F": ("F;32F", II, 1, 3, (32,), None),
+ "RGB": ("RGB", II, 2, 1, (8,8,8), None),
+ "RGBX": ("RGBX", II, 2, 1, (8,8,8,8), 0),
+ "RGBA": ("RGBA", II, 2, 1, (8,8,8,8), 2),
+ "CMYK": ("CMYK", II, 5, 1, (8,8,8,8), None),
+ "YCbCr": ("YCbCr", II, 6, 1, (8,8,8), None),
+ "LAB": ("LAB", II, 8, 1, (8,8,8), None),
+
+ "I;32BS": ("I;32BS", MM, 1, 2, (32,), None),
+ "I;16B": ("I;16B", MM, 1, 1, (16,), None),
+ "I;16BS": ("I;16BS", MM, 1, 2, (16,), None),
+ "F;32BF": ("F;32BF", MM, 1, 3, (32,), None),
+}
+
+def _cvt_res(value):
+ # convert value to TIFF rational number -- (numerator, denominator)
+ if type(value) in (type([]), type(())):
+ assert(len(value) % 2 == 0)
+ return value
+ if type(value) == type(1):
+ return (value, 1)
+ value = float(value)
+ return (int(value * 65536), 65536)
+
+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
+
+ ifd = ImageFileDirectory(prefix)
+
+ # -- multi-page -- skip TIFF header on subsequent pages
+ if 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))
+
+ ifd[IMAGEWIDTH] = im.size[0]
+ ifd[IMAGELENGTH] = im.size[1]
+
+ # 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):
+ ifd[key] = im.tag[key]
+ # 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"):
+ ifd[ICCPROFILE] = im.info["icc_profile"]
+ if im.encoderinfo.has_key("description"):
+ ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"]
+ if im.encoderinfo.has_key("resolution"):
+ ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \
+ = _cvt_res(im.encoderinfo["resolution"])
+ if im.encoderinfo.has_key("x resolution"):
+ ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"])
+ if im.encoderinfo.has_key("y resolution"):
+ ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"])
+ if im.encoderinfo.has_key("resolution unit"):
+ unit = im.encoderinfo["resolution unit"]
+ if unit == "inch":
+ ifd[RESOLUTION_UNIT] = 2
+ elif unit == "cm" or unit == "centimeter":
+ ifd[RESOLUTION_UNIT] = 3
+ else:
+ ifd[RESOLUTION_UNIT] = 1
+ if im.encoderinfo.has_key("software"):
+ ifd[SOFTWARE] = im.encoderinfo["software"]
+ if im.encoderinfo.has_key("date time"):
+ ifd[DATE_TIME] = im.encoderinfo["date time"]
+ if im.encoderinfo.has_key("artist"):
+ ifd[ARTIST] = im.encoderinfo["artist"]
+ if im.encoderinfo.has_key("copyright"):
+ ifd[COPYRIGHT] = im.encoderinfo["copyright"]
+
+ dpi = im.encoderinfo.get("dpi")
+ if dpi:
+ ifd[RESOLUTION_UNIT] = 2
+ ifd[X_RESOLUTION] = _cvt_res(dpi[0])
+ ifd[Y_RESOLUTION] = _cvt_res(dpi[1])
+
+ if bits != (1,):
+ ifd[BITSPERSAMPLE] = bits
+ if len(bits) != 1:
+ ifd[SAMPLESPERPIXEL] = len(bits)
+ if extra is not None:
+ ifd[EXTRASAMPLES] = extra
+ if format != 1:
+ ifd[SAMPLEFORMAT] = format
+
+ ifd[PHOTOMETRIC_INTERPRETATION] = photo
+
+ if im.mode == "P":
+ lut = im.im.getpalette("RGB", "RGB;L")
+ ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut))
+
+ # data orientation
+ 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
+
+ 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"):
+ #just to access o32 and o16 (using correct byte order)
+ im._debug_multipage = ifd
+
+#
+# --------------------------------------------------------------------
+# Register
+
+Image.register_open("TIFF", TiffImageFile, _accept)
+Image.register_save("TIFF", _save)
+
+Image.register_extension("TIFF", ".tif")
+Image.register_extension("TIFF", ".tiff")
+
+Image.register_mime("TIFF", "image/tiff")
diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py
new file mode 100644
index 000000000..33fd20941
--- /dev/null
+++ b/PIL/TiffTags.py
@@ -0,0 +1,209 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# TIFF tags
+#
+# This module provides clear-text names for various well-known
+# TIFF tags. the TIFF codec works just fine without it.
+#
+# Copyright (c) Secret Labs AB 1999.
+#
+# See the README file for information on usage and redistribution.
+#
+
+##
+# This module provides constants and clear-text names for various
+# well-known TIFF tags.
+##
+
+##
+# Map tag numbers (or tag number, tag value tuples) to tag names.
+
+TAGS = {
+
+ 254: "NewSubfileType",
+ 255: "SubfileType",
+ 256: "ImageWidth",
+ 257: "ImageLength",
+ 258: "BitsPerSample",
+
+ 259: "Compression",
+ (259, 1): "Uncompressed",
+ (259, 2): "CCITT 1d",
+ (259, 3): "Group 3 Fax",
+ (259, 4): "Group 4 Fax",
+ (259, 5): "LZW",
+ (259, 6): "JPEG",
+ (259, 32773): "PackBits",
+
+ 262: "PhotometricInterpretation",
+ (262, 0): "WhiteIsZero",
+ (262, 1): "BlackIsZero",
+ (262, 2): "RGB",
+ (262, 3): "RGB Palette",
+ (262, 4): "Transparency Mask",
+ (262, 5): "CMYK",
+ (262, 6): "YCbCr",
+ (262, 8): "CieLAB",
+ (262, 32803): "CFA", # TIFF/EP, Adobe DNG
+ (262, 32892): "LinearRaw", # Adobe DNG
+
+ 263: "Thresholding",
+ 264: "CellWidth",
+ 265: "CellHeight",
+ 266: "FillOrder",
+ 269: "DocumentName",
+
+ 270: "ImageDescription",
+ 271: "Make",
+ 272: "Model",
+ 273: "StripOffsets",
+ 274: "Orientation",
+ 277: "SamplesPerPixel",
+ 278: "RowsPerStrip",
+ 279: "StripByteCounts",
+
+ 280: "MinSampleValue",
+ 281: "MaxSampleValue",
+ 282: "XResolution",
+ 283: "YResolution",
+ 284: "PlanarConfiguration",
+ (284, 1): "Contigous",
+ (284, 2): "Separate",
+
+ 285: "PageName",
+ 286: "XPosition",
+ 287: "YPosition",
+ 288: "FreeOffsets",
+ 289: "FreeByteCounts",
+
+ 290: "GrayResponseUnit",
+ 291: "GrayResponseCurve",
+ 292: "T4Options",
+ 293: "T6Options",
+ 296: "ResolutionUnit",
+ 297: "PageNumber",
+
+ 301: "TransferFunction",
+ 305: "Software",
+ 306: "DateTime",
+
+ 315: "Artist",
+ 316: "HostComputer",
+ 317: "Predictor",
+ 318: "WhitePoint",
+ 319: "PrimaryChromaticies",
+
+ 320: "ColorMap",
+ 321: "HalftoneHints",
+ 322: "TileWidth",
+ 323: "TileLength",
+ 324: "TileOffsets",
+ 325: "TileByteCounts",
+
+ 332: "InkSet",
+ 333: "InkNames",
+ 334: "NumberOfInks",
+ 336: "DotRange",
+ 337: "TargetPrinter",
+ 338: "ExtraSamples",
+ 339: "SampleFormat",
+
+ 340: "SMinSampleValue",
+ 341: "SMaxSampleValue",
+ 342: "TransferRange",
+
+ 347: "JPEGTables",
+
+ # obsolete JPEG tags
+ 512: "JPEGProc",
+ 513: "JPEGInterchangeFormat",
+ 514: "JPEGInterchangeFormatLength",
+ 515: "JPEGRestartInterval",
+ 517: "JPEGLosslessPredictors",
+ 518: "JPEGPointTransforms",
+ 519: "JPEGQTables",
+ 520: "JPEGDCTables",
+ 521: "JPEGACTables",
+
+ 529: "YCbCrCoefficients",
+ 530: "YCbCrSubSampling",
+ 531: "YCbCrPositioning",
+ 532: "ReferenceBlackWhite",
+
+ # XMP
+ 700: "XMP",
+
+ 33432: "Copyright",
+
+ # various extensions (should check specs for "official" names)
+ 33723: "IptcNaaInfo",
+ 34377: "PhotoshopInfo",
+
+ # Exif IFD
+ 34665: "ExifIFD",
+
+ # ICC Profile
+ 34675: "ICCProfile",
+
+ # Adobe DNG
+ 50706: "DNGVersion",
+ 50707: "DNGBackwardVersion",
+ 50708: "UniqueCameraModel",
+ 50709: "LocalizedCameraModel",
+ 50710: "CFAPlaneColor",
+ 50711: "CFALayout",
+ 50712: "LinearizationTable",
+ 50713: "BlackLevelRepeatDim",
+ 50714: "BlackLevel",
+ 50715: "BlackLevelDeltaH",
+ 50716: "BlackLevelDeltaV",
+ 50717: "WhiteLevel",
+ 50718: "DefaultScale",
+ 50741: "BestQualityScale",
+ 50719: "DefaultCropOrigin",
+ 50720: "DefaultCropSize",
+ 50778: "CalibrationIlluminant1",
+ 50779: "CalibrationIlluminant2",
+ 50721: "ColorMatrix1",
+ 50722: "ColorMatrix2",
+ 50723: "CameraCalibration1",
+ 50724: "CameraCalibration2",
+ 50725: "ReductionMatrix1",
+ 50726: "ReductionMatrix2",
+ 50727: "AnalogBalance",
+ 50728: "AsShotNeutral",
+ 50729: "AsShotWhiteXY",
+ 50730: "BaselineExposure",
+ 50731: "BaselineNoise",
+ 50732: "BaselineSharpness",
+ 50733: "BayerGreenSplit",
+ 50734: "LinearResponseLimit",
+ 50735: "CameraSerialNumber",
+ 50736: "LensInfo",
+ 50737: "ChromaBlurRadius",
+ 50738: "AntiAliasStrength",
+ 50740: "DNGPrivateData",
+ 50741: "MakerNoteSafety",
+}
+
+##
+# Map type numbers to type names.
+
+TYPES = {
+
+ 1: "byte",
+ 2: "ascii",
+ 3: "short",
+ 4: "long",
+ 5: "rational",
+ 6: "signed byte",
+ 7: "undefined",
+ 8: "signed short",
+ 9: "signed long",
+ 10: "signed rational",
+ 11: "float",
+ 12: "double",
+
+}
diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py
new file mode 100644
index 000000000..fb8c38ab4
--- /dev/null
+++ b/PIL/WalImageFile.py
@@ -0,0 +1,126 @@
+# -*- coding: iso-8859-1 -*-
+#
+# The Python Imaging Library.
+# $Id$
+#
+# WAL file handling
+#
+# History:
+# 2003-04-23 fl created
+#
+# Copyright (c) 2003 by Fredrik Lundh.
+#
+# See the README file for information on usage and redistribution.
+#
+
+# NOTE: This format cannot be automatically recognized, so the reader
+# is not registered for use with Image.open(). To open a WEL file, use
+# the WalImageFile.open() function instead.
+
+# This reader is based on the specification available from:
+# http://www.flipcode.com/tutorials/tut_q2levels.shtml
+# and has been tested with a few sample files found using google.
+
+import Image
+
+def i32(c, o=0):
+ return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)
+
+##
+# Load texture from a Quake2 WAL texture file.
+#
+# By default, a Quake2 standard palette is attached to the texture.
+# To override the palette, use the putpalette method.
+#
+# @param filename WAL file name, or an opened file handle.
+# @return An image instance.
+
+def open(filename):
+ # FIXME: modify to return a WalImageFile instance instead of
+ # plain Image object ?
+
+ if hasattr(filename, "read"):
+ fp = filename
+ else:
+ import __builtin__
+ fp = __builtin__.open(filename, "rb")
+
+ # read header fields
+ header = fp.read(32+24+32+12)
+ size = i32(header, 32), i32(header, 36)
+ offset = i32(header, 40)
+
+ # load pixel data
+ fp.seek(offset)
+
+ im = Image.fromstring("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]
+ if next_name:
+ im.info["next_name"] = next_name
+
+ return im
+
+
+quake2palette = (
+ # default palette taken from piffo 0.93 by Hans Häggström
+ "\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"
+)
+
+if __name__ == "__main__":
+ im = open("../hacks/sample.wal")
+ print im.info, im.mode, im.size
+ im.save("../out.png")
diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py
new file mode 100644
index 000000000..5191f1e51
--- /dev/null
+++ b/PIL/WmfImagePlugin.py
@@ -0,0 +1,167 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# WMF stub codec
+#
+# history:
+# 1996-12-14 fl Created
+# 2004-02-22 fl Turned into a stub driver
+# 2004-02-23 fl Added EMF support
+#
+# Copyright (c) Secret Labs AB 1997-2004. All rights reserved.
+# Copyright (c) Fredrik Lundh 1996.
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.2"
+
+import Image, ImageFile
+
+_handler = None
+
+##
+# Install application-specific WMF image handler.
+#
+# @param handler Handler object.
+
+def register_handler(handler):
+ global _handler
+ _handler = handler
+
+if hasattr(Image.core, "drawwmf"):
+ # install default handler (windows only)
+
+ class WmfHandler:
+
+ def open(self, im):
+ im.mode = "RGB"
+ self.bbox = im.info["wmf_bbox"]
+
+ def load(self, im):
+ im.fp.seek(0) # rewind
+ return Image.fromstring(
+ "RGB", im.size,
+ Image.core.drawwmf(im.fp.read(), im.size, self.bbox),
+ "raw", "BGR", (im.size[0]*3 + 3) & -4, -1
+ )
+
+ register_handler(WmfHandler())
+
+# --------------------------------------------------------------------
+
+def word(c, o=0):
+ return ord(c[o]) + (ord(c[o+1])<<8)
+
+def short(c, o=0):
+ v = ord(c[o]) + (ord(c[o+1])<<8)
+ 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)
+
+#
+# --------------------------------------------------------------------
+# Read WMF file
+
+def _accept(prefix):
+ return (
+ prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or
+ prefix[:4] == "\x01\x00\x00\x00"
+ )
+
+##
+# Image plugin for Windows metafiles.
+
+class WmfStubImageFile(ImageFile.StubImageFile):
+
+ format = "WMF"
+ format_description = "Windows Metafile"
+
+ def _open(self):
+
+ # check placable header
+ s = self.fp.read(80)
+
+ if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00":
+
+ # placeable windows metafile
+
+ # get units per inch
+ inch = word(s, 14)
+
+ # get bounding box
+ x0 = short(s, 6); y0 = short(s, 8)
+ x1 = short(s, 10); y1 = short(s, 12)
+
+ # normalize size to 72 dots per inch
+ size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch
+
+ self.info["wmf_bbox"] = x0, y0, x1, y1
+
+ self.info["dpi"] = 72
+
+ # print self.mode, self.size, self.info
+
+ # sanity check (standard metafile header)
+ if s[22:26] != "\x01\x00\t\x00":
+ raise SyntaxError("Unsupported WMF file format")
+
+ elif long(s) == 1 and s[40:44] == " EMF":
+ # enhanced metafile
+
+ # get bounding box
+ x0 = long(s, 8); y0 = long(s, 12)
+ x1 = long(s, 16); y1 = long(s, 20)
+
+ # get frame (in 0.01 millimeter units)
+ frame = long(s, 24), long(s, 28), long(s, 32), long(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])
+
+ self.info["wmf_bbox"] = x0, y0, x1, y1
+
+ if xdpi == ydpi:
+ self.info["dpi"] = xdpi
+ else:
+ self.info["dpi"] = xdpi, ydpi
+
+ else:
+ raise SyntaxError("Unsupported file format")
+
+ self.mode = "RGB"
+ self.size = size
+
+ loader = self._load()
+ if loader:
+ loader.open(self)
+
+ def _load(self):
+ return _handler
+
+
+def _save(im, fp, filename):
+ if _handler is None or not hasattr("_handler", "save"):
+ raise IOError("WMF save handler not installed")
+ _handler.save(im, fp, filename)
+
+#
+# --------------------------------------------------------------------
+# Registry stuff
+
+Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept)
+Image.register_save(WmfStubImageFile.format, _save)
+
+Image.register_extension(WmfStubImageFile.format, ".wmf")
+Image.register_extension(WmfStubImageFile.format, ".emf")
diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py
new file mode 100644
index 000000000..ab1fb5d7a
--- /dev/null
+++ b/PIL/XVThumbImagePlugin.py
@@ -0,0 +1,73 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# XV Thumbnail file handler by Charles E. "Gene" Cash
+# (gcash@magicnet.net)
+#
+# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV,
+# available from ftp://ftp.cis.upenn.edu/pub/xv/
+#
+# history:
+# 98-08-15 cec created (b/w only)
+# 98-12-09 cec added color palette
+# 98-12-28 fl added to PIL (with only a few very minor modifications)
+#
+# To do:
+# FIXME: make save work (this requires quantization support)
+#
+
+__version__ = "0.1"
+
+import string
+import Image, ImageFile, ImagePalette
+
+# standard color palette for thumbnails (RGB332)
+PALETTE = ""
+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))
+
+##
+# Image plugin for XV thumbnail images.
+
+class XVThumbImageFile(ImageFile.ImageFile):
+
+ format = "XVThumb"
+ format_description = "XV thumbnail image"
+
+ def _open(self):
+
+ # check magic
+ s = self.fp.read(6)
+ if s != "P7 332":
+ raise SyntaxError, "not an XV thumbnail file"
+
+ # Skip to beginning of next line
+ self.fp.readline()
+
+ # skip info comments
+ while 1:
+ s = self.fp.readline()
+ if not s:
+ raise SyntaxError, "Unexpected EOF reading XV thumbnail file"
+ if s[0] != '#':
+ break
+
+ # parse header line (already read)
+ s = string.split(s.strip())
+
+ self.mode = "P"
+ self.size = int(s[0]), int(s[1])
+
+ self.palette = ImagePalette.raw("RGB", PALETTE)
+
+ self.tile = [
+ ("raw", (0, 0)+self.size,
+ self.fp.tell(), (self.mode, 0, 1)
+ )]
+
+# --------------------------------------------------------------------
+
+Image.register_open("XVThumb", XVThumbImageFile)
diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py
new file mode 100644
index 000000000..a8cf1026d
--- /dev/null
+++ b/PIL/XbmImagePlugin.py
@@ -0,0 +1,94 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# XBM File handling
+#
+# History:
+# 1995-09-08 fl Created
+# 1996-11-01 fl Added save support
+# 1997-07-07 fl Made header parser more tolerant
+# 1997-07-22 fl Fixed yet another parser bug
+# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
+# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog)
+# 2004-02-24 fl Allow some whitespace before first #define
+#
+# Copyright (c) 1997-2004 by Secret Labs AB
+# Copyright (c) 1996-1997 by Fredrik Lundh
+#
+# See the README file for information on usage and redistribution.
+#
+
+__version__ = "0.6"
+
+import re, string
+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\\[\\]"
+)
+
+def _accept(prefix):
+ return string.lstrip(prefix)[:7] == "#define"
+
+##
+# Image plugin for X11 bitmaps.
+
+class XbmImageFile(ImageFile.ImageFile):
+
+ format = "XBM"
+ format_description = "X11 Bitmap"
+
+ def _open(self):
+
+ m = xbm_head.match(self.fp.read(512))
+
+ if m:
+
+ xsize = int(m.group("width"))
+ ysize = int(m.group("height"))
+
+ if m.group("hotspot"):
+ self.info["hotspot"] = (
+ int(m.group("xhot")), int(m.group("yhot"))
+ )
+
+ self.mode = "1"
+ self.size = xsize, ysize
+
+ self.tile = [("xbm", (0, 0)+self.size, m.end(), None)]
+
+
+def _save(im, fp, filename):
+
+ if im.mode != "1":
+ 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])
+
+ 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("static char im_bits[] = {\n")
+
+ ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)])
+
+ fp.write("};\n")
+
+
+Image.register_open("XBM", XbmImageFile, _accept)
+Image.register_save("XBM", _save)
+
+Image.register_extension("XBM", ".xbm")
+
+Image.register_mime("XBM", "image/xbm")
diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py
new file mode 100644
index 000000000..a3f40f02c
--- /dev/null
+++ b/PIL/XpmImagePlugin.py
@@ -0,0 +1,129 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# XPM File handling
+#
+# History:
+# 1996-12-29 fl Created
+# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7)
+#
+# Copyright (c) Secret Labs AB 1997-2001.
+# Copyright (c) Fredrik Lundh 1996-2001.
+#
+# See the README file for information on usage and redistribution.
+#
+
+
+__version__ = "0.2"
+
+
+import re, string
+import Image, ImageFile, ImagePalette
+
+# XPM header
+xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)")
+
+
+def _accept(prefix):
+ return prefix[:9] == "/* XPM */"
+
+##
+# Image plugin for X11 pixel maps.
+
+class XpmImageFile(ImageFile.ImageFile):
+
+ format = "XPM"
+ format_description = "X11 Pixel Map"
+
+ def _open(self):
+
+ if not _accept(self.fp.read(9)):
+ raise SyntaxError, "not an XPM file"
+
+ # skip forward to next string
+ while 1:
+ s = self.fp.readline()
+ if not s:
+ raise SyntaxError, "broken XPM file"
+ m = xpm_head.match(s)
+ if m:
+ break
+
+ self.size = int(m.group(1)), int(m.group(2))
+
+ pal = int(m.group(3))
+ bpp = int(m.group(4))
+
+ if pal > 256 or bpp != 1:
+ raise ValueError, "cannot read this XPM file"
+
+ #
+ # load palette description
+
+ palette = ["\0\0\0"] * 256
+
+ for i in range(pal):
+
+ s = self.fp.readline()
+ if s[-2:] == '\r\n':
+ s = s[:-2]
+ elif s[-1:] in '\r\n':
+ s = s[:-1]
+
+ c = ord(s[1])
+ s = string.split(s[2:-2])
+
+ for i in range(0, len(s), 2):
+
+ if s[i] == "c":
+
+ # process colour key
+ rgb = s[i+1]
+ if rgb == "None":
+ self.info["transparency"] = c
+ elif rgb[0] == "#":
+ # 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)
+ else:
+ # unknown colour
+ raise ValueError, "cannot read this XPM file"
+ break
+
+ else:
+
+ # missing colour key
+ raise ValueError, "cannot read this XPM file"
+
+ self.mode = "P"
+ self.palette = ImagePalette.raw("RGB", string.join(palette, ""))
+
+ self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))]
+
+ def load_read(self, bytes):
+
+ #
+ # load all image data in one chunk
+
+ xsize, ysize = self.size
+
+ s = [None] * ysize
+
+ for i in range(ysize):
+ s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize)
+
+ self.fp = None
+
+ return string.join(s, "")
+
+#
+# Registry
+
+Image.register_open("XPM", XpmImageFile, _accept)
+
+Image.register_extension("XPM", ".xpm")
+
+Image.register_mime("XPM", "image/xpm")
diff --git a/PIL/__init__.py b/PIL/__init__.py
new file mode 100644
index 000000000..ed54d26f6
--- /dev/null
+++ b/PIL/__init__.py
@@ -0,0 +1,12 @@
+#
+# The Python Imaging Library.
+# $Id$
+#
+# package placeholder
+#
+# Copyright (c) 1999 by Secret Labs AB.
+#
+# See the README file for information on usage and redistribution.
+#
+
+# ;-)
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 000000000..c27993fb2
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,18 @@
+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
index e69de29bb..458975b3b 100644
--- a/README
+++ b/README
@@ -0,0 +1,300 @@
+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.txt b/README.txt
new file mode 100644
index 000000000..85882da91
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,4 @@
+Pillow
+======
+
+Pillow is a fork of the Python Imaging Library
diff --git a/Sane/CHANGES b/Sane/CHANGES
new file mode 100644
index 000000000..95c14697e
--- /dev/null
+++ b/Sane/CHANGES
@@ -0,0 +1,34 @@
+
+from V1.0 to V2.0
+
+_sane.c:
+ - Values for option constraints are correctly translated to floats
+ if value type is TYPE_FIXED for SANE_CONSTRAINT_RANGE and
+ SANE_CONSTRAINT_WORD_LIST
+ - added constants INFO_INEXACT, INFO_RELOAD_OPTIONS,
+ INFO_RELOAD_PARAMS (possible return values of set_option())
+ to module dictionnary.
+ - removed additional return variable 'i' from SaneDev_get_option(),
+ 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
+ the way scanDev.mode is set.
+ This should be the only incompatibility vs. version 1.0.
+
+sane.py
+ - ScanDev got new method __load_option_dict() called from __init__()
+ and from __setattr__() if backend reported that the frontend should
+ reload the options.
+ - Nice human-readable __repr__() method added for class Option
+ - if __setattr__ (i.e. set_option) reports that all other options
+ have to be reloaded due to a change in the backend then they are reloaded.
+ - due to the change in SaneDev_get_option() only the 'value' is
+ returned from get_option().
+ - in __setattr__ integer values are automatically converted to floats
+ if SANE backend expects SANE_FIXED (i.e. fix-point float)
+ - The scanner options can now directly be accessed via scanDev[optionName]
+ instead scanDev.opt[optionName]. (The old way still works).
+
+V1.0:
+ A.M. Kuchling's original pysane package.
\ No newline at end of file
diff --git a/Sane/README b/Sane/README
new file mode 100644
index 000000000..ddff0cf24
--- /dev/null
+++ b/Sane/README
@@ -0,0 +1,22 @@
+
+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
+(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):
+
+ python setup.py build
+
+In order to install the module type:
+
+ python setup.py install
+
+
+For some basic documentation please look at the file sanedoc.txt
+The two demo_*.py scripts give basic examples on how to use the software.
diff --git a/Sane/_sane.c b/Sane/_sane.c
new file mode 100644
index 000000000..21e542fa5
--- /dev/null
+++ b/Sane/_sane.c
@@ -0,0 +1,1325 @@
+/***********************************************************
+(C) Copyright 2003 A.M. Kuchling. All Rights Reserved
+(C) Copyright 2004 A.M. Kuchling, Ralph Heinkel All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+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
+distribution of the software without specific, written prior permission.
+
+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
+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.
+
+******************************************************************/
+
+/* SaneDev objects */
+
+#include "Python.h"
+#include "Imaging.h"
+#include
+
+#include
+
+static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ SANE_Handle h;
+} SaneDevObject;
+
+#ifdef WITH_THREAD
+PyThreadState *_save;
+#endif
+
+/* Raise a SANE exception */
+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;
+
+#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type)
+
+static SaneDevObject *
+newSaneDevObject(void)
+{
+ SaneDevObject *self;
+ self = PyObject_NEW(SaneDevObject, &SaneDev_Type);
+ if (self == NULL)
+ return NULL;
+ self->h=NULL;
+ return self;
+}
+
+/* SaneDev methods */
+
+static void
+SaneDev_dealloc(SaneDevObject *self)
+{
+ if (self->h) sane_close(self->h);
+ self->h=NULL;
+ PyObject_DEL(self);
+}
+
+static PyObject *
+SaneDev_close(SaneDevObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ if (self->h) sane_close(self->h);
+ self->h=NULL;
+ Py_INCREF(Py_None);
+ return (Py_None);
+}
+
+static PyObject *
+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)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ st=sane_get_parameters(self->h, &p);
+ Py_END_ALLOW_THREADS
+
+ if (st) return PySane_Error(st);
+ switch (p.format)
+ {
+ case(SANE_FRAME_GRAY): format="gray"; break;
+ case(SANE_FRAME_RGB): format="color"; break;
+ case(SANE_FRAME_RED): format="red"; break;
+ 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,
+ p.lines, p.depth, p.bytes_per_line);
+}
+
+
+static PyObject *
+SaneDev_fileno(SaneDevObject *self, PyObject *args)
+{
+ SANE_Status st;
+ SANE_Int fd;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ st=sane_get_select_fd(self->h, &fd);
+ if (st) return PySane_Error(st);
+ return PyInt_FromLong(fd);
+}
+
+static PyObject *
+SaneDev_start(SaneDevObject *self, PyObject *args)
+{
+ SANE_Status st;
+
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ /* sane_start can take several seconds, if the user initiates
+ a new scan, while the scan head of a flatbed scanner moves
+ back to the start position after finishing a previous scan.
+ Hence it is worth to allow threads here.
+ */
+ Py_BEGIN_ALLOW_THREADS
+ st=sane_start(self->h);
+ Py_END_ALLOW_THREADS
+ if (st) return PySane_Error(st);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+SaneDev_cancel(SaneDevObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ sane_cancel(self->h);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+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)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ if (!(list = PyList_New(0)))
+ return NULL;
+
+ do
+ {
+ d=sane_get_option_descriptor(self->h, i);
+ if (d!=NULL)
+ {
+ PyObject *constraint=NULL;
+ int j;
+
+ switch (d->constraint_type)
+ {
+ case(SANE_CONSTRAINT_NONE):
+ Py_INCREF(Py_None); constraint=Py_None; break;
+ case(SANE_CONSTRAINT_RANGE):
+ if (d->type == SANE_TYPE_INT)
+ 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),
+ SANE_UNFIX(d->constraint.range->quant));
+ break;
+ 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,
+ PyInt_FromLong(d->constraint.word_list[j]));
+ else
+ for (j=1; j<=d->constraint.word_list[0]; j++)
+ PyList_SetItem(constraint, j-1,
+ PyFloat_FromDouble(SANE_UNFIX(d->constraint.word_list[j])));
+ break;
+ case(SANE_CONSTRAINT_STRING_LIST):
+ constraint=PyList_New(0);
+ for(j=0; d->constraint.string_list[j]!=NULL; j++)
+ PyList_Append(constraint,
+ PyString_FromString(d->constraint.string_list[j]));
+ break;
+ }
+ value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc,
+ d->type, d->unit, d->size, d->cap, constraint);
+ PyList_Append(list, value);
+ }
+ i++;
+ } while (d!=NULL);
+ return list;
+}
+
+static PyObject *
+SaneDev_get_option(SaneDevObject *self, PyObject *args)
+{
+ SANE_Status st;
+ const SANE_Option_Descriptor *d;
+ PyObject *value=NULL;
+ int n;
+ void *v;
+
+ if (!PyArg_ParseTuple(args, "i", &n))
+ {
+ return NULL;
+ }
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ d=sane_get_option_descriptor(self->h, n);
+ v=malloc(d->size+1);
+ st=sane_control_option(self->h, n, SANE_ACTION_GET_VALUE,
+ v, NULL);
+
+ if (st)
+ {
+ free(v);
+ return PySane_Error(st);
+ }
+
+ switch(d->type)
+ {
+ case(SANE_TYPE_BOOL):
+ case(SANE_TYPE_INT):
+ value=Py_BuildValue("i", *( (SANE_Int*)v) );
+ break;
+ case(SANE_TYPE_FIXED):
+ value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) );
+ break;
+ case(SANE_TYPE_STRING):
+ value=Py_BuildValue("s", v);
+ break;
+ case(SANE_TYPE_BUTTON):
+ case(SANE_TYPE_GROUP):
+ value=Py_BuildValue("O", Py_None);
+ break;
+ }
+
+ free(v);
+ return value;
+}
+
+static PyObject *
+SaneDev_set_option(SaneDevObject *self, PyObject *args)
+{
+ SANE_Status st;
+ const SANE_Option_Descriptor *d;
+ SANE_Int i;
+ PyObject *value;
+ int n;
+ void *v;
+
+ if (!PyArg_ParseTuple(args, "iO", &n, &value))
+ return NULL;
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ d=sane_get_option_descriptor(self->h, n);
+ v=malloc(d->size+1);
+
+ switch(d->type)
+ {
+ case(SANE_TYPE_BOOL):
+ if (!PyInt_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "SANE_BOOL requires an integer");
+ free(v);
+ return NULL;
+ }
+ /* fall through */
+ case(SANE_TYPE_INT):
+ if (!PyInt_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "SANE_INT requires an integer");
+ free(v);
+ return NULL;
+ }
+ *( (SANE_Int*)v) = PyInt_AsLong(value);
+ break;
+ case(SANE_TYPE_FIXED):
+ if (!PyFloat_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "SANE_FIXED requires a floating point number");
+ free(v);
+ return NULL;
+ }
+ *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value));
+ break;
+ case(SANE_TYPE_STRING):
+ if (!PyString_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string");
+ free(v);
+ return NULL;
+ }
+ strncpy(v, PyString_AsString(value), d->size-1);
+ ((char*)v)[d->size-1] = 0;
+ break;
+ 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);
+}
+
+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)
+ {
+ 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);
+ }
+
+#define READSIZE 32768
+
+static PyObject *
+SaneDev_snap(SaneDevObject *self, PyObject *args)
+{
+ 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? */
+ SANE_Int len, lastlen;
+ Imaging im;
+ SANE_Parameters p;
+ int px, py, remain, cplen, bufpos, padbytes;
+ long L;
+ char errmsg[80];
+ 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)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+ im=(Imaging)L;
+
+ if (pyNoCancel)
+ noCancel = PyObject_IsTrue(pyNoCancel);
+
+ st=SANE_STATUS_GOOD; px=py=0;
+ /* xxx not yet implemented
+ - handscanner support (i.e., unknown image length during start)
+ - generally: move the functionality from method snap in sane.py
+ down here -- I don't like this cross-dependency.
+ 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:
+ remain = p.bytes_per_line * im->ysize;
+ padbytes = p.bytes_per_line - (im->xsize+7)/8;
+ bufpos = 0;
+ lastlen = len = 0;
+ while (st!=SANE_STATUS_EOF && py < im->ysize)
+ {
+ while (len > 0 && py < im->ysize)
+ {
+ int i, j, k;
+ j = buffer[bufpos++];
+ k = 0x80;
+ for (i = 0; i < 8 && px < im->xsize; i++)
+ {
+ im->image8[py][px++] = (k&j) ? 0 : 0xFF;
+ k = k >> 1;
+ }
+ len--;
+ if (px >= im->xsize)
+ {
+ bufpos += padbytes;
+ len -= padbytes;
+ py++;
+ px = 0;
+ }
+ }
+ st=sane_read(self->h, buffer,
+ remainh);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ bufpos -= lastlen;
+ lastlen = len;
+ remain -= len;
+ /* skip possible pad bytes at the start of the buffer */
+ len -= bufpos;
+ }
+ break;
+ case 8:
+ remain = p.bytes_per_line * im->ysize;
+ padbytes = p.bytes_per_line - im->xsize;
+ bufpos = 0;
+ len = 0;
+ while (st!=SANE_STATUS_EOF && py < im->ysize)
+ {
+ while (len > 0 && py < im->ysize)
+ {
+ cplen = len;
+ if (px + cplen >= im->xsize)
+ cplen = im->xsize - px;
+ memcpy(&im->image8[py][px], &buffer[bufpos], cplen);
+ len -= cplen;
+ bufpos += cplen;
+ px += cplen;
+ if (px >= im->xsize)
+ {
+ px = 0;
+ py++;
+ bufpos += padbytes;
+ len -= padbytes;
+ }
+ }
+ bufpos = -len;
+
+ st=sane_read(self->h, buffer,
+ remainh);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ remain -= len;
+ len -= bufpos;
+ }
+ break;
+ case 16:
+ remain = p.bytes_per_line * im->ysize;
+ padbytes = p.bytes_per_line - 2 * im->xsize;
+ bufpos = endian.c[0];
+ lastlen = len = 0;
+ while (st!=SANE_STATUS_EOF && py < im->ysize)
+ {
+ while (len > 0 && py < im->ysize)
+ {
+ im->image8[py][px++] = buffer[bufpos];
+ bufpos += 2;
+ len -= 2;
+ if (px >= im->xsize)
+ {
+ bufpos += padbytes;
+ len -= padbytes;
+ py++;
+ px = 0;
+ }
+ }
+ st=sane_read(self->h, buffer,
+ remainh);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ remain -= len;
+ bufpos -= lastlen;
+ lastlen = len;
+ len -= bufpos;
+ }
+ break;
+ default:
+ /* other depths are not formally "illegal" according to the
+ Sane API, but it's agreed by Sane developers that other
+ depths than 1, 8, 16 should not be used
+ */
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth);
+ PyErr_SetString(ErrorObject, errmsg);
+ return NULL;
+ }
+ }
+ else if (p.format == SANE_FRAME_RGB)
+ {
+ int incr, color, pxs, pxmax, bit, val, mask;
+ switch (p.depth)
+ {
+ case 1:
+ remain = p.bytes_per_line * im->ysize;
+ padbytes = p.bytes_per_line - ((im->xsize+7)/8) * 3;
+ bufpos = 0;
+ len = 0;
+ lastlen = 0;
+ pxmax = 4 * im->xsize;
+ while (st!=SANE_STATUS_EOF && py < im->ysize)
+ {
+ pxs = px;
+ for (color = 0; color < 3; color++)
+ {
+ while (len <= 0 && st == SANE_STATUS_GOOD)
+ {
+ st=sane_read(self->h, buffer,
+ remainh);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ bufpos -= lastlen;
+ remain -= len;
+ lastlen = len;
+ /* skip possible pad bytes at the start of the buffer */
+ len -= bufpos;
+ }
+ if (st == SANE_STATUS_EOF) break;
+ pxs = px;
+ val = buffer[bufpos++];
+ len--;
+ mask = 0x80;
+ for (bit = 0; (bit < 8) && (px < pxmax); bit++)
+ {
+ ((UINT8**)(im->image32))[py][px] = (val&mask) ? 0xFF : 0;
+ mask = mask >> 1;
+ px += 4;
+ }
+ pxs++;
+ px = pxs;
+ }
+ if (st == SANE_STATUS_EOF)
+ break;
+ for (bit = 0; bit < 8 && px < pxmax; bit++)
+ {
+ ((UINT8**)(im->image32))[py][px] = 0;
+ px += 4;
+ }
+ px -= 3;
+ if (px >= pxmax)
+ {
+ bufpos += padbytes;
+ len -= padbytes;
+ py++;
+ px = 0;
+ }
+ }
+ break;
+ case 8:
+ case 16:
+ if (p.depth == 8)
+ {
+ padbytes = p.bytes_per_line - 3 * im->xsize;
+ bufpos = 0;
+ incr = 1;
+ }
+ else
+ {
+ padbytes = p.bytes_per_line - 6 * im->xsize;
+ bufpos = endian.c[0];
+ incr = 2;
+ }
+ remain = p.bytes_per_line * im->ysize;
+ len = 0;
+ lastlen = 0;
+ pxmax = 4 * im->xsize;
+ /* probably not very efficient. But we have to deal with these
+ possible conditions:
+ - 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
+ red/green/blue pixel values
+
+ */
+ while (st != SANE_STATUS_EOF && py < im->ysize)
+ {
+ for (color = 0; color < 3; color++)
+ {
+ while (len <= 0 && st == SANE_STATUS_GOOD)
+ {
+ bufpos -= lastlen;
+ if (remain == 0)
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ PyErr_SetString(ErrorObject, "internal _sane error: premature end of scan");
+ return NULL;
+ }
+ st = sane_read(self->h, buffer,
+ remain<(READSIZE) ? remain : (READSIZE), &len);
+ if (st && (st!=SANE_STATUS_EOF))
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ lastlen = len;
+ remain -= len;
+ len -= bufpos;
+ }
+ if (st == SANE_STATUS_EOF) break;
+ ((UINT8**)(im->image32))[py][px++] = buffer[bufpos];
+ bufpos += incr;
+ len -= incr;
+ }
+ if (st == SANE_STATUS_EOF) break;
+
+ ((UINT8**)(im->image32))[py][px++] = 0;
+
+ if (px >= pxmax)
+ {
+ px = 0;
+ py++;
+ bufpos += padbytes;
+ len -= padbytes;
+ }
+ }
+ break;
+ default:
+ Py_BLOCK_THREADS
+ sane_cancel(self->h);
+ snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth);
+ 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
+ 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...
+ So let's count the number of frames we try to acquire
+ */
+ while (!p.last_frame && frame_count < 4)
+ {
+ frame_count++;
+ st = sane_get_parameters(self->h, &p);
+ if (st)
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ remain = p.bytes_per_line * im->ysize;
+ bufpos = 0;
+ len = 0;
+ lastlen = 0;
+ py = 0;
+ switch (p.format)
+ {
+ case SANE_FRAME_RED:
+ offset = 0;
+ break;
+ case SANE_FRAME_GREEN:
+ offset = 1;
+ break;
+ case SANE_FRAME_BLUE:
+ offset = 2;
+ break;
+ default:
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ snprintf(errmsg, 80, "unknown/invalid frame format: %i", p.format);
+ PyErr_SetString(ErrorObject, errmsg);
+ return NULL;
+ }
+ px = offset;
+ pxa = 3;
+ pxmax = im->xsize * 4;
+ switch (p.depth)
+ {
+ case 1:
+ padbytes = p.bytes_per_line - (im->xsize+7)/8;
+ st = SANE_STATUS_GOOD;
+ while (st != SANE_STATUS_EOF && py < im->ysize)
+ {
+ while (len > 0)
+ {
+ int bit, mask, val;
+ val = buffer[bufpos++]; len--;
+ mask = 0x80;
+ for (bit = 0; bit < 8 && px < pxmax; bit++)
+ {
+ ((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;
+ pxa = 3;
+ py++;
+ bufpos += padbytes;
+ len -= padbytes;
+ }
+ }
+ while (len <= 0 && st == SANE_STATUS_GOOD && remain > 0)
+ {
+ bufpos -= lastlen;
+ st = sane_read(self->h, buffer,
+ remain<(READSIZE) ? remain : (READSIZE), &len);
+ if (st && (st!=SANE_STATUS_EOF))
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ remain -= len;
+ lastlen = len;
+ len -= bufpos;
+ }
+ }
+ break;
+ case 8:
+ case 16:
+ if (p.depth == 8)
+ {
+ padbytes = p.bytes_per_line - im->xsize;
+ incr = 1;
+ }
+ else
+ {
+ padbytes = p.bytes_per_line - 2 * im->xsize;
+ incr = 2;
+ bufpos = endian.c[0];
+ }
+ st = SANE_STATUS_GOOD;
+ while (st != SANE_STATUS_EOF && py < im->ysize)
+ {
+ while (len <= 0)
+ {
+ bufpos -= lastlen;
+ if (remain == 0)
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ PyErr_SetString(ErrorObject, "internal _sane error: premature end of scan");
+ return NULL;
+ }
+ st = sane_read(self->h, buffer,
+ remain<(READSIZE) ? remain : (READSIZE), &len);
+ if (st && (st!=SANE_STATUS_EOF))
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ if (st == SANE_STATUS_EOF)
+ break;
+ lastlen = len;
+ remain -= len;
+ if (bufpos >= len)
+ len = 0;
+ else
+ len -= bufpos;
+ }
+ if (st == SANE_STATUS_EOF)
+ break;
+ ((UINT8**)(im->image32))[py][px] = buffer[bufpos];
+ ((UINT8**)(im->image32))[py][pxa] = 0;
+ bufpos += incr;
+ len -= incr;
+ px += 4;
+ pxa += 4;
+
+ if (px >= pxmax)
+ {
+ px = offset;
+ pxa = 3;
+ py++;
+ bufpos += padbytes;
+ len -= padbytes;
+ }
+ }
+ break;
+ default:
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth);
+ PyErr_SetString(ErrorObject, errmsg);
+ return NULL;
+ }
+ if (!p.last_frame)
+ {
+ /* all sane_read calls in the above loop may return
+ SANE_STATUS_GOOD, but the backend may need another sane_read
+ call which returns SANE_STATUS_EOF in order to start
+ a new frame.
+ */
+ do {
+ st = sane_read(self->h, buffer, READSIZE, &len);
+ }
+ while (st == SANE_STATUS_GOOD);
+ if (st != SANE_STATUS_EOF)
+ {
+ Py_BLOCK_THREADS
+ sane_cancel(self->h);
+ return PySane_Error(st);
+ }
+
+ st = sane_start(self->h);
+ if (st)
+ {
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+ }
+ }
+ }
+ /* enforce SANE_STATUS_EOF. Can be necessary for ADF scans for some backends */
+ do {
+ st = sane_read(self->h, buffer, READSIZE, &len);
+ }
+ while (st == SANE_STATUS_GOOD);
+ if (st != SANE_STATUS_EOF)
+ {
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ return PySane_Error(st);
+ }
+
+ if (!noCancel)
+ sane_cancel(self->h);
+ Py_BLOCK_THREADS
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+#ifdef WITH_NUMARRAY
+
+#include "numarray/libnumarray.h"
+
+/* this global variable is set to 1 in 'init_sane()' after successfully
+ importing the numarray module. */
+int NUMARRAY_IMPORTED = 0;
+
+static PyObject *
+SaneDev_arr_snap(SaneDevObject *self, PyObject *args)
+{
+ SANE_Status st;
+ SANE_Byte buffer[READSIZE];
+ SANE_Int len;
+ SANE_Parameters p;
+
+ PyArrayObject *pyArr = NULL;
+ NumarrayType arrType;
+ int line, line_index, buffer_index, remain_bytes_line, num_pad_bytes;
+ int cp_num_bytes, total_remain, bpp, arr_bytes_per_line;
+ int pixels_per_line = -1;
+ char errmsg[80];
+
+ if (!NUMARRAY_IMPORTED)
+ {
+ PyErr_SetString(ErrorObject, "numarray package not available");
+ return NULL;
+ }
+
+ if (!PyArg_ParseTuple(args, "|i", &pixels_per_line))
+ return NULL;
+ if (self->h==NULL)
+ {
+ PyErr_SetString(ErrorObject, "SaneDev object is closed");
+ return NULL;
+ }
+
+ sane_get_parameters(self->h, &p);
+ if (p.format != SANE_FRAME_GRAY)
+ {
+ sane_cancel(self->h);
+ snprintf(errmsg, 80, "numarray only supports gray-scale images");
+ PyErr_SetString(ErrorObject, errmsg);
+ return NULL;
+ }
+
+ if (p.depth == 8)
+ {
+ bpp=1; /* bytes-per-pixel */
+ arrType = tUInt8;
+ }
+ else if (p.depth == 16)
+ {
+ bpp=2; /* bytes-per-pixel */
+ arrType = tUInt16;
+ }
+ else
+ {
+ sane_cancel(self->h);
+ snprintf(errmsg, 80, "arrsnap: unsupported pixel depth: %i", p.depth);
+ PyErr_SetString(ErrorObject, errmsg);
+ return NULL;
+ }
+
+ if (pixels_per_line < 1)
+ /* The user can choose a smaller result array than the actual scan */
+ pixels_per_line = p.pixels_per_line;
+ else
+ if (pixels_per_line > p.pixels_per_line)
+ {
+ PyErr_SetString(ErrorObject,"given pixels_per_line too big");
+ return NULL;
+ }
+ /* important: NumArray have indices like (y, x) !! */
+ if (!(pyArr = NA_NewArray(NULL, arrType, 2, p.lines, pixels_per_line)))
+ {
+ 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,
+ 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,
+ READSIZE < total_remain ? READSIZE : total_remain, &len);
+ Py_END_ALLOW_THREADS
+#ifdef WRITE_PGM
+ printf("p5_write: read %d of %d\n", len, READSIZE);
+ fwrite(buffer, 1, len, fp);
+#endif
+
+ buffer_index = 0;
+ total_remain -= len;
+
+ while (len > 0)
+ {
+ /* copy at most the number of bytes that fit into (the rest of)
+ one line: */
+ cp_num_bytes = (len > remain_bytes_line ? remain_bytes_line : len);
+ remain_bytes_line -= cp_num_bytes;
+ len -= cp_num_bytes;
+#ifdef DEBUG
+ printf("copying %d bytes from b_idx %d to d_idx %d\n",
+ cp_num_bytes, buffer_index,
+ line * arr_bytes_per_line + line_index);
+ printf("len is now %d\n", len);
+#endif
+ memcpy(pyArr->data + line * arr_bytes_per_line + line_index,
+ buffer + buffer_index, cp_num_bytes);
+
+ buffer_index += cp_num_bytes;
+ if (remain_bytes_line ==0)
+ {
+ /* The line has been completed, so reinitialize remain_bytes_line
+ increase the line counter, and reset line_index */
+#ifdef DEBUG
+ printf("line %d full, skipping %d bytes\n",line,num_pad_bytes);
+#endif
+ remain_bytes_line = arr_bytes_per_line;
+ line++;
+ line_index = 0;
+ /* Skip the number of bytes in the input stream which
+ are not used: */
+ len -= num_pad_bytes;
+ buffer_index += num_pad_bytes;
+ }
+ else
+ line_index += cp_num_bytes;
+ }
+ }
+#ifdef WRITE_PGM
+ fclose(fp);
+ printf("p5_write finished\n");
+#endif
+ sane_cancel(self->h);
+ return (PyObject*) pyArr;
+}
+
+
+
+#endif /* WITH_NUMARRAY */
+
+static PyMethodDef SaneDev_methods[] = {
+ {"get_parameters", (PyCFunction)SaneDev_get_parameters, 1},
+
+ {"get_options", (PyCFunction)SaneDev_get_options, 1},
+ {"get_option", (PyCFunction)SaneDev_get_option, 1},
+ {"set_option", (PyCFunction)SaneDev_set_option, 1},
+ {"set_auto_option", (PyCFunction)SaneDev_set_auto_option, 1},
+
+ {"start", (PyCFunction)SaneDev_start, 1},
+ {"cancel", (PyCFunction)SaneDev_cancel, 1},
+ {"snap", (PyCFunction)SaneDev_snap, 1},
+#ifdef WITH_NUMARRAY
+ {"arr_snap", (PyCFunction)SaneDev_arr_snap, 1},
+#endif /* WITH_NUMARRAY */
+ {"fileno", (PyCFunction)SaneDev_fileno, 1},
+ {"close", (PyCFunction)SaneDev_close, 1},
+ {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*/
+ "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*/
+};
+
+/* --------------------------------------------------------------------- */
+
+static PyObject *
+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);
+ if (st) return PySane_Error(st);
+ return Py_BuildValue("iiii", version, SANE_VERSION_MAJOR(version),
+ SANE_VERSION_MINOR(version), SANE_VERSION_BUILD(version));
+}
+
+static PyObject *
+PySane_exit(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ sane_exit();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+PySane_get_devices(PyObject *self, PyObject *args)
+{
+ SANE_Device **devlist;
+ SANE_Device *dev;
+ SANE_Status st;
+ PyObject *list;
+ int local_only, i;
+
+ if (!PyArg_ParseTuple(args, "|i", &local_only))
+ {
+ return NULL;
+ }
+
+ st=sane_get_devices(&devlist, local_only);
+ 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,
+ dev->model, dev->type));
+ }
+
+ return list;
+}
+
+/* Function returning new SaneDev object */
+
+static PyObject *
+PySane_open(PyObject *self, PyObject *args)
+{
+ SaneDevObject *rv;
+ SANE_Status st;
+ char *name;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+ rv = newSaneDevObject();
+ if ( rv == NULL )
+ return NULL;
+ st = sane_open(name, &(rv->h));
+ if (st)
+ {
+ Py_DECREF(rv);
+ return PySane_Error(st);
+ }
+ return (PyObject *)rv;
+}
+
+static PyObject *
+PySane_OPTION_IS_ACTIVE(PyObject *self, PyObject *args)
+{
+ SANE_Int cap;
+ long lg;
+
+ if (!PyArg_ParseTuple(args, "l", &lg))
+ return NULL;
+ cap=lg;
+ return PyInt_FromLong( SANE_OPTION_IS_ACTIVE(cap));
+}
+
+static PyObject *
+PySane_OPTION_IS_SETTABLE(PyObject *self, PyObject *args)
+{
+ SANE_Int cap;
+ long lg;
+
+ if (!PyArg_ParseTuple(args, "l", &lg))
+ return NULL;
+ cap=lg;
+ return PyInt_FromLong( SANE_OPTION_IS_SETTABLE(cap));
+}
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef PySane_methods[] = {
+ {"init", PySane_init, 1},
+ {"exit", PySane_exit, 1},
+ {"get_devices", PySane_get_devices, 1},
+ {"_open", PySane_open, 1},
+ {"OPTION_IS_ACTIVE", PySane_OPTION_IS_ACTIVE, 1},
+ {"OPTION_IS_SETTABLE", PySane_OPTION_IS_SETTABLE, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+
+static void
+insint(PyObject *d, char *name, int value)
+{
+ PyObject *v = PyInt_FromLong((long) value);
+ if (!v || PyDict_SetItemString(d, name, v))
+ Py_FatalError("can't initialize sane module");
+
+ Py_DECREF(v);
+}
+
+void
+init_sane(void)
+{
+ PyObject *m, *d;
+
+ /* Create the module and add the functions */
+ m = Py_InitModule("_sane", PySane_methods);
+
+ /* Add some symbolic constants to the module */
+ d = PyModule_GetDict(m);
+ ErrorObject = PyString_FromString("_sane.error");
+ PyDict_SetItemString(d, "error", ErrorObject);
+
+ insint(d, "INFO_INEXACT", SANE_INFO_INEXACT);
+ insint(d, "INFO_RELOAD_OPTIONS", SANE_INFO_RELOAD_OPTIONS);
+ insint(d, "RELOAD_PARAMS", SANE_INFO_RELOAD_PARAMS);
+
+ insint(d, "FRAME_GRAY", SANE_FRAME_GRAY);
+ insint(d, "FRAME_RGB", SANE_FRAME_RGB);
+ insint(d, "FRAME_RED", SANE_FRAME_RED);
+ insint(d, "FRAME_GREEN", SANE_FRAME_GREEN);
+ insint(d, "FRAME_BLUE", SANE_FRAME_BLUE);
+
+ insint(d, "CONSTRAINT_NONE", SANE_CONSTRAINT_NONE);
+ insint(d, "CONSTRAINT_RANGE", SANE_CONSTRAINT_RANGE);
+ insint(d, "CONSTRAINT_WORD_LIST", SANE_CONSTRAINT_WORD_LIST);
+ insint(d, "CONSTRAINT_STRING_LIST", SANE_CONSTRAINT_STRING_LIST);
+
+ insint(d, "TYPE_BOOL", SANE_TYPE_BOOL);
+ insint(d, "TYPE_INT", SANE_TYPE_INT);
+ insint(d, "TYPE_FIXED", SANE_TYPE_FIXED);
+ insint(d, "TYPE_STRING", SANE_TYPE_STRING);
+ insint(d, "TYPE_BUTTON", SANE_TYPE_BUTTON);
+ insint(d, "TYPE_GROUP", SANE_TYPE_GROUP);
+
+ insint(d, "UNIT_NONE", SANE_UNIT_NONE);
+ insint(d, "UNIT_PIXEL", SANE_UNIT_PIXEL);
+ insint(d, "UNIT_BIT", SANE_UNIT_BIT);
+ insint(d, "UNIT_MM", SANE_UNIT_MM);
+ insint(d, "UNIT_DPI", SANE_UNIT_DPI);
+ insint(d, "UNIT_PERCENT", SANE_UNIT_PERCENT);
+ insint(d, "UNIT_MICROSECOND", SANE_UNIT_MICROSECOND);
+
+ insint(d, "CAP_SOFT_SELECT", SANE_CAP_SOFT_SELECT);
+ insint(d, "CAP_HARD_SELECT", SANE_CAP_HARD_SELECT);
+ insint(d, "CAP_SOFT_DETECT", SANE_CAP_SOFT_DETECT);
+ insint(d, "CAP_EMULATED", SANE_CAP_EMULATED);
+ insint(d, "CAP_AUTOMATIC", SANE_CAP_AUTOMATIC);
+ insint(d, "CAP_INACTIVE", SANE_CAP_INACTIVE);
+ insint(d, "CAP_ADVANCED", SANE_CAP_ADVANCED);
+
+ /* handy for checking array lengths: */
+ insint(d, "SANE_WORD_SIZE", sizeof(SANE_Word));
+
+ /* possible return values of set_option() */
+ 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");
+
+#ifdef WITH_NUMARRAY
+ import_libnumarray();
+ if (PyErr_Occurred())
+ PyErr_Clear();
+ else
+ /* 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 */
+}
diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py
new file mode 100644
index 000000000..0104af2d5
--- /dev/null
+++ b/Sane/demo_numarray.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+#
+# Shows how to scan a 16 bit grayscale image into a numarray object
+#
+
+# Get the path set up to find PIL modules if not installed yet:
+import sys ; sys.path.append('../PIL')
+
+from numarray import *
+import sane
+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())
+ 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())
+ return im
+
+print 'SANE version:', sane.init()
+print 'Available devices=', sane.get_devices()
+
+s = sane.open(sane.get_devices()[0][0])
+
+# Set scan parameters
+s.mode = 'gray'
+s.br_x=320. ; s.br_y=240.
+
+print 'Device parameters:', s.get_parameters()
+
+s.depth=16
+arr16 = s.arr_scan()
+toImage(arr16).show()
diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py
new file mode 100644
index 000000000..016361f8a
--- /dev/null
+++ b/Sane/demo_pil.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+#
+# Shows how to scan a color image into a PIL rgb-image
+#
+
+# 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()
+
+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()
+
+# Initiate the scan
+s.start()
+
+# Get an Image object
+# (For my B&W QuickCam, this is a grey-scale image. Other scanning devices
+# may return a
+im=s.snap()
+
+# Write the image out as a GIF file
+#im.save('foo.gif')
+
+# The show method() simply saves the image to a temporary file and calls "xv".
+im.show()
diff --git a/Sane/sane.py b/Sane/sane.py
new file mode 100644
index 000000000..27be5a259
--- /dev/null
+++ b/Sane/sane.py
@@ -0,0 +1,289 @@
+# sane.py
+#
+# Python wrapper on top of the _sane module, which is in turn a very
+# thin wrapper on top of the SANE library. For a complete understanding
+# of SANE, consult the documentation at the SANE home page:
+# http://www.mostang.com/sane/ .
+
+__version__ = '2.0'
+__author__ = ['Andrew Kuchling', 'Ralph Heinkel']
+
+from PIL import Image
+
+import _sane
+from _sane import *
+
+TYPE_STR = { TYPE_BOOL: "TYPE_BOOL", TYPE_INT: "TYPE_INT",
+ TYPE_FIXED: "TYPE_FIXED", TYPE_STRING: "TYPE_STRING",
+ TYPE_BUTTON: "TYPE_BUTTON", TYPE_GROUP: "TYPE_GROUP" }
+
+UNIT_STR = { UNIT_NONE: "UNIT_NONE",
+ UNIT_PIXEL: "UNIT_PIXEL",
+ UNIT_BIT: "UNIT_BIT",
+ UNIT_MM: "UNIT_MM",
+ UNIT_DPI: "UNIT_DPI",
+ UNIT_PERCENT: "UNIT_PERCENT",
+ UNIT_MICROSECOND: "UNIT_MICROSECOND" }
+
+
+class Option:
+ """Class representing a SANE option.
+ Attributes:
+ index -- number from 0 to n, giving the option number
+ name -- a string uniquely identifying the option
+ title -- single-line string containing a title for the option
+ desc -- a long string describing the option; useful as a help message
+ type -- type of this option. Possible values: TYPE_BOOL,
+ TYPE_INT, TYPE_STRING, and so forth.
+ unit -- units of this option. Possible values: UNIT_NONE,
+ UNIT_PIXEL, etc.
+ size -- size of the value in bytes
+ cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc.
+ constraint -- constraint on values. Possible values:
+ None : No constraint
+ (min,max,step) Integer values, from min to max, stepping by
+ list of integers or strings: only the listed values are allowed
+ """
+
+ 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]
+ self.type, self.unit = args[4], args[5]
+ self.size, self.cap = args[6], args[7]
+ self.constraint = args[8]
+ 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), '')
+
+ def is_active(self):
+ return _sane.OPTION_IS_ACTIVE(self.cap)
+ def is_settable(self):
+ return _sane.OPTION_IS_SETTABLE(self.cap)
+ def __repr__(self):
+ if self.is_settable():
+ settable = 'yes'
+ else:
+ settable = 'no'
+ if self.is_active():
+ active = 'yes'
+ curValue = repr(getattr(self.scanDev, self.py_name))
+ else:
+ active = 'no'
+ curValue = ''
+ s = """\nName: %s
+Cur value: %s
+Index: %d
+Title: %s
+Desc: %s
+Type: %s
+Unit: %s
+Constr: %s
+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)
+ return s
+
+
+class _SaneIterator:
+ """ intended for ADF scans.
+ """
+
+ def __init__(self, device):
+ self.device = device
+
+ def __iter__(self):
+ return self
+
+ def __del__(self):
+ self.device.cancel()
+
+ def next(self):
+ try:
+ self.device.start()
+ except error, v:
+ if v == 'Document feeder out of documents':
+ raise StopIteration
+ else:
+ raise
+ return self.device.snap(1)
+
+
+
+class SaneDev:
+ """Class representing a SANE device.
+ Methods:
+ start() -- initiate a scan, using the current settings
+ snap() -- snap a picture, returning an Image object
+ arr_snap() -- snap a picture, returning a numarray object
+ cancel() -- cancel an in-progress scanning operation
+ fileno() -- return the file descriptor for the scanner (handy for select)
+
+ Also available, but rather low-level:
+ get_parameters() -- get the current parameter settings of the device
+ get_options() -- return a list of tuples describing all the options.
+
+ Attributes:
+ optlist -- list of option names
+
+ You can also access an option name to retrieve its value, and to
+ set it. For example, if one option has a .name attribute of
+ imagemode, and scanner is a SaneDev object, you can do:
+ print scanner.imagemode
+ scanner.imagemode = 'Full frame'
+ scanner.['imagemode'] returns the corresponding Option object.
+ """
+ def __init__(self, devname):
+ d=self.__dict__
+ d['sane_signature'] = self._getSaneSignature(devname)
+ d['scanner_model'] = d['sane_signature'][1:3]
+ d['dev'] = _sane._open(devname)
+ self.__load_option_dict()
+
+ def _getSaneSignature(self, devname):
+ devices = get_devices()
+ if not devices:
+ raise RuntimeError('no scanner available')
+ for dev in devices:
+ if devname == dev[0]:
+ return dev
+ raise RuntimeError('no such scan device "%s"' % devname)
+
+ def __load_option_dict(self):
+ d=self.__dict__
+ d['opt']={}
+ optlist=d['dev'].get_options()
+ for t in optlist:
+ o=Option(t, self)
+ if o.type!=TYPE_GROUP:
+ d['opt'][o.py_name]=o
+
+ def __setattr__(self, key, value):
+ dev=self.__dict__['dev']
+ optdict=self.__dict__['opt']
+ if not optdict.has_key(key):
+ self.__dict__[key]=value ; return
+ opt=optdict[key]
+ if opt.type==TYPE_GROUP:
+ raise AttributeError, "Groups can't be set: "+key
+ if not _sane.OPTION_IS_ACTIVE(opt.cap):
+ 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:
+ # avoid annoying errors of backend if int is given instead float:
+ value = float(value)
+ self.last_opt = dev.set_option(opt.index, value)
+ # do binary AND to find if we have to reload options:
+ if self.last_opt & INFO_RELOAD_OPTIONS:
+ self.__load_option_dict()
+
+ def __getattr__(self, key):
+ dev=self.__dict__['dev']
+ optdict=self.__dict__['opt']
+ if key=='optlist':
+ return 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
+ opt=optdict[key]
+ if opt.type==TYPE_BUTTON:
+ raise AttributeError, "Buttons don't have values: "+key
+ if opt.type==TYPE_GROUP:
+ raise AttributeError, "Groups don't have values: "+key
+ if not _sane.OPTION_IS_ACTIVE(opt.cap):
+ raise AttributeError, 'Inactive option: '+key
+ value = dev.get_option(opt.index)
+ return value
+
+ def __getitem__(self, key):
+ return self.opt[key]
+
+ def get_parameters(self):
+ """Return a 5-tuple holding all the current device settings:
+ (format, last_frame, (pixels_per_line, lines), depth, bytes_per_line)
+
+- format is one of 'L' (grey), 'RGB', 'R' (red), 'G' (green), 'B' (blue).
+- last_frame [bool] indicates if this is the last frame of a multi frame image
+- (pixels_per_line, lines) specifies the size of the scanned image (x,y)
+- lines denotes the number of scanlines per frame
+- depth gives number of pixels per sample
+"""
+ return self.dev.get_parameters()
+
+ def get_options(self):
+ "Return a list of tuples describing all the available options"
+ return self.dev.get_options()
+
+ def start(self):
+ "Initiate a scanning operation"
+ return self.dev.start()
+
+ def cancel(self):
+ "Cancel an in-progress scanning operation"
+ return self.dev.cancel()
+
+ def snap(self, no_cancel=0):
+ "Snap a picture, returning a PIL image object with the results"
+ (mode, last_frame,
+ (xsize, ysize), depth, bytes_per_line) = self.get_parameters()
+ if mode in ['gray', 'red', 'green', 'blue']:
+ format = 'L'
+ elif mode == 'color':
+ format = 'RGB'
+ else:
+ raise ValueError('got unknown "mode" from self.get_parameters()')
+ im=Image.new(format, (xsize,ysize))
+ self.dev.snap( im.im.id, no_cancel )
+ return im
+
+ def scan(self):
+ self.start()
+ return self.snap()
+
+ def multi_scan(self):
+ return _SaneIterator(self)
+
+ def arr_snap(self, multipleOf=1):
+ """Snap a picture, returning a numarray object with the results.
+ By default the resulting array has the same number of pixels per
+ line as specified in self.get_parameters()[2][0]
+ However sometimes it is necessary to obtain arrays where
+ the number of pixels per line is e.g. a multiple of 4. This can then
+ be achieved with the option 'multipleOf=4'. So if the scanner
+ scanned 34 pixels per line, you will obtain an array with 32 pixels
+ per line.
+ """
+ (mode, last_frame, (xsize, ysize), depth, bpl) = self.get_parameters()
+ if not mode in ['gray', 'red', 'green', 'blue']:
+ raise RuntimeError('arr_snap() only works with monochrome images')
+ if multipleOf < 1:
+ raise ValueError('option "multipleOf" must be a positive number')
+ elif multipleOf > 1:
+ pixels_per_line = xsize - divmod(xsize, 4)[1]
+ else:
+ pixels_per_line = xsize
+ return self.dev.arr_snap(pixels_per_line)
+
+ def arr_scan(self, multipleOf=1):
+ self.start()
+ return self.arr_snap(multipleOf=multipleOf)
+
+ def fileno(self):
+ "Return the file descriptor for the scanning device"
+ return self.dev.fileno()
+
+ def close(self):
+ self.dev.close()
+
+
+def open(devname):
+ "Open a device for scanning"
+ new=SaneDev(devname)
+ return new
diff --git a/Sane/sanedoc.txt b/Sane/sanedoc.txt
new file mode 100644
index 000000000..db86938e3
--- /dev/null
+++ b/Sane/sanedoc.txt
@@ -0,0 +1,294 @@
+The _sane_ module is an Python interface to the SANE (Scanning is Now
+Easy) library, which provides access to various raster scanning
+devices such as flatbed scanners and digital cameras. For more
+information about SANE, consult the SANE Web site at
+http://www.mostang.com/sane/ . Note that this
+documentation doesn't duplicate all the information in the SANE
+documentation, which you must also consult to get a complete
+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
+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.
+
+get_devices()
+ Return a list of 4-tuples containing the available scanning
+ devices. Each tuple contains 4 strings: the device name, suitable for
+ passing to _open()_; the device's vendor; the model; and the type of
+ device, such as 'virtual device' or 'video camera'.
+
+ >>> import sane ; sane.get_devices()
+ [('epson:libusb:001:004', 'Epson', 'GT-8300', 'flatbed scanner')]
+
+open(devicename)
+ Open a device, given a string containing its name. SANE
+ devices have names like 'epson:libusb:001:004'. If the attempt
+ to open the device fails, a _sane.error_ exception will be raised. If
+ there are no problems, a SaneDev object will be returned.
+ As an easy way to open the scanner (if only one is available) just type
+ >>> sane.open(sane.get_devices()[0][0])
+
+
+SaneDev objects
+===============
+
+The basic process of scanning an image consists of getting a SaneDev
+object for the device, setting various parameters, starting the scan,
+and then reading the image data. Images are composed of one or more
+frames; greyscale and one-pass colour scanners return a single frame
+containing all the image data, but 3-pass scanners will usually return
+3 frames, one for each of the red, green, blue channels.
+
+Methods:
+--------
+fileno()
+ Returns a file descriptor for the scanning device. This
+ method's existence means that SaneDev objects can be used by the
+ select module.
+
+get_parameters()
+ Return a tuple containing information about the current settings of
+ the device and the current frame: (format, last_frame,
+ pixels_per_line, lines, depth, bytes_per_line).
+
+ mode -- 'gray' for greyscale image, 'color' for RGB image, or
+ one of 'red', 'green', 'blue' if the image is a single
+ channel of an RGB image (from PIL's point of view,
+ this is equivalent to 'L').
+ last_frame -- A Boolean value, which is true if this is the
+ last frame of the image, and false otherwise.
+ pixels_per_line -- Width of the frame.
+ lines -- Height of the frame.
+ depth -- Depth of the image, measured in bits. SANE will only
+ allow using 8, 16, or 24-bit depths.
+ bytes_per_line -- Bytes required to store a single line of
+ data, as computed from pixels_per_line and depth.
+
+start()
+ Start a scan. This function must be called before the
+ _snap()_ method can be used.
+
+cancel()
+ Cancel a scan already in progress.
+
+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,
+ when snap() is called for the front side image. If no_cancel is true,
+ cancel() should be called manually, after all scans are finished.
+
+scan()
+ This is just a shortcut for s.start(); s.snap()
+ 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
+ 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
+ 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
+ this feature will not be activated).
+ By default the resulting array has the same number of pixels per
+ line as specified in self.get_parameters()[2][0]
+ However sometimes it is necessary to obtain arrays where
+ the number of pixels per line is e.g. a multiple of 4. This can then
+ be achieved with the option 'multipleOf=4'. So if the scanner
+ scanned 34 pixels per line, you will obtain an array with 32 pixels
+ per line.
+ Note that this only works with monochrome images (e.g. gray-scales)
+
+arr_scan(multipleOf=1)
+ This is just a shortcut for s.start(); s.arr_snap(multipleOf=1)
+ Returns a NumArray object
+
+close()
+ Closes the object.
+
+
+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.
+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
+value, or string, depending on the nature of the option.
+
+sane_signature
+ The tuple for this scandev that is returned by sane.get_devices()
+ e.g. ('epson:libusb:001:006', 'Epson', 'GT-8300', 'flatbed scanner')
+
+scanner_model
+ same as sane_signature[1:3], i.e. ('Epson', 'GT-8300') for the case above.
+
+optlist
+ A list containing the all the options supported by this device.
+
+ >>> import sane ; s=sane.open('epson:libusb:001:004') ; s.optlist
+ ['focus_position', 'color_correction', 'sharpness', ...., 'br_x']
+
+A closer look at all options listed in s.optlist can be obtained
+through the SaneOption objects.
+
+SaneOption objects
+==================
+
+SANE's option handling is its most elaborate subsystem, intended to
+allow automatically generating dialog boxes and prompts for user
+configuration of the scanning device. The SaneOption object can be
+used to get a human-readable name and description for an option, the
+units to use, and what the legal values are. No information about the
+current value of the option is available; for that, read the
+corresponding attribute of a SaneDev object.
+
+This documentation does not explain all the details of SANE's option
+handling; consult the SANE documentation for all the details.
+
+A scandevice option is accessed via __getitem__. For example
+s['mode'] returns the option descriptor for the mode-option which
+controls whether the scanner works in color, grayscale, or b/w mode.
+
+>>> s['mode']
+Name: mode
+Cur value: Color
+Index: 2
+Title: Scan mode
+Desc: Selects the scan mode (e.g., lineart, monochrome, or color).
+Type: TYPE_STRING
+Unit: UNIT_NONE
+Constr: ['Binary', 'Gray', 'Color']
+active: yes
+settable: yes
+
+In order to change 'mode' to 'gray', just type:
+>>> s.mode = 'gray'
+
+
+With the attributes and methods of sane-option objects it is possible
+to access individual option values:
+
+is_active()
+ Returns true if the option is active.
+
+is_settable()
+ Returns true if the option can be set under software control.
+
+
+Attributes:
+
+cap
+ An integer containing various flags about the object's
+ capabilities; whether it's active, whether it's settable, etc. Also
+ available as the _capability_ attribute.
+
+constraint
+ The constraint placed on the value of this option. If it's
+ _None_, there are essentially no constraint of the value. It may also
+ be a list of integers or strings, in which case the value *must* be
+ one of the possibilities in the list. Numeric values may have a
+ 3-tuple as the constraint; this 3-tuple contains _(minimum, maximum,
+ increment)_, and the value must be in the defined range.
+
+desc
+ A lengthy description of what the option does; it may be shown
+ to the user for clarification.
+
+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.
+
+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.
+
+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
+ constants found in the SANE module:
+ TYPE_BOOL
+ TYPE_INT
+ TYPE_FIXED
+ TYPE_STRING
+ TYPE_BUTTON
+ TYPE_GROUP
+
+unit
+ For numeric-valued options, this is a constant representing
+ the unit used for this option. It will be one of the following
+ constants found in the SANE module:
+ UNIT_NONE
+ UNIT_PIXEL
+ UNIT_BIT
+ UNIT_MM
+ UNIT_DPI
+ UNIT_PERCENT
+
+
+
+Example us usage:
+=================
+>>> import sane
+>>> print 'SANE version:', sane.init()
+>>> print 'Available devices=', sane.get_devices()
+SANE version: (16777230, 1, 0, 14)
+>>> s = sane.open(sane.get_devices()[0][0])
+>>> print 'Device parameters:', s.get_parameters()
+Device parameters: ('L', 1, (424, 585), 1, 53)
+>>> print s.resolution
+50
+
+## In order to scan a color image into a PIL object:
+>>> s.mode = 'color'
+>>> s.start()
+>>> img = s.snap()
+>>> img.show()
+
+
+## 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.resolution = 100
+>>> s.depth=16
+>>> s.start()
+>>> s.get_parameters()[2] # just check the size
+(624, 472)
+>>> arr16 = s.arr_snap()
+>>> arr16
+array([[63957, 64721, 65067, ..., 65535, 65535, 65535],
+ [63892, 64342, 64236, ..., 65535, 65535, 65535],
+ [64286, 64248, 64705, ..., 65535, 65535, 65535],
+ ...,
+ [65518, 65249, 65058, ..., 65535, 65535, 65535],
+ [64435, 65047, 65081, ..., 65535, 65535, 65535],
+ [65309, 65438, 65535, ..., 65535, 65535, 65535]], type=UInt16)
+>>> arr16.shape # inverse order of coordinates, first y, then x!
+(472, 624)
+
diff --git a/Sane/setup.py b/Sane/setup.py
new file mode 100644
index 000000000..3837198ec
--- /dev/null
+++ b/Sane/setup.py
@@ -0,0 +1,24 @@
+from distutils.core import setup, Extension
+
+PIL_BUILD_DIR = '..'
+PIL_IMAGING_DIR = PIL_BUILD_DIR+'/libImaging'
+
+defs = []
+try:
+ import numarray
+ defs.append(('WITH_NUMARRAY',None))
+except ImportError:
+ pass
+
+sane = Extension('_sane',
+ include_dirs = [PIL_IMAGING_DIR],
+ libraries = ['sane'],
+ library_dirs = [PIL_IMAGING_DIR],
+ define_macros = defs,
+ sources = ['_sane.c'])
+
+setup (name = 'pysane',
+ version = '2.0',
+ description = 'This is the pysane package',
+ py_modules = ['sane'],
+ ext_modules = [sane])
diff --git a/Scripts/README b/Scripts/README
new file mode 100644
index 000000000..a09b0621a
--- /dev/null
+++ b/Scripts/README
@@ -0,0 +1,83 @@
+-------
+Scripts
+-------
+
+This directory contains a number of more or less trivial utilities
+and demo programs.
+
+Comments and contributions are welcome.
+
+
+
+--------------------------------------------------------------------
+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.
+
+--------------------------------------------------------------------
+viewer.py
+
+A simple image viewer. Can display all file formats handled by
+PIL. Transparent images are properly handled.
+
+--------------------------------------------------------------------
+thresholder.py
+
+A simple utility that demonstrates how a transparent 1-bit overlay
+can be used to show the current thresholding of an 8-bit image.
+
+--------------------------------------------------------------------
+enhancer.py
+
+Illustrates the ImageEnhance module. Drag the sliders to modify the
+images. This might be very slow on some platforms, depending on the
+Tk version.
+
+--------------------------------------------------------------------
+painter.py
+
+Illustrates how a painting program could be based on PIL and Tk.
+Press the left mouse button and drag over the image to remove the
+colour. Some clever tricks have been used to get decent performance
+when updating the screen; see the sources for details.
+
+--------------------------------------------------------------------
+player.py
+
+A simple image sequence player. You can use either a sequence format
+like FLI/FLC, GIF, or ARG, or give a number of images which are
+interpreted as frames in a sequence. All frames must have the same
+size.
+
+--------------------------------------------------------------------
+gifmaker.py
+
+Convert a sequence file to a GIF animation.
+
+Note that the GIF encoder provided with this release of PIL writes
+uncompressed GIF files only, so the resulting animations are rather
+large compared with these created by other tools.
+
+--------------------------------------------------------------------
+explode.py
+
+Split a sequence file into individual frames.
+
+--------------------------------------------------------------------
+image2py.py
+
+Convert an image to a Python module containing an IMAGE variable.
+Note that the module using the module must include JPEG and ZIP
+decoders, unless the -u option is used.
+
+--------------------------------------------------------------------
+olesummary.py
+
+Uses the OleFileIO module to dump the summary information from an OLE
+structured storage file. This works with most OLE files, including
+Word documents, FlashPix images, etc.
+
+Note that datetime fields currently show the number of seconds since
+January 1st, 1601.
diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py
new file mode 100644
index 000000000..957b51c8d
--- /dev/null
+++ b/Scripts/enhancer.py
@@ -0,0 +1,53 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# this demo script creates four windows containing an image and a slider.
+# drag the slider to modify the image.
+#
+
+from Tkinter import *
+from PIL import Image, ImageTk, ImageEnhance
+import sys
+
+#
+# enhancer widget
+
+class Enhance(Frame):
+ def __init__(self, master, image, name, enhancer, lo, hi):
+ Frame.__init__(self, master)
+
+ # set up the image
+ self.tkim = ImageTk.PhotoImage(image.mode, image.size)
+ self.enhancer = enhancer(image)
+ self.update("1.0") # normalize
+
+ # image window
+ Label(self, image=self.tkim).pack()
+
+ # scale
+ s = Scale(self, label=name, orient=HORIZONTAL,
+ from_=lo, to=hi, resolution=0.01,
+ command=self.update)
+ s.set(self.value)
+ s.pack()
+
+ def update(self, value):
+ self.value = eval(value)
+ self.tkim.paste(self.enhancer.enhance(self.value))
+
+#
+# main
+
+root = Tk()
+
+im = Image.open(sys.argv[1])
+
+im.thumbnail((200, 200))
+
+Enhance(root, im, "Color", ImageEnhance.Color, 0.0, 4.0).pack()
+Enhance(Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack()
+Enhance(Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack()
+Enhance(Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack()
+
+root.mainloop()
diff --git a/Scripts/explode.py b/Scripts/explode.py
new file mode 100644
index 000000000..a336f0699
--- /dev/null
+++ b/Scripts/explode.py
@@ -0,0 +1,107 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# split an animation into a number of frame files
+#
+
+from PIL import Image
+import os, string, sys
+
+class Interval:
+
+ def __init__(self, interval = "0"):
+
+ self.setinterval(interval)
+
+ def setinterval(self, interval):
+
+ self.hilo = []
+
+ for s in string.split(interval, ","):
+ if not string.strip(s):
+ continue
+ try:
+ v = string.atoi(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:])
+
+ self.hilo.append((hi, lo))
+
+ if not self.hilo:
+ self.hilo = [(sys.maxint, 0)]
+
+ def __getitem__(self, index):
+
+ for hi, lo in self.hilo:
+ if hi >= index >= lo:
+ return 1
+ return 0
+
+# --------------------------------------------------------------------
+# main program
+
+html = 0
+
+if sys.argv[1:2] == ["-h"]:
+ html = 1
+ 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."
+ sys.exit(1)
+
+infile = sys.argv[1]
+outfile = sys.argv[2]
+
+frames = Interval(string.join(sys.argv[3:], ","))
+
+try:
+ # check if outfile contains a placeholder
+ outfile % 1
+except TypeError:
+ file, ext = os.path.splitext(outfile)
+ outfile = file + "%03d" + ext
+
+ix = 1
+
+im = Image.open(infile)
+
+if html:
+ file, ext = os.path.splitext(outfile)
+ html = open(file+".html", "w")
+ html.write("\n\n")
+
+while 1:
+
+ if frames[ix]:
+ im.save(outfile % ix)
+ print outfile % ix
+
+ if html:
+ html.write("
\n" % outfile % ix)
+
+ try:
+ im.seek(ix)
+ except EOFError:
+ break
+
+ ix = ix + 1
+
+if html:
+ html.write("\n\n")
diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py
new file mode 100644
index 000000000..95524eacc
--- /dev/null
+++ b/Scripts/gifmaker.py
@@ -0,0 +1,135 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# convert sequence format to GIF animation
+#
+# history:
+# 97-01-03 fl created
+#
+# Copyright (c) Secret Labs AB 1997. All rights reserved.
+# Copyright (c) Fredrik Lundh 1997.
+#
+# See the README file for information on usage and redistribution.
+#
+
+#
+# For special purposes, you can import this module and call
+# the makedelta or compress functions yourself. For example,
+# if you have an application that generates a sequence of
+# images, you can convert it to a GIF animation using some-
+# thing like the following code:
+#
+# import Image
+# import gifmaker
+#
+# sequence = []
+#
+# # generate sequence
+# for i in range(100):
+# im =
+# sequence.append(im)
+#
+# # write GIF animation
+# fp = open("out.gif", "wb")
+# gifmaker.makedelta(fp, sequence)
+# fp.close()
+#
+# Alternatively, use an iterator to generate the sequence, and
+# write data directly to a socket. Or something...
+#
+
+from PIL import Image, ImageChops
+import string
+
+from PIL.GifImagePlugin import getheader, getdata
+
+# --------------------------------------------------------------------
+# sequence iterator
+
+class image_sequence:
+ 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
+
+# --------------------------------------------------------------------
+# straightforward delta encoding
+
+def makedelta(fp, sequence):
+ """Convert list of image frames to a GIF animation file"""
+
+ frames = 0
+
+ previous = None
+
+ for im in sequence:
+
+ #
+ # FIXME: write graphics control block before each frame
+
+ if not previous:
+
+ # global header
+ for s in getheader(im) + getdata(im):
+ fp.write(s)
+
+ else:
+
+ # delta frame
+ delta = ImageChops.subtract_modulo(im, previous)
+
+ bbox = delta.getbbox()
+
+ if bbox:
+
+ # compress difference
+ for s in getdata(im.crop(bbox), offset = bbox[:2]):
+ fp.write(s)
+
+ else:
+ # FIXME: what should we do in this case?
+ pass
+
+ previous = im.copy()
+
+ frames = frames + 1
+
+ fp.write(";")
+
+ return frames
+
+# --------------------------------------------------------------------
+# main hack
+
+def compress(infile, outfile):
+
+ # open input image, and force loading of first frame
+ im = Image.open(infile)
+ im.load()
+
+ # open output file
+ fp = open(outfile, "wb")
+
+ seq = image_sequence(im)
+
+ makedelta(fp, seq)
+
+ fp.close()
+
+
+if __name__ == "__main__":
+
+ import sys
+
+ if len(sys.argv) < 3:
+ 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
new file mode 100644
index 000000000..efe307386
--- /dev/null
+++ b/Scripts/painter.py
@@ -0,0 +1,72 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# this demo script illustrates pasting into an already displayed
+# photoimage. note that the current version of Tk updates the whole
+# image everytime we paste, so to get decent performance, we split
+# the image into a set of tiles.
+#
+
+from Tkinter import *
+from PIL import Image, ImageTk
+import sys
+
+#
+# painter widget
+
+class PaintCanvas(Canvas):
+ def __init__(self, master, image):
+ Canvas.__init__(self, master, width=image.size[0], height=image.size[1])
+
+ # fill the canvas
+ self.tile = {}
+ self.tilesize = tilesize = 32
+ xsize, ysize = image.size
+ for x in range(0, xsize, tilesize):
+ for y in range(0, ysize, tilesize):
+ box = x, y, min(xsize, x+tilesize), min(ysize, y+tilesize)
+ tile = ImageTk.PhotoImage(image.crop(box))
+ self.create_image(x, y, image=tile, anchor=NW)
+ self.tile[(x,y)] = box, tile
+
+ self.image = image
+
+ self.bind("", self.paint)
+
+ def paint(self, event):
+ xy = event.x - 10, event.y - 10, event.x + 10, event.y + 10
+ im = self.image.crop(xy)
+
+ # process the image in some fashion
+ im = im.convert("L")
+
+ self.image.paste(im, xy)
+ self.repair(xy)
+
+ def repair(self, box):
+ # update canvas
+ dx = box[0] % self.tilesize
+ dy = box[1] % self.tilesize
+ for x in range(box[0]-dx, box[2]+1, self.tilesize):
+ for y in range(box[1]-dy, box[3]+1, self.tilesize):
+ try:
+ xy, tile = self.tile[(x, y)]
+ tile.paste(self.image.crop(xy))
+ except KeyError:
+ pass # outside the image
+ self.update_idletasks()
+
+#
+# main
+
+root = Tk()
+
+im = Image.open(sys.argv[1])
+
+if im.mode != "RGB":
+ im = im.convert("RGB")
+
+PaintCanvas(root, im).pack()
+
+root.mainloop()
diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py
new file mode 100644
index 000000000..1c688f7c9
--- /dev/null
+++ b/Scripts/pilconvert.py
@@ -0,0 +1,96 @@
+#! /usr/local/bin/python
+#
+# The Python Imaging Library.
+# $Id$
+#
+# convert image files
+#
+# History:
+# 0.1 96-04-20 fl Created
+# 0.2 96-10-04 fl Use draft mode when converting images
+# 0.3 96-12-30 fl Optimize output (PNG, JPEG)
+# 0.4 97-01-18 fl Made optimize an option (PNG, JPEG)
+# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter)
+#
+
+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"
+ sys.exit(1)
+
+if len(sys.argv) == 1:
+ usage()
+
+try:
+ opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r")
+except getopt.error, v:
+ print v
+ sys.exit(1)
+
+format = None
+convert = None
+
+options = { }
+
+for o, a in opt:
+
+ if o == "-f":
+ Image.init()
+ id = Image.ID[:]
+ id.sort()
+ print "Supported formats (* indicates output format):"
+ for i in id:
+ if Image.SAVE.has_key(i):
+ print i+"*",
+ else:
+ print i,
+ sys.exit(1)
+
+ elif o == "-c":
+ format = a
+
+ if o == "-g":
+ convert = "L"
+ elif o == "-p":
+ convert = "P"
+ elif o == "-r":
+ convert = "RGB"
+
+ elif o == "-o":
+ options["optimize"] = 1
+ elif o == "-q":
+ options["quality"] = string.atoi(a)
+
+if len(argv) != 2:
+ usage()
+
+try:
+ im = Image.open(argv[0])
+ if convert and im.mode != convert:
+ im.draft(convert, im.size)
+ im = im.convert(convert)
+ if format:
+ apply(im.save, (argv[1], format), options)
+ else:
+ apply(im.save, (argv[1],), options)
+except:
+ print "cannot convert image",
+ print "(%s:%s)" % (sys.exc_type, sys.exc_value)
diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py
new file mode 100644
index 000000000..5dd575a3f
--- /dev/null
+++ b/Scripts/pildriver.py
@@ -0,0 +1,524 @@
+#!/usr/bin/env python
+"""PILdriver, an image-processing calculator using PIL.
+
+An instance of class PILDriver is essentially a software stack machine
+(Polish-notation interpreter) for sequencing PIL image
+transformations. The state of the instance is the interpreter stack.
+
+The only method one will normally invoke after initialization is the
+`execute' method. This takes an argument list of tokens, pushes them
+onto the instance's stack, and then tries to clear the stack by
+successive evaluation of PILdriver operators. Any part of the stack
+not cleaned off persists and is part of the evaluation context for
+the next call of the execute method.
+
+PILDriver doesn't catch any exceptions, on the theory that these
+are actually diagnostic information that should be interpreted by
+the calling code.
+
+When called as a script, the command-line arguments are passed to
+a PILDriver instance. If there are no command-line arguments, the
+module runs an interactive interpreter, each line of which is split into
+space-separated tokens and passed to the execute method.
+
+In the method descriptions below, a first line beginning with the string
+`usage:' means this method can be invoked with the token that follows
+it. Following <>-enclosed arguments describe how the method interprets
+the entries on the stack. Each argument specification begins with a
+type specification: either `int', `float', `string', or `image'.
+
+All operations consume their arguments off the stack (use `dup' to
+keep copies around). Use `verbose 1' to see the stack state displayed
+before each operation.
+
+Usage examples:
+
+ `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion
+of its upper-left-hand corner and displays the cropped portion.
+
+ `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it
+30 degrees, and saves the result as rotated.png (in PNG format).
+"""
+# by Eric S. Raymond
+# $Id$
+
+# TO DO:
+# 1. Add PILFont capabilities, once that's documented.
+# 2. Add PILDraw operations.
+# 3. Add support for composing and decomposing multiple-image files.
+#
+
+from PIL import Image
+import string
+
+class PILDriver:
+
+ verbose = 0
+
+ def do_verbose(self):
+ """usage: verbose
+
+ Set verbosity flag from top of stack.
+ """
+ self.verbose = self.do_pop()
+
+ # The evaluation stack (internal only)
+
+ stack = [] # Stack of pending operations
+
+ def push(self, item):
+ "Push an argument onto the evaluation stack."
+ self.stack = [item] + self.stack
+
+ def top(self):
+ "Return the top-of-stack element."
+ return self.stack[0]
+
+ # Stack manipulation (callable)
+
+ def do_clear(self):
+ """usage: clear
+
+ Clear the stack.
+ """
+ self.stack = []
+
+ def do_pop(self):
+ """usage: pop
+
+ Discard the top element on the stack.
+ """
+ top = self.stack[0]
+ self.stack = self.stack[1:]
+ return top
+
+ def do_dup(self):
+ """usage: dup
+
+ Duplicate the top-of-stack item.
+ """
+ if hasattr(self, 'format'): # If it's an image, do a real copy
+ dup = self.stack[0].copy()
+ else:
+ dup = self.stack[0]
+ self.stack = [dup] + self.stack
+
+ def do_swap(self):
+ """usage: swap
+
+ Swap the top-of-stack item with the next one down.
+ """
+ self.stack = [self.stack[1], self.stack[0]] + self.stack[2:]
+
+ # Image module functions (callable)
+
+ def do_new(self):
+ """usage: new :
+
+ Create and push a greyscale image of given size and color.
+ """
+ xsize = int(self.do_pop())
+ ysize = int(self.do_pop())
+ color = int(self.do_pop())
+ self.push(Image.new("L", (xsize, ysize), color))
+
+ def do_open(self):
+ """usage: open
+
+ Open the indicated image, read it, push the image on the stack.
+ """
+ self.push(Image.open(self.do_pop()))
+
+ def do_blend(self):
+ """usage: blend
+
+ Replace two images and an alpha with the blended image.
+ """
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ alpha = float(self.do_pop())
+ self.push(Image.blend(image1, image2, alpha))
+
+ def do_composite(self):
+ """usage: composite
+
+ Replace two images and a mask with their composite.
+ """
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ mask = self.do_pop()
+ self.push(Image.composite(image1, image2, mask))
+
+ def do_merge(self):
+ """usage: merge [ [ []]]
+
+ Merge top-of stack images in a way described by the mode.
+ """
+ mode = self.do_pop()
+ bandlist = []
+ for band in mode:
+ bandlist.append(self.do_pop())
+ self.push(Image.merge(mode, bandlist))
+
+ # Image class methods
+
+ def do_convert(self):
+ """usage: convert
+
+ Convert the top image to the given mode.
+ """
+ mode = self.do_pop()
+ image = self.do_pop()
+ self.push(image.convert(mode))
+
+ def do_copy(self):
+ """usage: copy
+
+ Make and push a true copy of the top image.
+ """
+ self.dup()
+
+ def do_crop(self):
+ """usage: crop
+
+ Crop and push a rectangular region from the current image.
+ """
+ left = int(self.do_pop())
+ upper = int(self.do_pop())
+ right = int(self.do_pop())
+ lower = int(self.do_pop())
+ image = self.do_pop()
+ self.push(image.crop((left, upper, right, lower)))
+
+ def do_draft(self):
+ """usage: draft
+
+ Configure the loader for a given mode and size.
+ """
+ mode = self.do_pop()
+ xsize = int(self.do_pop())
+ ysize = int(self.do_pop())
+ self.push(self.draft(mode, (xsize, ysize)))
+
+ def do_filter(self):
+ """usage: filter
+
+ Process the top image with the given filter.
+ """
+ import ImageFilter
+ filter = eval("ImageFilter." + string.upper(self.do_pop()))
+ image = self.do_pop()
+ self.push(image.filter(filter))
+
+ def do_getbbox(self):
+ """usage: getbbox
+
+ Push left, upper, right, and lower pixel coordinates of the top image.
+ """
+ bounding_box = self.do_pop().getbbox()
+ self.push(bounding_box[3])
+ self.push(bounding_box[2])
+ self.push(bounding_box[1])
+ self.push(bounding_box[0])
+
+ def do_getextrema(self):
+ """usage: extrema
+
+ Push minimum and maximum pixel values of the top image.
+ """
+ extrema = self.do_pop().extrema()
+ self.push(extrema[1])
+ self.push(extrema[0])
+
+ def do_offset(self):
+ """usage: offset
+
+ Offset the pixels in the top image.
+ """
+ xoff = int(self.do_pop())
+ yoff = int(self.do_pop())
+ image = self.do_pop()
+ self.push(image.offset(xoff, yoff))
+
+ def do_paste(self):
+ """usage: paste
+
+ Paste figure image into ground with upper left at given offsets.
+ """
+ figure = self.do_pop()
+ xoff = int(self.do_pop())
+ yoff = int(self.do_pop())
+ ground = self.do_pop()
+ if figure.mode == "RGBA":
+ ground.paste(figure, (xoff, yoff), figure)
+ else:
+ ground.paste(figure, (xoff, yoff))
+ self.push(ground)
+
+ def do_resize(self):
+ """usage: resize
+
+ Resize the top image.
+ """
+ ysize = int(self.do_pop())
+ xsize = int(self.do_pop())
+ image = self.do_pop()
+ self.push(image.resize((xsize, ysize)))
+
+ def do_rotate(self):
+ """usage: rotate
+
+ Rotate image through a given angle
+ """
+ angle = int(self.do_pop())
+ image = self.do_pop()
+ self.push(image.rotate(angle))
+
+ def do_save(self):
+ """usage: save
+
+ Save image with default options.
+ """
+ filename = self.do_pop()
+ image = self.do_pop()
+ image.save(filename)
+
+ def do_save2(self):
+ """usage: save2
+
+ Save image with specified options.
+ """
+ filename = self.do_pop()
+ options = self.do_pop()
+ image = self.do_pop()
+ image.save(filename, None, options)
+
+ def do_show(self):
+ """usage: show
+
+ Display and pop the top image.
+ """
+ self.do_pop().show()
+
+ def do_thumbnail(self):
+ """usage: thumbnail
+
+ Modify the top image in the stack to contain a thumbnail of itself.
+ """
+ ysize = int(self.do_pop())
+ xsize = int(self.do_pop())
+ self.top().thumbnail((xsize, ysize))
+
+ def do_transpose(self):
+ """usage: transpose
+
+ Transpose the top image.
+ """
+ transpose = string.upper(self.do_pop())
+ image = self.do_pop()
+ self.push(image.transpose(transpose))
+
+ # Image attributes
+
+ def do_format(self):
+ """usage: format
+
+ Push the format of the top image onto the stack.
+ """
+ self.push(self.pop().format)
+
+ def do_mode(self):
+ """usage: mode
+
+ Push the mode of the top image onto the stack.
+ """
+ self.push(self.pop().mode)
+
+ def do_size(self):
+ """usage: size
+
+ Push the image size on the stack as (y, x).
+ """
+ size = self.pop().size
+ self.push(size[0])
+ self.push(size[1])
+
+ # ImageChops operations
+
+ def do_invert(self):
+ """usage: invert
+
+ Invert the top image.
+ """
+ import ImageChops
+ self.push(ImageChops.invert(self.do_pop()))
+
+ def do_lighter(self):
+ """usage: lighter
+
+ Pop the two top images, push an image of the lighter pixels of both.
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ self.push(ImageChops.lighter(image1, image2))
+
+ def do_darker(self):
+ """usage: darker
+
+ Pop the two top images, push an image of the darker pixels of both.
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ self.push(ImageChops.darker(image1, image2))
+
+ def do_difference(self):
+ """usage: difference
+
+ Pop the two top images, push the difference image
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ self.push(ImageChops.difference(image1, image2))
+
+ def do_multiply(self):
+ """usage: multiply
+
+ Pop the two top images, push the multiplication image.
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ self.push(ImageChops.multiply(image1, image2))
+
+ def do_screen(self):
+ """usage: screen
+
+ Pop the two top images, superimpose their inverted versions.
+ """
+ import ImageChops
+ image2 = self.do_pop()
+ image1 = self.do_pop()
+ self.push(ImageChops.screen(image1, image2))
+
+ def do_add(self):
+ """usage: add
+
+ Pop the two top images, produce the scaled sum with offset.
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ scale = float(self.do_pop())
+ offset = int(self.do_pop())
+ self.push(ImageChops.add(image1, image2, scale, offset))
+
+ def do_subtract(self):
+ """usage: subtract
+
+ Pop the two top images, produce the scaled difference with offset.
+ """
+ import ImageChops
+ image1 = self.do_pop()
+ image2 = self.do_pop()
+ scale = float(self.do_pop())
+ offset = int(self.do_pop())
+ self.push(ImageChops.subtract(image1, image2, scale, offset))
+
+ # ImageEnhance classes
+
+ def do_color(self):
+ """usage: color
+
+ Enhance color in the top image.
+ """
+ import ImageEnhance
+ factor = float(self.do_pop())
+ image = self.do_pop()
+ enhancer = ImageEnhance.Color(image)
+ self.push(enhancer.enhance(factor))
+
+ def do_contrast(self):
+ """usage: contrast
+
+ Enhance contrast in the top image.
+ """
+ import ImageEnhance
+ factor = float(self.do_pop())
+ image = self.do_pop()
+ enhancer = ImageEnhance.Color(image)
+ self.push(enhancer.enhance(factor))
+
+ def do_brightness(self):
+ """usage: brightness
+
+ Enhance brightness in the top image.
+ """
+ import ImageEnhance
+ factor = float(self.do_pop())
+ image = self.do_pop()
+ enhancer = ImageEnhance.Color(image)
+ self.push(enhancer.enhance(factor))
+
+ def do_sharpness(self):
+ """usage: sharpness
+
+ Enhance sharpness in the top image.
+ """
+ import ImageEnhance
+ factor = float(self.do_pop())
+ image = self.do_pop()
+ enhancer = ImageEnhance.Color(image)
+ self.push(enhancer.enhance(factor))
+
+ # The interpreter loop
+
+ def execute(self, list):
+ "Interpret a list of PILDriver commands."
+ list.reverse()
+ while len(list) > 0:
+ self.push(list[0])
+ list = list[1:]
+ if self.verbose:
+ print "Stack: " + `self.stack`
+ top = self.top()
+ if type(top) != type(""):
+ continue;
+ funcname = "do_" + top
+ if not hasattr(self, funcname):
+ continue
+ else:
+ self.do_pop()
+ func = getattr(self, funcname)
+ func()
+
+if __name__ == '__main__':
+ import sys
+ try:
+ import readline
+ except ImportError:
+ pass # not available on all platforms
+
+ # If we see command-line arguments, interpret them as a stack state
+ # and execute. Otherwise go interactive.
+
+ driver = PILDriver()
+ if len(sys.argv[1:]) > 0:
+ driver.execute(sys.argv[1:])
+ else:
+ print "PILDriver says hello."
+ while 1:
+ try:
+ line = raw_input('pildriver> ');
+ except EOFError:
+ print "\nPILDriver says goodbye."
+ break
+ driver.execute(string.split(line))
+ print driver.stack
+
+# The following sets edit modes for GNU EMACS
+# Local Variables:
+# mode:python
+# End:
diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py
new file mode 100644
index 000000000..695725796
--- /dev/null
+++ b/Scripts/pilfile.py
@@ -0,0 +1,94 @@
+#! /usr/local/bin/python
+#
+# The Python Imaging Library.
+# $Id$
+#
+# a utility to identify image files
+#
+# this script identifies image files, extracting size and
+# pixel mode information for known file formats. Note that
+# you don't need the PIL C extension to use this module.
+#
+# History:
+# 0.0 1995-09-01 fl Created
+# 0.1 1996-05-18 fl Modified options, added debugging mode
+# 0.2 1996-12-29 fl Added verify mode
+# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later)
+# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks
+#
+
+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"
+ sys.exit(1)
+
+try:
+ opt, args = getopt.getopt(sys.argv[1:], "fqivD")
+except getopt.error, v:
+ print v
+ sys.exit(1)
+
+verbose = quiet = verify = 0
+
+for o, a in opt:
+ if o == "-f":
+ Image.init()
+ id = Image.ID[:]
+ id.sort()
+ print "Supported formats:"
+ for i in id:
+ print i,
+ sys.exit(1)
+ elif o == "-i":
+ verbose = 1
+ elif o == "-q":
+ quiet = 1
+ elif o == "-v":
+ verify = 1
+ elif o == "-D":
+ Image.DEBUG = Image.DEBUG + 1
+
+def globfix(files):
+ # expand wildcards where necessary
+ if sys.platform == "win32":
+ out = []
+ for file in files:
+ if glob.has_magic(file):
+ out.extend(glob.glob(file))
+ else:
+ out.append(file)
+ return out
+ return files
+
+for file in globfix(args):
+ try:
+ im = Image.open(file)
+ print "%s:" % file, im.format, "%dx%d" % im.size, im.mode,
+ if verbose:
+ print im.info, im.tile,
+ 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:
+ if not quiet:
+ print file, "failed:", v
+ except:
+ import traceback
+ if not quiet:
+ print file, "failed:", "unexpected error"
+ traceback.print_exc(file=sys.stdout)
diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py
new file mode 100644
index 000000000..df08d4c08
--- /dev/null
+++ b/Scripts/pilfont.py
@@ -0,0 +1,54 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# PIL raster font compiler
+#
+# history:
+# 1997-08-25 fl created
+# 2002-03-10 fl use "from PIL import"
+#
+
+VERSION = "0.4"
+
+import site
+import glob, os, 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."
+ sys.exit(1)
+
+files = []
+for f in sys.argv[1:]:
+ files = files + glob.glob(f)
+
+for f in files:
+
+ print f + "...",
+
+ try:
+
+ fp = open(f, "rb")
+
+ try:
+ p = PcfFontFile.PcfFontFile(fp)
+ except SyntaxError:
+ fp.seek(0)
+ p = BdfFontFile.BdfFontFile(fp)
+
+ p.save(f)
+
+ except (SyntaxError, IOError):
+ print "failed"
+
+ else:
+ print "OK"
diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py
new file mode 100644
index 000000000..a98b39f7d
--- /dev/null
+++ b/Scripts/pilprint.py
@@ -0,0 +1,93 @@
+#! /usr/local/bin/python
+#
+# The Python Imaging Library.
+# $Id$
+#
+# print image files to postscript printer
+#
+# History:
+# 0.1 1996-04-20 fl Created
+# 0.2 1996-10-04 fl Use draft mode when converting.
+# 0.3 2003-05-06 fl Fixed a typo or two.
+#
+
+VERSION = "pilprint 0.3/2003-05-05"
+
+from PIL import Image
+from PIL import PSDraw
+
+letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 )
+
+def description(file, image):
+ import os
+ title = os.path.splitext(os.path.split(file)[1])[0]
+ format = " (%dx%d "
+ if image.format:
+ format = " (" + image.format + " %dx%d "
+ return title + format % image.size + image.mode + ")"
+
+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"
+ sys.exit(1)
+
+try:
+ opt, argv = getopt.getopt(sys.argv[1:], "cdpP:")
+except getopt.error, v:
+ print v
+ sys.exit(1)
+
+printer = None # print to stdout
+monochrome = 1 # reduce file size for most common case
+
+for o, a in opt:
+ if o == "-d":
+ # debug: show available drivers
+ Image.init()
+ print Image.ID
+ sys.exit(1)
+ elif o == "-c":
+ # colour printer
+ monochrome = 0
+ elif o == "-p":
+ # default printer channel
+ printer = "lpr"
+ elif o == "-P":
+ # printer channel
+ printer = "lpr -P%s" % a
+
+for file in argv:
+ try:
+
+ im = Image.open(file)
+
+ title = description(file, im)
+
+ if monochrome and im.mode not in ["1", "L"]:
+ im.draft("L", im.size)
+ im = im.convert("L")
+
+ if printer:
+ fp = os.popen(printer, "w")
+ else:
+ fp = sys.stdout
+
+ ps = PSDraw.PSDraw(fp)
+
+ ps.begin_document()
+ ps.setfont("Helvetica-Narrow-Bold", 18)
+ ps.text((letter[0], letter[3]+24), title)
+ ps.setfont("Helvetica-Narrow-Bold", 8)
+ ps.text((letter[0], letter[1]-30), VERSION)
+ ps.image(letter, im)
+ ps.end_document()
+
+ except:
+ print "cannot print image",
+ print "(%s:%s)" % (sys.exc_type, sys.exc_value)
diff --git a/Scripts/player.py b/Scripts/player.py
new file mode 100644
index 000000000..9ca4500bf
--- /dev/null
+++ b/Scripts/player.py
@@ -0,0 +1,121 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+
+from Tkinter import *
+from PIL import Image, ImageTk
+import sys
+
+
+Image.DEBUG = 0
+
+
+# --------------------------------------------------------------------
+# experimental: support ARG animation scripts
+
+import ArgImagePlugin
+
+def applet_hook(animation, images):
+ app = animation(animation_display, images)
+ app.run()
+
+ArgImagePlugin.APPLET_HOOK = applet_hook
+
+class AppletDisplay:
+ def __init__(self, ui):
+ self.__ui = ui
+ def paste(self, im, bbox):
+ self.__ui.image.paste(im, bbox)
+ def update(self):
+ self.__ui.update_idletasks()
+
+# --------------------------------------------------------------------
+# an image animation player
+
+class UI(Label):
+
+ def __init__(self, master, im):
+ if type(im) == type([]):
+ # list of images
+ self.im = im[1:]
+ im = self.im[0]
+ else:
+ # sequence
+ self.im = im
+
+ if im.mode == "1":
+ self.image = ImageTk.BitmapImage(im, foreground="white")
+ else:
+ self.image = ImageTk.PhotoImage(im)
+
+ # APPLET SUPPORT (very crude, and not 100% safe)
+ global animation_display
+ animation_display = AppletDisplay(self)
+
+ Label.__init__(self, master, image=self.image, bg="black", bd=0)
+
+ self.update()
+
+ try:
+ duration = im.info["duration"]
+ except KeyError:
+ duration = 100
+ self.after(duration, self.next)
+
+ def next(self):
+
+ if type(self.im) == type([]):
+
+ try:
+ im = self.im[0]
+ del self.im[0]
+ self.image.paste(im)
+ except IndexError:
+ return # end of list
+
+ else:
+
+ try:
+ im = self.im
+ im.seek(im.tell() + 1)
+ self.image.paste(im)
+ except EOFError:
+ return # end of file
+
+ try:
+ duration = im.info["duration"]
+ except KeyError:
+ duration = 100
+ self.after(duration, self.next)
+
+ self.update_idletasks()
+
+
+# --------------------------------------------------------------------
+# script interface
+
+if __name__ == "__main__":
+
+ if not sys.argv[1:]:
+ print "Syntax: python player.py imagefile(s)"
+ sys.exit(1)
+
+ filename = sys.argv[1]
+
+ root = Tk()
+ root.title(filename)
+
+ if len(sys.argv) > 2:
+ # list of images
+ print "loading..."
+ im = []
+ for filename in sys.argv[1:]:
+ im.append(Image.open(filename))
+ else:
+ # sequence
+ im = Image.open(filename)
+
+ UI(root, im).pack()
+
+ root.mainloop()
diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py
new file mode 100644
index 000000000..eb5109872
--- /dev/null
+++ b/Scripts/thresholder.py
@@ -0,0 +1,68 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+# this demo script illustrates how a 1-bit BitmapImage can be used
+# as a dynamically updated overlay
+#
+
+from Tkinter import *
+from PIL import Image, ImageTk
+import sys
+
+#
+# an image viewer
+
+class UI(Frame):
+ def __init__(self, master, im, value = 128):
+ Frame.__init__(self, master)
+
+ self.image = im
+ self.value = value
+
+ self.canvas = Canvas(self, width=im.size[0], height=im.size[1])
+ self.backdrop = ImageTk.PhotoImage(im)
+ self.canvas.create_image(0, 0, image=self.backdrop, anchor=NW)
+ self.canvas.pack()
+
+ scale = Scale(self, orient=HORIZONTAL, from_=0, to=255,
+ resolution=1, command=self.update, length=256)
+ scale.set(value)
+ scale.bind("", self.redraw)
+ scale.pack()
+
+ # uncomment the following line for instant feedback (might
+ # be too slow on some platforms)
+ # self.redraw()
+
+ def update(self, value):
+ self.value = eval(value)
+
+ self.redraw()
+
+ def redraw(self, event = None):
+
+ # create overlay (note the explicit conversion to mode "1")
+ im = self.image.point(lambda v,t=self.value: v>=t, "1")
+ self.overlay = ImageTk.BitmapImage(im, foreground="green")
+
+ # update canvas
+ self.canvas.delete("overlay")
+ self.canvas.create_image(0, 0, image=self.overlay, anchor=NW,
+ tags="overlay")
+
+# --------------------------------------------------------------------
+# main
+
+root = Tk()
+
+im = Image.open(sys.argv[1])
+
+if im.mode != "L":
+ im = im.convert("L")
+
+# im.thumbnail((320,200))
+
+UI(root, im).pack()
+
+root.mainloop()
diff --git a/Scripts/viewer.py b/Scripts/viewer.py
new file mode 100644
index 000000000..6e4dc8b67
--- /dev/null
+++ b/Scripts/viewer.py
@@ -0,0 +1,46 @@
+#
+# The Python Imaging Library
+# $Id$
+#
+
+from Tkinter import *
+from PIL import Image, ImageTk
+
+#
+# an image viewer
+
+class UI(Label):
+
+ def __init__(self, master, im):
+
+ if im.mode == "1":
+ # bitmap image
+ self.image = ImageTk.BitmapImage(im, foreground="white")
+ Label.__init__(self, master, image=self.image, bg="black", bd=0)
+
+ else:
+ # photo image
+ self.image = ImageTk.PhotoImage(im)
+ Label.__init__(self, master, image=self.image, bd=0)
+
+#
+# script interface
+
+if __name__ == "__main__":
+
+ import sys
+
+ if not sys.argv[1:]:
+ print "Syntax: python viewer.py imagefile"
+ sys.exit(1)
+
+ filename = sys.argv[1]
+
+ root = Tk()
+ root.title(filename)
+
+ im = Image.open(filename)
+
+ UI(root, im).pack()
+
+ root.mainloop()
diff --git a/Tk/booster.txt b/Tk/booster.txt
new file mode 100644
index 000000000..2d787983b
--- /dev/null
+++ b/Tk/booster.txt
@@ -0,0 +1,108 @@
+====================================================================
+The Photoimage Booster Patch (for Windows 95/NT)
+====================================================================
+
+ This patch kit boosts performance for 16/24-bit displays. The
+first patch is required on Tk 4.2 (where it fixes the problems for
+16-bit displays) and later versions, with the exception for Tk 8.0b1
+where Sun added something similar themselves, only to remove it in
+8.0b2. By installing both patches, Tk's PhotoImage handling becomes
+much faster on both 16-bit and 24-bit displays. The patch has been
+tested with Tk 4.2 and 8.0.
+
+ Here's a benchmark, made with a sample program which loads two
+512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays
+each of them in a separate toplevel windows. Tcl/Tk was compiled
+with Visual C 4.0, and run on a P100 under Win95. Image load times
+are not included in the timings:
+
+ 8-bit 16-bit 24-bit
+--------------------------------------------------------------------
+1. original 4.2 code 5.52 s 8.57 s 3.79 s
+2. booster patch 5.49 s 1.87 s 1.82 s
+
+ speedup None 4.6x 2.1x
+
+====================================================================
+
+Here's the patches:
+
+1. For portability and speed, the best thing under Windows is to
+treat 16-bit displays as if they were 24-bit. The Windows device
+drivers take care of the rest.
+
+ ----------------------------------------------------------------
+ If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this
+ patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2.
+ ----------------------------------------------------------------
+
+In win/tkWinImage.c, change the following line in XCreateImage:
+
+ imagePtr->bits_per_pixel = depth;
+
+to
+
+/* ==================================================================== */
+/* The tk photo image booster patch -- patch section 1 */
+/* ==================================================================== */
+
+ if (visual->class == TrueColor)
+ /* true colour is stored as 3 bytes: (blue, green, red) */
+ imagePtr->bits_per_pixel = 24;
+ else
+ imagePtr->bits_per_pixel = depth;
+
+/* ==================================================================== */
+
+
+2. The DitherInstance implementation is not good. It's especially
+bad on highend truecolour displays. IMO, it should be rewritten from
+scratch (some other day...).
+
+ Anyway, the following band-aid makes the situation a little bit
+better under Windows. This hack trades some marginal quality (no
+dithering on 16-bit displays) for a dramatic performance boost.
+Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1.
+
+In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance
+function:
+
+ for (; height > 0; height -= nLines) {
+ if (nLines > height) {
+ nLines = height;
+ }
+ dstLinePtr = (unsigned char *) imagePtr->data;
+ yEnd = yStart + nLines;
+
+/* ==================================================================== */
+/* The tk photo image booster patch -- patch section 2 */
+/* ==================================================================== */
+
+#ifdef __WIN32__
+ if (colorPtr->visualInfo.class == TrueColor
+ && instancePtr->gamma == 1.0) {
+ /* Windows hicolor/truecolor booster */
+ for (y = yStart; y < yEnd; ++y) {
+ destBytePtr = dstLinePtr;
+ srcPtr = srcLinePtr;
+ for (x = xStart; x < xEnd; ++x) {
+ destBytePtr[0] = srcPtr[2];
+ destBytePtr[1] = srcPtr[1];
+ destBytePtr[2] = srcPtr[0];
+ destBytePtr += 3; srcPtr += 3;
+ }
+ srcLinePtr += lineLength;
+ dstLinePtr += bytesPerLine;
+ }
+ } else
+#endif
+
+/* ==================================================================== */
+
+ for (y = yStart; y < yEnd; ++y) {
+ srcPtr = srcLinePtr;
+ errPtr = errLinePtr;
+ destBytePtr = dstLinePtr;
+
+====================================================================
+last updated: 97-07-03/fl
diff --git a/Tk/install.txt b/Tk/install.txt
new file mode 100644
index 000000000..e24d36301
--- /dev/null
+++ b/Tk/install.txt
@@ -0,0 +1,41 @@
+====================================================================
+Using PIL With Tkinter
+====================================================================
+
+Starting with 1.0 final (release candidate 2 and later, to be
+precise), PIL can attach itself to Tkinter in flight. As a result,
+you no longer need to rebuild the Tkinter extension to be able to
+use PIL.
+
+However, if you cannot get the this to work on your platform, you
+can do it in the old way:
+
+* Adding Tkinter support
+
+1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL
+ flags set, and link it with tkImaging.c and tkappinit.c. To
+ do this, copy the former to the Modules directory, and edit
+ the _tkinter line in Setup (or Setup.in) according to the
+ instructions in that file.
+
+ NOTE: if you have an old Python version, the tkappinit.c
+ file is not included by default. If this is the case, you
+ will have to add the following lines to tkappinit.c, after
+ the MOREBUTTONS stuff:
+
+ {
+ extern void TkImaging_Init(Tcl_Interp* interp);
+ TkImaging_Init(interp);
+ }
+
+ This registers a Tcl command called "PyImagingPhoto", which is
+ use to communicate between PIL and Tk's PhotoImage handler.
+
+ You must also change the _tkinter line in Setup (or Setup.in)
+ to something like:
+
+ _tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT
+ -I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11
+
+
+
diff --git a/Tk/pilbitmap.txt b/Tk/pilbitmap.txt
new file mode 100644
index 000000000..e7c46c65f
--- /dev/null
+++ b/Tk/pilbitmap.txt
@@ -0,0 +1,149 @@
+====================================================================
+The PIL Bitmap Booster Patch
+====================================================================
+
+The pilbitmap booster patch greatly improves performance of the
+ImageTk.BitmapImage constructor. Unfortunately, the design of Tk
+doesn't allow us to do this from the tkImaging interface module, so
+you have to patch the Tk sources.
+
+Once installed, the ImageTk module will automatically detect this
+patch.
+
+(Note: this patch has been tested with Tk 8.0 on Win32 only, but it
+should work just fine on other platforms as well).
+
+1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add
+ the following stuff:
+
+------------------------------------------------------------------------
+ int width, height, numBytes, hotX, hotY;
+ char *p, *end, *expandedFileName;
+ ParseInfo pi;
+ char *data = NULL;
+ Tcl_DString buffer;
+
+/* ==================================================================== */
+/* The pilbitmap booster patch -- patch section */
+/* ==================================================================== */
+
+ char *PILGetBitmapData();
+
+ if (string) {
+ /* Is this a PIL bitmap reference? */
+ data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr);
+ if (data)
+ return data;
+ }
+
+/* ==================================================================== */
+
+ pi.string = string;
+ if (string == NULL) {
+ if (Tcl_IsSafe(interp)) {
+------------------------------------------------------------------------
+
+
+2. Append the following to the same file (you may wish to include
+Imaging.h instead of copying the struct declaration...)
+
+------------------------------------------------------------------------
+
+/* ==================================================================== */
+/* The pilbitmap booster patch -- code section */
+/* ==================================================================== */
+
+/* Imaging declaration boldly copied from Imaging.h (!) */
+
+typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */
+
+typedef unsigned char UINT8;
+typedef int INT32;
+
+struct ImagingInstance {
+
+ /* Format */
+ char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */
+ int type; /* Always 0 in this version */
+ int depth; /* Always 8 in this version */
+ int bands; /* Number of bands (1, 3, or 4) */
+ int xsize; /* Image dimension. */
+ int ysize;
+
+ /* Colour palette (for "P" images only) */
+ void* palette;
+
+ /* Data pointers */
+ UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */
+ INT32 **image32; /* Set for 32-bit image (pixelsize=4). */
+
+ /* Internals */
+ char **image; /* Actual raster data. */
+ char *block; /* Set if data is allocated in a single block. */
+
+ int pixelsize; /* Size of a pixel, in bytes (1 or 4) */
+ int linesize; /* Size of a line, in bytes (xsize * pixelsize) */
+
+ /* Virtual methods */
+ void (*im_delete)(Imaging *);
+
+};
+
+/* The pilbitmap booster patch allows you to pass PIL images to the
+ Tk bitmap decoder. Passing images this way is much more efficient
+ than using the "tobitmap" method. */
+
+char *
+PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr)
+ char *string;
+ int *widthPtr, *heightPtr;
+ int *hotXPtr, *hotYPtr;
+{
+ char* data;
+ char* p;
+ int y;
+ Imaging im;
+
+ if (strncmp(string, "PIL:", 4) != 0)
+ return NULL;
+
+ im = (Imaging) atol(string + 4);
+
+ if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0)
+ return NULL;
+
+ data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize);
+
+ for (y = 0; y < im->ysize; y++) {
+ char* in = im->image8[y];
+ int i, m, b;
+ b = 0; m = 1;
+ for (i = 0; i < im->xsize; i++) {
+ if (in[i] != 0)
+ b |= m;
+ m <<= 1;
+ if (m == 256){
+ *p++ = b;
+ b = 0; m = 1;
+ }
+ }
+ if (m != 1)
+ *p++ = b;
+ }
+
+ *widthPtr = im->xsize;
+ *heightPtr = im->ysize;
+ *hotXPtr = -1;
+ *hotYPtr = -1;
+
+ return data;
+}
+
+/* ==================================================================== */
+
+------------------------------------------------------------------------
+
+3. Recompile Tk and relink the _tkinter module (where necessary).
+
+====================================================================
+Last updated: 97-05-17/fl
diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c
new file mode 100644
index 000000000..5963ee24c
--- /dev/null
+++ b/Tk/tkImaging.c
@@ -0,0 +1,265 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * TK interface for Python Imaging objects
+ *
+ * Copies (parts of) a named display memory to a photo image object.
+ * Also contains code to create an display memory. Under Tk, a
+ * display memory is simply an "L" or "RGB" image memory that is
+ * allocated in a single block.
+ *
+ * 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);
+ * }
+ *
+ * This registers a Tcl command called "PyImagingPhoto", which is used
+ * to communicate between PIL and Tk's PhotoImage handler.
+ *
+ * Compile and link tkImaging.c with tkappinit.c and _tkinter (see the
+ * Setup file for details on how to use tkappinit.c). Note that
+ * _tkinter.c must be compiled with WITH_APPINIT.
+ *
+ * History:
+ * 1995-09-12 fl Created
+ * 1996-04-08 fl Ready for release
+ * 1997-05-09 fl Use command instead of image type
+ * 2001-03-18 fl Initialize alpha layer pointer (struct changed in 8.3)
+ * 2003-04-23 fl Fixed building for Tk 8.4.1 and later (Jack Jansen)
+ * 2004-06-24 fl Fixed building for Tk 8.4.6 and later.
+ *
+ * Copyright (c) 1997-2004 by Secret Labs AB
+ * Copyright (c) 1995-2004 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#define TK (TK_MAJOR_VERSION*10 + TK_MINOR_VERSION)
+
+/* This is needed for (at least) Tk 8.4.6 and later, to avoid warnings
+ for the Tcl_CreateCommand command. */
+#define USE_COMPAT_CONST
+
+#include "tk.h"
+
+#include "Imaging.h"
+
+#include
+
+
+static Imaging
+ImagingFind(const char* name)
+{
+ long id;
+
+ /* FIXME: use CObject instead? */
+ id = atol(name);
+ if (!id)
+ return NULL;
+
+ return (Imaging) id;
+}
+
+
+static int
+PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp,
+ int argc, char **argv)
+{
+ Imaging im;
+ Tk_PhotoHandle photo;
+ Tk_PhotoImageBlock block;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "usage: ", argv[0],
+ " destPhoto srcImage", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /* get Tcl PhotoImage handle */
+ photo = Tk_FindPhoto(interp, argv[1]);
+ if (photo == NULL) {
+ Tcl_AppendResult(
+ interp, "destination photo must exist", (char *) NULL
+ );
+ return TCL_ERROR;
+ }
+
+ /* get PIL Image handle */
+ im = ImagingFind(argv[2]);
+ if (!im) {
+ Tcl_AppendResult(interp, "bad name", (char*) NULL);
+ return TCL_ERROR;
+ }
+ if (!im->block) {
+ Tcl_AppendResult(interp, "bad display memory", (char*) NULL);
+ return TCL_ERROR;
+ }
+
+ /* Active region */
+#if 0
+ if (src_xoffset + xsize > im->xsize)
+ xsize = im->xsize - src_xoffset;
+ if (src_yoffset + ysize > im->ysize)
+ ysize = im->ysize - src_yoffset;
+ if (xsize < 0 || ysize < 0
+ || src_xoffset >= im->xsize
+ || src_yoffset >= im->ysize)
+ return TCL_OK;
+#endif
+
+ /* Mode */
+
+ if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+ block.pixelSize = 1;
+ block.offset[0] = block.offset[1] = block.offset[2] = 0;
+ } else if (strncmp(im->mode, "RGB", 3) == 0) {
+ block.pixelSize = 4;
+ block.offset[0] = 0;
+ block.offset[1] = 1;
+ block.offset[2] = 2;
+ if (strcmp(im->mode, "RGBA") == 0)
+ block.offset[3] = 3; /* alpha (or reserved, under 8.2) */
+ else
+ block.offset[3] = 0; /* no alpha */
+ } else {
+ Tcl_AppendResult(interp, "Bad mode", (char*) NULL);
+ return TCL_ERROR;
+ }
+
+ block.width = im->xsize;
+ block.height = im->ysize;
+ block.pitch = im->linesize;
+ block.pixelPtr = (unsigned char*) im->block;
+#if 0
+ block.pixelPtr = (unsigned char*) im->block +
+ src_yoffset * im->linesize +
+ src_xoffset * im->pixelsize;
+#endif
+
+#if TK < 84 /* < 8.4.0 */
+ if (strcmp(im->mode, "RGBA") == 0) {
+ /* Copy non-transparent pixels to photo image */
+ int x, y;
+ Tk_PhotoImageBlock run;
+
+ /* Clear current contents */
+ Tk_PhotoBlank(photo);
+
+ /* Setup run descriptor */
+ run.height = 1;
+ run.pitch = block.pitch;
+ run.pixelSize = block.pixelSize;
+ run.offset[0] = 0;
+ run.offset[1] = 1;
+ run.offset[2] = 2;
+ run.offset[3] = 0; /* no alpha (or reserved, under 8.2) */
+
+ /* Copy opaque runs to photo image */
+ for (y = 0; y < block.height; y++) {
+ unsigned char* p = block.pixelPtr + y*block.pitch;
+ unsigned char* s = p;
+ int w = 0;
+ for (x = 0; x < block.width; x++) {
+ if (p[3]) {
+ /* opaque: add pixel to current run */
+ if (w == 0)
+ s = p;
+ w = w + 1;
+ } else if (s) {
+ /* copy run to photo image */
+ if (w > 0) {
+ run.width = w;
+ run.pixelPtr = s;
+ Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1);
+ }
+ w = 0;
+ }
+ p += block.pixelSize;
+ }
+ if (w > 0) {
+ /* copy final run, if any */
+ run.width = w;
+ run.pixelPtr = s;
+ Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1);
+ }
+ }
+
+ } else
+
+ /* Copy opaque block to photo image, and leave the rest to TK */
+ Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height);
+
+#else /* Tk 8.4 and newer */
+#if TK < 85 /* Tk 8.4 */
+ Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height,
+ TK_PHOTO_COMPOSITE_SET);
+ if (strcmp(im->mode, "RGBA") == 0)
+ /* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */
+ /* (fixed in Tk 8.5a3) */
+ Tk_PhotoSetSize(photo, block.width, block.height);
+#else /* Tk 8.5 */
+ Tk_PhotoPutBlock(interp, photo, &block, 0, 0, block.width, block.height,
+ TK_PHOTO_COMPOSITE_SET);
+#endif
+#endif
+
+ return TCL_OK;
+}
+
+
+static int
+PyImagingPhotoGet(ClientData clientdata, Tcl_Interp* interp,
+ int argc, char **argv)
+{
+ Tk_PhotoHandle photo;
+ Tk_PhotoImageBlock block;
+
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "usage: ", argv[0],
+ " srcPhoto", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /* get Tcl PhotoImage handle */
+ photo = Tk_FindPhoto(interp, argv[1]);
+ if (photo == NULL) {
+ Tcl_AppendResult(
+ interp, "source photo must exist", (char *) NULL
+ );
+ return TCL_ERROR;
+ }
+
+ Tk_PhotoGetImage(photo, &block);
+
+ printf("pixelPtr = %p\n", block.pixelPtr);
+ printf("width = %d\n", block.width);
+ printf("height = %d\n", block.height);
+ printf("pitch = %d\n", block.pitch);
+ printf("pixelSize = %d\n", block.pixelSize);
+ printf("offset = %d %d %d %d\n", block.offset[0], block.offset[1],
+ block.offset[2], block.offset[3]);
+
+ Tcl_AppendResult(
+ interp, "this function is not yet supported", (char *) NULL
+ );
+
+ return TCL_ERROR;
+}
+
+
+void
+TkImaging_Init(Tcl_Interp* interp)
+{
+ Tcl_CreateCommand(interp, "PyImagingPhoto", PyImagingPhotoPut,
+ (ClientData) 0, (Tcl_CmdDeleteProc*) NULL);
+ Tcl_CreateCommand(interp, "PyImagingPhotoGet", PyImagingPhotoGet,
+ (ClientData) 0, (Tcl_CmdDeleteProc*) NULL);
+}
diff --git a/_imaging.c b/_imaging.c
new file mode 100644
index 000000000..c21897dec
--- /dev/null
+++ b/_imaging.c
@@ -0,0 +1,3281 @@
+/*
+ * The Python Imaging Library.
+ *
+ * the imaging library bindings
+ *
+ * history:
+ * 1995-09-24 fl Created
+ * 1996-03-24 fl Ready for first public release (release 0.0)
+ * 1996-03-25 fl Added fromstring (for Jack's "img" library)
+ * 1996-03-28 fl Added channel operations
+ * 1996-03-31 fl Added point operation
+ * 1996-04-08 fl Added new/new_block/new_array factories
+ * 1996-04-13 fl Added decoders
+ * 1996-05-04 fl Added palette hack
+ * 1996-05-12 fl Compile cleanly as C++
+ * 1996-05-19 fl Added matrix conversions, gradient fills
+ * 1996-05-27 fl Added display_mode
+ * 1996-07-22 fl Added getbbox, offset
+ * 1996-07-23 fl Added sequence semantics
+ * 1996-08-13 fl Added logical operators, point mode
+ * 1996-08-16 fl Modified paste interface
+ * 1996-09-06 fl Added putdata methods, use abstract interface
+ * 1996-11-01 fl Added xbm encoder
+ * 1996-11-04 fl Added experimental path stuff, draw_lines, etc
+ * 1996-12-10 fl Added zip decoder, crc32 interface
+ * 1996-12-14 fl Added modulo arithmetics
+ * 1996-12-29 fl Added zip encoder
+ * 1997-01-03 fl Added fli and msp decoders
+ * 1997-01-04 fl Added experimental sun_rle and tga_rle decoders
+ * 1997-01-05 fl Added gif encoder, getpalette hack
+ * 1997-02-23 fl Added histogram mask
+ * 1997-05-12 fl Minor tweaks to match the IFUNC95 interface
+ * 1997-05-21 fl Added noise generator, spread effect
+ * 1997-06-05 fl Added mandelbrot generator
+ * 1997-08-02 fl Modified putpalette to coerce image mode if necessary
+ * 1998-01-11 fl Added INT32 support
+ * 1998-01-22 fl Fixed draw_points to draw the last point too
+ * 1998-06-28 fl Added getpixel, getink, draw_ink
+ * 1998-07-12 fl Added getextrema
+ * 1998-07-17 fl Added point conversion to arbitrary formats
+ * 1998-09-21 fl Added support for resampling filters
+ * 1998-09-22 fl Added support for quad transform
+ * 1998-12-29 fl Added support for arcs, chords, and pieslices
+ * 1999-01-10 fl Added some experimental arrow graphics stuff
+ * 1999-02-06 fl Added draw_bitmap, font acceleration stuff
+ * 2001-04-17 fl Fixed some egcs compiler nits
+ * 2001-09-17 fl Added screen grab primitives (win32)
+ * 2002-03-09 fl Added stretch primitive
+ * 2002-03-10 fl Fixed filter handling in rotate
+ * 2002-06-06 fl Added I, F, and RGB support to putdata
+ * 2002-06-08 fl Added rankfilter
+ * 2002-06-09 fl Added support for user-defined filter kernels
+ * 2002-11-19 fl Added clipboard grab primitives (win32)
+ * 2002-12-11 fl Added draw context
+ * 2003-04-26 fl Tweaks for Python 2.3 beta 1
+ * 2003-05-21 fl Added createwindow primitive (win32)
+ * 2003-09-13 fl Added thread section hooks
+ * 2003-09-15 fl Added expand helper
+ * 2003-09-26 fl Added experimental LA support
+ * 2004-02-21 fl Handle zero-size images in quantize
+ * 2004-06-05 fl Added ptr attribute (used to access Imaging objects)
+ * 2004-06-05 fl Don't crash when fetching pixels from zero-wide images
+ * 2004-09-17 fl Added getcolors
+ * 2004-10-04 fl Added modefilter
+ * 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) 1995-2006 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Python.h"
+
+#include "Imaging.h"
+
+
+/* Configuration stuff. Feel free to undef things you don't need. */
+#define WITH_IMAGECHOPS /* ImageChops support */
+#define WITH_IMAGEDRAW /* ImageDraw support */
+#define WITH_MAPPING /* use memory mapping to read some file formats */
+#define WITH_IMAGEPATH /* ImagePath stuff */
+#define WITH_ARROW /* arrow graphics stuff (experimental) */
+#define WITH_EFFECTS /* special effects */
+#define WITH_QUANTIZE /* quantization support */
+#define WITH_RANKFILTER /* rank filter */
+#define WITH_MODEFILTER /* mode filter */
+#define WITH_THREADING /* "friendly" threading support */
+#define WITH_UNSHARPMASK /* Kevin Cazabon's unsharpmask module */
+
+#define WITH_DEBUG /* extra debugging interfaces */
+
+/* PIL Plus extensions */
+#undef WITH_CRACKCODE /* pil plus */
+
+#undef VERBOSE
+
+#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
+
+#define B16(p, i) ((((int)p[(i)]) << 8) + p[(i)+1])
+#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 */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ Imaging image;
+ ImagingAccess access;
+} ImagingObject;
+
+staticforward PyTypeObject Imaging_Type;
+
+#ifdef WITH_IMAGEDRAW
+
+typedef struct
+{
+ /* to write a character, cut out sxy from glyph data, place
+ at current position plus dxy, and advance by (dx, dy) */
+ int dx, dy;
+ int dx0, dy0, dx1, dy1;
+ int sx0, sy0, sx1, sy1;
+} Glyph;
+
+typedef struct {
+ PyObject_HEAD
+ ImagingObject* ref;
+ Imaging bitmap;
+ int ysize;
+ int baseline;
+ Glyph glyphs[256];
+} ImagingFontObject;
+
+staticforward PyTypeObject ImagingFont_Type;
+
+typedef struct {
+ PyObject_HEAD
+ ImagingObject* image;
+ UINT8 ink[4];
+ int blend;
+} ImagingDrawObject;
+
+staticforward PyTypeObject ImagingDraw_Type;
+
+#endif
+
+typedef struct {
+ PyObject_HEAD
+ ImagingObject* image;
+ int readonly;
+} PixelAccessObject;
+
+staticforward PyTypeObject PixelAccess_Type;
+
+PyObject*
+PyImagingNew(Imaging imOut)
+{
+ ImagingObject* imagep;
+
+ if (!imOut)
+ return NULL;
+
+ imagep = PyObject_New(ImagingObject, &Imaging_Type);
+ if (imagep == NULL) {
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+#ifdef VERBOSE
+ printf("imaging %p allocated\n", imagep);
+#endif
+
+ imagep->image = imOut;
+ imagep->access = ImagingAccessNew(imOut);
+
+ return (PyObject*) imagep;
+}
+
+static void
+_dealloc(ImagingObject* imagep)
+{
+
+#ifdef VERBOSE
+ printf("imaging %p deleted\n", imagep);
+#endif
+
+ if (imagep->access)
+ ImagingAccessDelete(imagep->image, imagep->access);
+ ImagingDelete(imagep->image);
+ PyObject_Del(imagep);
+}
+
+#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type)
+
+Imaging PyImaging_AsImaging(PyObject *op)
+{
+ if (!PyImaging_Check(op)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ return ((ImagingObject *)op)->image;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* THREAD HANDLING */
+/* -------------------------------------------------------------------- */
+
+void ImagingSectionEnter(ImagingSectionCookie* cookie)
+{
+#ifdef WITH_THREADING
+ *cookie = (PyThreadState *) PyEval_SaveThread();
+#endif
+}
+
+void ImagingSectionLeave(ImagingSectionCookie* cookie)
+{
+#ifdef WITH_THREADING
+ PyEval_RestoreThread((PyThreadState*) *cookie);
+#endif
+}
+
+/* -------------------------------------------------------------------- */
+/* BUFFER HANDLING */
+/* -------------------------------------------------------------------- */
+/* 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);
+}
+
+int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr)
+{
+ /* must call check_buffer first! */
+#if PY_VERSION_HEX < 0x02050000
+ int n = 0;
+#else
+ Py_ssize_t n = 0;
+#endif
+ PyObject_AsReadBuffer(buffer, ptr, &n);
+ return (int) n;
+}
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/* EXCEPTION REROUTING */
+/* -------------------------------------------------------------------- */
+
+/* error messages */
+static const char* must_be_sequence = "argument must be a sequence";
+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* no_palette = "image has no palette";
+static const char* readonly = "image is readonly";
+/* static const char* no_content = "image has no content"; */
+
+void *
+ImagingError_IOError(void)
+{
+ PyErr_SetString(PyExc_IOError, "error when accessing file");
+ return NULL;
+}
+
+void *
+ImagingError_MemoryError(void)
+{
+ return PyErr_NoMemory();
+}
+
+void *
+ImagingError_Mismatch(void)
+{
+ PyErr_SetString(PyExc_ValueError, "images do not match");
+ return NULL;
+}
+
+void *
+ImagingError_ModeError(void)
+{
+ PyErr_SetString(PyExc_ValueError, "image has wrong mode");
+ return NULL;
+}
+
+void *
+ImagingError_ValueError(const char *message)
+{
+ PyErr_SetString(
+ PyExc_ValueError,
+ (message) ? (char*) message : "unrecognized argument value"
+ );
+ return NULL;
+}
+
+void
+ImagingError_Clear(void)
+{
+ PyErr_Clear();
+}
+
+/* -------------------------------------------------------------------- */
+/* HELPERS */
+/* -------------------------------------------------------------------- */
+
+static int
+getbands(const char* mode)
+{
+ Imaging im;
+ int bands;
+
+ /* FIXME: add primitive to libImaging to avoid extra allocation */
+ im = ImagingNew(mode, 0, 0);
+ if (!im)
+ return -1;
+
+ bands = im->bands;
+
+ ImagingDelete(im);
+
+ return bands;
+}
+
+#define TYPE_UINT8 (0x100|sizeof(UINT8))
+#define TYPE_INT32 (0x200|sizeof(INT32))
+#define TYPE_FLOAT32 (0x300|sizeof(FLOAT32))
+#define TYPE_DOUBLE (0x400|sizeof(double))
+
+static void*
+getlist(PyObject* arg, int* length, const char* wrong_length, int type)
+{
+ int i, n;
+ void* list;
+
+ if (!PySequence_Check(arg)) {
+ 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;
+ }
+
+ list = malloc(n * (type & 0xff));
+ if (!list)
+ return PyErr_NoMemory();
+
+ switch (type) {
+ case TYPE_UINT8:
+ if (PyList_Check(arg)) {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(arg, i);
+ int temp = PyInt_AsLong(op);
+ ((UINT8*)list)[i] = CLIP(temp);
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(arg, i);
+ int temp = PyInt_AsLong(op);
+ Py_XDECREF(op);
+ ((UINT8*)list)[i] = CLIP(temp);
+ }
+ }
+ break;
+ case TYPE_INT32:
+ if (PyList_Check(arg)) {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(arg, i);
+ int temp = PyInt_AsLong(op);
+ ((INT32*)list)[i] = temp;
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(arg, i);
+ int temp = PyInt_AsLong(op);
+ Py_XDECREF(op);
+ ((INT32*)list)[i] = temp;
+ }
+ }
+ break;
+ case TYPE_FLOAT32:
+ if (PyList_Check(arg)) {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(arg, i);
+ double temp = PyFloat_AsDouble(op);
+ ((FLOAT32*)list)[i] = (FLOAT32) temp;
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(arg, i);
+ double temp = PyFloat_AsDouble(op);
+ Py_XDECREF(op);
+ ((FLOAT32*)list)[i] = (FLOAT32) temp;
+ }
+ }
+ break;
+ case TYPE_DOUBLE:
+ if (PyList_Check(arg)) {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(arg, i);
+ double temp = PyFloat_AsDouble(op);
+ ((double*)list)[i] = temp;
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(arg, i);
+ double temp = PyFloat_AsDouble(op);
+ Py_XDECREF(op);
+ ((double*)list)[i] = temp;
+ }
+ }
+ break;
+ }
+
+ if (length)
+ *length = n;
+
+ PyErr_Clear();
+
+ return list;
+}
+
+static inline PyObject*
+getpixel(Imaging im, ImagingAccess access, int x, int y)
+{
+ union {
+ UINT8 b[4];
+ INT16 h;
+ INT32 i;
+ FLOAT32 f;
+ } pixel;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ 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;
+ case IMAGING_TYPE_INT32:
+ return PyInt_FromLong(pixel.i);
+ case IMAGING_TYPE_FLOAT32:
+ return PyFloat_FromDouble(pixel.f);
+ case IMAGING_TYPE_SPECIAL:
+ if (strncmp(im->mode, "I;16", 4) == 0)
+ return PyInt_FromLong(pixel.h);
+ break;
+ }
+
+ /* unknown type */
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static char*
+getink(PyObject* color, Imaging im, char* ink)
+{
+ int r, g, b, a;
+ double f;
+
+ /* fill ink buffer (four bytes) with something that can
+ be cast to either UINT8 or INT32 */
+
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ /* unsigned integer */
+ if (im->bands == 1) {
+ /* unsigned integer, single layer */
+ r = PyInt_AsLong(color);
+ if (r == -1 && PyErr_Occurred())
+ return NULL;
+ ink[0] = CLIP(r);
+ ink[1] = ink[2] = ink[3] = 0;
+ } else {
+ a = 255;
+ if (PyInt_Check(color)) {
+ r = PyInt_AS_LONG(color);
+ /* compatibility: ABGR */
+ a = (UINT8) (r >> 24);
+ b = (UINT8) (r >> 16);
+ g = (UINT8) (r >> 8);
+ r = (UINT8) r;
+ } else {
+ if (im->bands == 2) {
+ if (!PyArg_ParseTuple(color, "i|i", &r, &a))
+ return NULL;
+ g = b = r;
+ } else {
+ if (!PyArg_ParseTuple(color, "iii|i", &r, &g, &b, &a))
+ return NULL;
+ }
+ }
+ ink[0] = CLIP(r);
+ ink[1] = CLIP(g);
+ ink[2] = CLIP(b);
+ ink[3] = CLIP(a);
+ }
+ return ink;
+ case IMAGING_TYPE_INT32:
+ /* signed integer */
+ r = PyInt_AsLong(color);
+ if (r == -1 && PyErr_Occurred())
+ return NULL;
+ *(INT32*) ink = r;
+ return ink;
+ case IMAGING_TYPE_FLOAT32:
+ /* floating point */
+ f = PyFloat_AsDouble(color);
+ if (f == -1.0 && PyErr_Occurred())
+ return NULL;
+ *(FLOAT32*) ink = (FLOAT32) f;
+ return ink;
+ case IMAGING_TYPE_SPECIAL:
+ if (strncmp(im->mode, "I;16", 4) == 0) {
+ r = PyInt_AsLong(color);
+ if (r == -1 && PyErr_Occurred())
+ return NULL;
+ ink[0] = (UINT8) r;
+ ink[1] = (UINT8) (r >> 8);
+ ink[2] = ink[3] = 0;
+ return ink;
+ }
+ }
+
+ PyErr_SetString(PyExc_ValueError, wrong_mode);
+ return NULL;
+}
+
+/* -------------------------------------------------------------------- */
+/* FACTORIES */
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_fill(PyObject* self, PyObject* args)
+{
+ char* mode;
+ int xsize, ysize;
+ 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;
+
+ im = ImagingNew(mode, xsize, ysize);
+ if (!im)
+ return NULL;
+
+ if (color) {
+ if (!getink(color, im, buffer)) {
+ ImagingDelete(im);
+ return NULL;
+ }
+ } else
+ buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0;
+
+ (void) ImagingFill(im, buffer);
+
+ return PyImagingNew(im);
+}
+
+static PyObject*
+_new(PyObject* self, PyObject* args)
+{
+ char* mode;
+ int xsize, ysize;
+
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
+ return NULL;
+
+ return PyImagingNew(ImagingNew(mode, xsize, ysize));
+}
+
+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 PyImagingNew(ImagingNewArray(mode, xsize, ysize));
+}
+
+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 PyImagingNew(ImagingNewBlock(mode, xsize, ysize));
+}
+
+static PyObject*
+_getcount(PyObject* self, PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ":getcount"))
+ return NULL;
+
+ return PyInt_FromLong(ImagingNewCount);
+}
+
+static PyObject*
+_linear_gradient(PyObject* self, PyObject* args)
+{
+ char* mode;
+
+ if (!PyArg_ParseTuple(args, "s", &mode))
+ return NULL;
+
+ return PyImagingNew(ImagingFillLinearGradient(mode));
+}
+
+static PyObject*
+_radial_gradient(PyObject* self, PyObject* args)
+{
+ char* mode;
+
+ if (!PyArg_ParseTuple(args, "s", &mode))
+ return NULL;
+
+ return PyImagingNew(ImagingFillRadialGradient(mode));
+}
+
+static PyObject*
+_open_ppm(PyObject* self, PyObject* args)
+{
+ char* filename;
+
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ return PyImagingNew(ImagingOpenPPM(filename));
+}
+
+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;
+
+ return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image,
+ (float) alpha));
+}
+
+/* -------------------------------------------------------------------- */
+/* METHODS */
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_convert(ImagingObject* self, PyObject* args)
+{
+ char* mode;
+ int dither = 0;
+ ImagingObject *paletteimage = NULL;
+
+ if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage))
+ 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;
+ }
+ }
+
+ return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither));
+}
+
+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;
+
+ if (!ImagingConvert2(imagep1->image, imagep2->image))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+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;
+ }
+
+ return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
+}
+
+static PyObject*
+_copy(ImagingObject* self, PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ""))
+ return NULL;
+
+ return PyImagingNew(ImagingCopy(self->image));
+}
+
+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;
+
+ if (!ImagingCopy2(imagep1->image, imagep2->image))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_crop(ImagingObject* self, PyObject* args)
+{
+ int x0, y0, x1, y1;
+ if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1))
+ return NULL;
+
+ return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1));
+}
+
+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 PyImagingNew(ImagingExpand(self->image, x, y, mode));
+}
+
+static PyObject*
+_filter(ImagingObject* self, PyObject* args)
+{
+ PyObject* imOut;
+ int kernelsize;
+ FLOAT32* kerneldata;
+
+ int xsize, ysize;
+ float divisor, offset;
+ PyObject* kernel = NULL;
+ if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize,
+ &divisor, &offset, &kernel))
+ return NULL;
+
+ /* get user-defined kernel */
+ kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32);
+ if (!kerneldata)
+ return NULL;
+ if (kernelsize != xsize * ysize) {
+ free(kerneldata);
+ return ImagingError_ValueError("bad kernel size");
+ }
+
+ imOut = PyImagingNew(
+ ImagingFilter(self->image, xsize, ysize, kerneldata, offset, divisor)
+ );
+
+ free(kerneldata);
+
+ return imOut;
+}
+
+#ifdef WITH_UNSHARPMASK
+static PyObject*
+_gaussian_blur(ImagingObject* self, PyObject* args)
+{
+ Imaging imIn;
+ Imaging imOut;
+
+ float radius = 0;
+ if (!PyArg_ParseTuple(args, "f", &radius))
+ return NULL;
+
+ imIn = self->image;
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+
+ if (!ImagingGaussianBlur(imIn, imOut, radius))
+ return NULL;
+
+ return PyImagingNew(imOut);
+}
+#endif
+
+static PyObject*
+_getpalette(ImagingObject* self, PyObject* args)
+{
+ PyObject* palette;
+ int palettesize = 256;
+ int bits;
+ ImagingShuffler pack;
+
+ char* mode = "RGB";
+ char* rawmode = "RGB";
+ if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode))
+ return NULL;
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ pack = ImagingFindPacker(mode, rawmode, &bits);
+ if (!pack) {
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
+ palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8);
+ if (!palette)
+ return NULL;
+
+ pack((UINT8*) PyString_AsString(palette),
+ self->image->palette->palette, palettesize);
+
+ return palette;
+}
+
+static inline int
+_getxy(PyObject* xy, int* x, int *y)
+{
+ PyObject* value;
+
+ 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);
+ else if (PyFloat_Check(value))
+ *x = (int) PyFloat_AS_DOUBLE(value);
+ else
+ goto badval;
+
+ value = PyTuple_GET_ITEM(xy, 1);
+ if (PyInt_Check(value))
+ *y = PyInt_AS_LONG(value);
+ else if (PyFloat_Check(value))
+ *y = (int) PyFloat_AS_DOUBLE(value);
+ else
+ goto badval;
+
+ return 0;
+
+ badarg:
+ PyErr_SetString(
+ PyExc_TypeError,
+ "argument must be sequence of length 2"
+ );
+ return -1;
+
+ badval:
+ PyErr_SetString(
+ PyExc_TypeError,
+ "an integer is required"
+ );
+ return -1;
+}
+
+static PyObject*
+_getpixel(ImagingObject* self, PyObject* args)
+{
+ PyObject* xy;
+ int x, y;
+
+ if (PyTuple_GET_SIZE(args) != 1) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "argument 1 must be sequence of length 2"
+ );
+ return NULL;
+ }
+
+ xy = PyTuple_GET_ITEM(args, 0);
+
+ if (_getxy(xy, &x, &y))
+ return NULL;
+
+ if (self->access == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return getpixel(self->image, self->access, x, y);
+}
+
+static PyObject*
+_histogram(ImagingObject* self, PyObject* args)
+{
+ ImagingHistogram h;
+ PyObject* list;
+ int i;
+ union {
+ UINT8 u[2];
+ INT32 i[2];
+ FLOAT32 f[2];
+ } extrema;
+ void* ep;
+ int i0, i1;
+ double f0, f1;
+
+ PyObject* extremap = NULL;
+ ImagingObject* maskp = NULL;
+ if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
+ return NULL;
+
+ if (extremap) {
+ ep = &extrema;
+ switch (self->image->type) {
+ case IMAGING_TYPE_UINT8:
+ if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
+ return NULL;
+ /* FIXME: clip */
+ extrema.u[0] = i0;
+ extrema.u[1] = i1;
+ break;
+ case IMAGING_TYPE_INT32:
+ if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
+ return NULL;
+ extrema.i[0] = i0;
+ extrema.i[1] = i1;
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1))
+ return NULL;
+ extrema.f[0] = (FLOAT32) f0;
+ extrema.f[1] = (FLOAT32) f1;
+ break;
+ default:
+ ep = NULL;
+ break;
+ }
+ } else
+ ep = NULL;
+
+ h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
+
+ if (!h)
+ 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);
+ }
+
+ ImagingHistogramDelete(h);
+
+ return list;
+}
+
+#ifdef WITH_MODEFILTER
+static PyObject*
+_modefilter(ImagingObject* self, PyObject* args)
+{
+ int size;
+ if (!PyArg_ParseTuple(args, "i", &size))
+ return NULL;
+
+ return PyImagingNew(ImagingModeFilter(self->image, size));
+}
+#endif
+
+static PyObject*
+_offset(ImagingObject* self, PyObject* args)
+{
+ int xoffset, yoffset;
+ if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset))
+ return NULL;
+
+ return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset));
+}
+
+static PyObject*
+_paste(ImagingObject* self, PyObject* args)
+{
+ int status;
+ char ink[4];
+
+ PyObject* source;
+ 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;
+
+ if (PyImaging_Check(source))
+ status = ImagingPaste(
+ self->image, PyImaging_AsImaging(source),
+ (maskp) ? maskp->image : NULL,
+ x0, y0, x1, y1
+ );
+
+ else {
+ if (!getink(source, self->image, ink))
+ return NULL;
+ status = ImagingFill2(
+ self->image, ink,
+ (maskp) ? maskp->image : NULL,
+ x0, y0, x1, y1
+ );
+ }
+
+ if (status < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_point(ImagingObject* self, PyObject* args)
+{
+ static const char* wrong_number = "wrong number of lut entries";
+
+ int n, i;
+ int bands;
+ Imaging im;
+
+ PyObject* list;
+ char* mode;
+ if (!PyArg_ParseTuple(args, "Oz", &list, &mode))
+ return NULL;
+
+ if (mode && !strcmp(mode, "F")) {
+ FLOAT32* data;
+
+ /* map from 8-bit data to floating point */
+ n = 256;
+ data = getlist(list, &n, wrong_number, TYPE_FLOAT32);
+ if (!data)
+ return NULL;
+ im = ImagingPoint(self->image, mode, (void*) data);
+ free(data);
+
+ } else if (!strcmp(self->image->mode, "I") && mode && !strcmp(mode, "L")) {
+ UINT8* data;
+
+ /* map from 16-bit subset of 32-bit data to 8-bit */
+ /* FIXME: support arbitrary number of entries (requires API change) */
+ n = 65536;
+ data = getlist(list, &n, wrong_number, TYPE_UINT8);
+ if (!data)
+ return NULL;
+ im = ImagingPoint(self->image, mode, (void*) data);
+ free(data);
+
+ } else {
+ INT32* data;
+ UINT8 lut[1024];
+
+ if (mode) {
+ bands = getbands(mode);
+ if (bands < 0)
+ return NULL;
+ } else
+ bands = self->image->bands;
+
+ /* map to integer data */
+ n = 256 * bands;
+ data = getlist(list, &n, wrong_number, TYPE_INT32);
+ if (!data)
+ return NULL;
+
+ if (mode && !strcmp(mode, "I"))
+ im = ImagingPoint(self->image, mode, (void*) data);
+ else if (mode && bands > 1) {
+ for (i = 0; i < 256; i++) {
+ lut[i*4] = CLIP(data[i]);
+ lut[i*4+1] = CLIP(data[i+256]);
+ lut[i*4+2] = CLIP(data[i+512]);
+ if (n > 768)
+ lut[i*4+3] = CLIP(data[i+768]);
+ }
+ im = ImagingPoint(self->image, mode, (void*) lut);
+ } else {
+ /* map individual bands */
+ for (i = 0; i < n; i++)
+ lut[i] = CLIP(data[i]);
+ im = ImagingPoint(self->image, mode, (void*) lut);
+ }
+ free(data);
+ }
+
+ return PyImagingNew(im);
+}
+
+static PyObject*
+_point_transform(ImagingObject* self, PyObject* args)
+{
+ double scale = 1.0;
+ double offset = 0.0;
+ if (!PyArg_ParseTuple(args, "|dd", &scale, &offset))
+ return NULL;
+
+ return PyImagingNew(ImagingPointTransform(self->image, scale, offset));
+}
+
+static PyObject*
+_putdata(ImagingObject* self, PyObject* args)
+{
+ Imaging image;
+ int 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;
+
+ if (!PySequence_Check(data)) {
+ 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 (image->image8) {
+ if (PyString_Check(data)) {
+ unsigned char* p;
+ p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data);
+ if (scale == 1.0 && offset == 0.0)
+ /* Plain string data */
+ for (i = y = 0; i < n; i += image->xsize, y++) {
+ x = n - i;
+ if (x > (int) image->xsize)
+ x = image->xsize;
+ memcpy(image->image8[y], p+i, x);
+ }
+ else
+ /* Scaled and clipped string data */
+ for (i = x = y = 0; i < n; i++) {
+ image->image8[y][x] = CLIP((int) (p[i] * scale + offset));
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ } else {
+ if (scale == 1.0 && offset == 0.0) {
+ /* Clipped data */
+ if (PyList_Check(data)) {
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(data, i);
+ image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ } else {
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(data, i);
+ image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op));
+ Py_XDECREF(op);
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ }
+ } else {
+ if (PyList_Check(data)) {
+ /* Scaled and clipped data */
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PyList_GET_ITEM(data, i);
+ image->image8[y][x] = CLIP(
+ (int) (PyFloat_AsDouble(op) * scale + offset));
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ } else {
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(data, i);
+ image->image8[y][x] = CLIP(
+ (int) (PyFloat_AsDouble(op) * scale + offset));
+ Py_XDECREF(op);
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ }
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ }
+ } else {
+ /* 32-bit images */
+ switch (image->type) {
+ case IMAGING_TYPE_INT32:
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(data, i);
+ IMAGING_PIXEL_INT32(image, x, y) =
+ (INT32) (PyFloat_AsDouble(op) * scale + offset);
+ Py_XDECREF(op);
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ for (i = x = y = 0; i < n; i++) {
+ PyObject *op = PySequence_GetItem(data, i);
+ IMAGING_PIXEL_FLOAT32(image, x, y) =
+ (FLOAT32) (PyFloat_AsDouble(op) * scale + offset);
+ Py_XDECREF(op);
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ default:
+ for (i = x = y = 0; i < n; i++) {
+ char ink[4];
+ PyObject *op = PySequence_GetItem(data, i);
+ if (!op || !getink(op, image, ink)) {
+ Py_DECREF(op);
+ return NULL;
+ }
+ /* FIXME: what about scale and offset? */
+ image->image32[y][x] = *((INT32*) ink);
+ Py_XDECREF(op);
+ if (++x >= (int) image->xsize)
+ x = 0, y++;
+ }
+ PyErr_Clear(); /* Avoid weird exceptions */
+ break;
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_QUANTIZE
+
+#include "Quant.h"
+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;
+
+ if (!self->image->xsize || !self->image->ysize) {
+ /* no content; return an empty image */
+ return PyImagingNew(
+ ImagingNew("P", self->image->xsize, self->image->ysize)
+ );
+ }
+
+ return PyImagingNew(ImagingQuantize(self->image, colours, method, kmeans));
+}
+#endif
+
+static PyObject*
+_putpalette(ImagingObject* self, PyObject* args)
+{
+ ImagingShuffler unpack;
+ int bits;
+
+ char* rawmode;
+ UINT8* palette;
+ int palettesize;
+ if (!PyArg_ParseTuple(args, "ss#", &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;
+ }
+
+ unpack = ImagingFindUnpacker("RGB", rawmode, &bits);
+ if (!unpack) {
+ PyErr_SetString(PyExc_ValueError, wrong_raw_mode);
+ return NULL;
+ }
+
+ ImagingPaletteDelete(self->image->palette);
+
+ strcpy(self->image->mode, "P");
+
+ self->image->palette = ImagingPaletteNew("RGB");
+
+ unpack(self->image->palette->palette, palette, palettesize * 8 / bits);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_putpalettealpha(ImagingObject* self, PyObject* args)
+{
+ int index;
+ int alpha = 0;
+ if (!PyArg_ParseTuple(args, "i|i", &index, &alpha))
+ return NULL;
+
+ if (!self->image->palette) {
+ PyErr_SetString(PyExc_ValueError, no_palette);
+ return NULL;
+ }
+
+ if (index < 0 || index >= 256) {
+ PyErr_SetString(PyExc_ValueError, outside_palette);
+ return NULL;
+ }
+
+ strcpy(self->image->palette->mode, "RGBA");
+ self->image->palette->palette[index*4+3] = (UINT8) alpha;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_putpixel(ImagingObject* self, PyObject* args)
+{
+ Imaging im;
+ char ink[4];
+
+ int x, y;
+ PyObject* color;
+ if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color))
+ return NULL;
+
+ im = self->image;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return NULL;
+ }
+
+ if (!getink(color, im, ink))
+ return NULL;
+
+ if (self->access)
+ self->access->put_pixel(im, x, y, ink);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_RANKFILTER
+static PyObject*
+_rankfilter(ImagingObject* self, PyObject* args)
+{
+ int size, rank;
+ if (!PyArg_ParseTuple(args, "ii", &size, &rank))
+ return NULL;
+
+ return PyImagingNew(ImagingRankFilter(self->image, size, rank));
+}
+#endif
+
+static PyObject*
+_resize(ImagingObject* self, PyObject* args)
+{
+ Imaging imIn;
+ Imaging imOut;
+
+ int xsize, ysize;
+ int filter = IMAGING_TRANSFORM_NEAREST;
+ if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
+ return NULL;
+
+ imIn = self->image;
+
+ imOut = ImagingNew(imIn->mode, xsize, ysize);
+ if (imOut)
+ (void) ImagingResize(imOut, imIn, filter);
+
+ return PyImagingNew(imOut);
+}
+
+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;
+
+ imIn = self->image;
+
+ theta = fmod(theta, 360.0);
+ if (theta < 0.0)
+ 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);
+ } else if (theta == 90.0 || theta == 270.0) {
+ /* Use fast version */
+ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
+ if (imOut) {
+ if (theta == 90.0)
+ (void) ImagingRotate90(imOut, imIn);
+ else
+ (void) ImagingRotate270(imOut, imIn);
+ }
+ } else {
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+ if (imOut) {
+ if (theta == 0.0)
+ /* No rotation: simply copy the input image */
+ (void) ImagingCopy2(imOut, imIn);
+ else if (theta == 180.0)
+ /* Use fast version */
+ (void) ImagingRotate180(imOut, imIn);
+ else
+ /* Use ordinary version */
+ (void) ImagingRotate(imOut, imIn, theta, 0);
+ }
+ }
+
+ return PyImagingNew(imOut);
+}
+
+#define IS_RGB(mode)\
+ (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX"))
+
+static PyObject*
+im_setmode(ImagingObject* self, PyObject* args)
+{
+ /* attempt to modify the mode of an image in place */
+
+ Imaging im;
+
+ char* mode;
+ int modelen;
+ if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen))
+ return NULL;
+
+ im = self->image;
+
+ /* move all logic in here to the libImaging primitive */
+
+ if (!strcmp(im->mode, mode)) {
+ ; /* same mode; always succeeds */
+ } else if (IS_RGB(im->mode) && IS_RGB(mode)) {
+ /* color to color */
+ strcpy(im->mode, mode);
+ im->bands = modelen;
+ if (!strcmp(mode, "RGBA"))
+ (void) ImagingFillBand(im, 3, 255);
+ } else {
+ /* trying doing an in-place conversion */
+ if (!ImagingConvertInPlace(im, mode))
+ return NULL;
+ }
+
+ if (self->access)
+ ImagingAccessDelete(im, self->access);
+ self->access = ImagingAccessNew(im);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_stretch(ImagingObject* self, PyObject* args)
+{
+ Imaging imIn;
+ Imaging imTemp;
+ Imaging imOut;
+
+ int xsize, ysize;
+ int filter = IMAGING_TRANSFORM_NEAREST;
+ if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter))
+ return NULL;
+
+ imIn = self->image;
+
+ /* two-pass resize: minimize size of intermediate image */
+ if (imIn->xsize * ysize < xsize * imIn->ysize)
+ imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize);
+ else
+ imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize);
+ if (!imTemp)
+ return NULL;
+
+ /* first pass */
+ if (!ImagingStretch(imTemp, imIn, filter)) {
+ ImagingDelete(imTemp);
+ return NULL;
+ }
+
+ imOut = ImagingNew(imIn->mode, xsize, ysize);
+ if (!imOut) {
+ ImagingDelete(imTemp);
+ return NULL;
+ }
+
+ /* second pass */
+ if (!ImagingStretch(imOut, imTemp, filter)) {
+ ImagingDelete(imOut);
+ ImagingDelete(imTemp);
+ return NULL;
+ }
+
+ ImagingDelete(imTemp);
+
+ return PyImagingNew(imOut);
+}
+
+static PyObject*
+_transform2(ImagingObject* self, PyObject* args)
+{
+ static const char* wrong_number = "wrong number of matrix entries";
+
+ Imaging imIn;
+ Imaging imOut;
+ int n;
+ double *a;
+
+ ImagingObject* imagep;
+ int x0, y0, x1, y1;
+ int method;
+ PyObject* data;
+ int filter = IMAGING_TRANSFORM_NEAREST;
+ int fill = 1;
+ if (!PyArg_ParseTuple(args, "(iiii)O!iO|ii",
+ &x0, &y0, &x1, &y1,
+ &Imaging_Type, &imagep,
+ &method, &data,
+ &filter, &fill))
+ return NULL;
+
+ switch (method) {
+ case IMAGING_TRANSFORM_AFFINE:
+ n = 6;
+ break;
+ case IMAGING_TRANSFORM_PERSPECTIVE:
+ n = 8;
+ break;
+ case IMAGING_TRANSFORM_QUAD:
+ n = 8;
+ break;
+ default:
+ n = -1; /* force error */
+ }
+
+ a = getlist(data, &n, wrong_number, TYPE_DOUBLE);
+ if (!a)
+ return NULL;
+
+ imOut = self->image;
+ imIn = imagep->image;
+
+ /* FIXME: move transform dispatcher into libImaging */
+
+ switch (method) {
+ case IMAGING_TRANSFORM_AFFINE:
+ imOut = ImagingTransformAffine(
+ imOut, imIn, x0, y0, x1, y1, a, filter, 1
+ );
+ break;
+ case IMAGING_TRANSFORM_PERSPECTIVE:
+ imOut = ImagingTransformPerspective(
+ imOut, imIn, x0, y0, x1, y1, a, filter, 1
+ );
+ break;
+ case IMAGING_TRANSFORM_QUAD:
+ imOut = ImagingTransformQuad(
+ imOut, imIn, x0, y0, x1, y1, a, filter, 1
+ );
+ break;
+ default:
+ (void) ImagingError_ValueError("bad transform method");
+ }
+
+ free(a);
+
+ if (!imOut)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_transpose(ImagingObject* self, PyObject* args)
+{
+ Imaging imIn;
+ Imaging imOut;
+
+ int op;
+ if (!PyArg_ParseTuple(args, "i", &op))
+ return NULL;
+
+ imIn = self->image;
+
+ switch (op) {
+ case 0: /* flip left right */
+ case 1: /* flip top bottom */
+ case 3: /* rotate 180 */
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+ break;
+ case 2: /* rotate 90 */
+ case 4: /* rotate 270 */
+ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize);
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError, "No such transpose operation");
+ return NULL;
+ }
+
+ if (imOut)
+ switch (op) {
+ case 0:
+ (void) ImagingFlipLeftRight(imOut, imIn);
+ break;
+ case 1:
+ (void) ImagingFlipTopBottom(imOut, imIn);
+ break;
+ case 2:
+ (void) ImagingRotate90(imOut, imIn);
+ break;
+ case 3:
+ (void) ImagingRotate180(imOut, imIn);
+ break;
+ case 4:
+ (void) ImagingRotate270(imOut, imIn);
+ break;
+ }
+
+ return PyImagingNew(imOut);
+}
+
+#ifdef WITH_UNSHARPMASK
+static PyObject*
+_unsharp_mask(ImagingObject* self, PyObject* args)
+{
+ Imaging imIn;
+ Imaging imOut;
+
+ float radius;
+ int percent, threshold;
+ if (!PyArg_ParseTuple(args, "fii", &radius, &percent, &threshold))
+ return NULL;
+
+
+ imIn = self->image;
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+
+ if (!ImagingUnsharpMask(imIn, imOut, radius, percent, threshold))
+ return NULL;
+
+ return PyImagingNew(imOut);
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_isblock(ImagingObject* self, PyObject* args)
+{
+ return PyInt_FromLong((long) self->image->block);
+}
+
+static PyObject*
+_getbbox(ImagingObject* self, PyObject* args)
+{
+ int bbox[4];
+ if (!ImagingGetBBox(self->image, bbox)) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]);
+}
+
+static PyObject*
+_getcolors(ImagingObject* self, PyObject* args)
+{
+ ImagingColorItem* items;
+ int i, colors;
+ PyObject* out;
+
+ int maxcolors = 256;
+ if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors))
+ return NULL;
+
+ items = ImagingGetColors(self->image, maxcolors, &colors);
+ if (!items)
+ return NULL;
+
+ if (colors > maxcolors) {
+ out = Py_None;
+ Py_INCREF(out);
+ } else {
+ out = PyList_New(colors);
+ for (i = 0; i < colors; i++) {
+ ImagingColorItem* v = &items[i];
+ PyObject* item = Py_BuildValue(
+ "iN", v->count, getpixel(self->image, self->access, v->x, v->y)
+ );
+ PyList_SetItem(out, i, item);
+ }
+ }
+
+ free(items);
+
+ return out;
+}
+
+static PyObject*
+_getextrema(ImagingObject* self, PyObject* args)
+{
+ union {
+ UINT8 u[2];
+ INT32 i[2];
+ FLOAT32 f[2];
+ } extrema;
+ int status;
+
+ status = ImagingGetExtrema(self->image, &extrema);
+ if (status < 0)
+ return NULL;
+
+ if (status)
+ switch (self->image->type) {
+ case IMAGING_TYPE_UINT8:
+ return Py_BuildValue("ii", extrema.u[0], extrema.u[1]);
+ case IMAGING_TYPE_INT32:
+ return Py_BuildValue("ii", extrema.i[0], extrema.i[1]);
+ case IMAGING_TYPE_FLOAT32:
+ return Py_BuildValue("dd", extrema.f[0], extrema.f[1]);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_getprojection(ImagingObject* self, PyObject* args)
+{
+ unsigned char* xprofile;
+ unsigned char* yprofile;
+ PyObject* result;
+
+ xprofile = malloc(self->image->xsize);
+ yprofile = malloc(self->image->ysize);
+
+ if (xprofile == NULL || yprofile == NULL) {
+ 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);
+
+ free(xprofile);
+ free(yprofile);
+
+ return result;
+}
+
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_getband(ImagingObject* self, PyObject* args)
+{
+ int band;
+
+ if (!PyArg_ParseTuple(args, "i", &band))
+ return NULL;
+
+ return PyImagingNew(ImagingGetBand(self->image, band));
+}
+
+static PyObject*
+_fillband(ImagingObject* self, PyObject* args)
+{
+ int band;
+ int color;
+
+ if (!PyArg_ParseTuple(args, "ii", &band, &color))
+ return NULL;
+
+ if (!ImagingFillBand(self->image, band, color))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_putband(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+ int band;
+ if (!PyArg_ParseTuple(args, "O!i",
+ &Imaging_Type, &imagep,
+ &band))
+ return NULL;
+
+ if (!ImagingPutBand(self->image, imagep->image, band))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_IMAGECHOPS
+
+static PyObject*
+_chop_invert(ImagingObject* self, PyObject* args)
+{
+ return PyImagingNew(ImagingNegative(self->image));
+}
+
+static PyObject*
+_chop_lighter(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopLighter(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_darker(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopDarker(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_difference(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopDifference(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_multiply(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopMultiply(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_screen(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopScreen(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_add(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+ float scale;
+ int offset;
+
+ scale = 1.0;
+ offset = 0;
+
+ if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep,
+ &scale, &offset))
+ return NULL;
+
+ return PyImagingNew(ImagingChopAdd(self->image, imagep->image,
+ scale, offset));
+}
+
+static PyObject*
+_chop_subtract(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+ float scale;
+ int offset;
+
+ scale = 1.0;
+ offset = 0;
+
+ if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep,
+ &scale, &offset))
+ return NULL;
+
+ return PyImagingNew(ImagingChopSubtract(self->image, imagep->image,
+ scale, offset));
+}
+
+static PyObject*
+_chop_and(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopAnd(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_or(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopOr(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_xor(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopXor(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_add_modulo(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image));
+}
+
+static PyObject*
+_chop_subtract_modulo(ImagingObject* self, PyObject* args)
+{
+ ImagingObject* imagep;
+
+ if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep))
+ return NULL;
+
+ return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image));
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_IMAGEDRAW
+
+static PyObject*
+_font_new(PyObject* self_, PyObject* args)
+{
+ ImagingFontObject *self;
+ int i, y0, y1;
+ static const char* wrong_length = "descriptor table has wrong size";
+
+ ImagingObject* imagep;
+ unsigned char* glyphdata;
+ int glyphdata_length;
+ if (!PyArg_ParseTuple(args, "O!s#",
+ &Imaging_Type, &imagep,
+ &glyphdata, &glyphdata_length))
+ return NULL;
+
+ if (glyphdata_length != 256 * 20) {
+ PyErr_SetString(PyExc_ValueError, wrong_length);
+ return NULL;
+ }
+
+ self = PyObject_New(ImagingFontObject, &ImagingFont_Type);
+ if (self == NULL)
+ return NULL;
+
+ /* glyph bitmap */
+ self->bitmap = imagep->image;
+
+ y0 = y1 = 0;
+
+ /* glyph glyphs */
+ for (i = 0; i < 256; i++) {
+ self->glyphs[i].dx = S16(B16(glyphdata, 0));
+ self->glyphs[i].dy = S16(B16(glyphdata, 2));
+ self->glyphs[i].dx0 = S16(B16(glyphdata, 4));
+ self->glyphs[i].dy0 = S16(B16(glyphdata, 6));
+ self->glyphs[i].dx1 = S16(B16(glyphdata, 8));
+ self->glyphs[i].dy1 = S16(B16(glyphdata, 10));
+ self->glyphs[i].sx0 = S16(B16(glyphdata, 12));
+ self->glyphs[i].sy0 = S16(B16(glyphdata, 14));
+ self->glyphs[i].sx1 = S16(B16(glyphdata, 16));
+ self->glyphs[i].sy1 = S16(B16(glyphdata, 18));
+ if (self->glyphs[i].dy0 < y0)
+ y0 = self->glyphs[i].dy0;
+ if (self->glyphs[i].dy1 > y1)
+ y1 = self->glyphs[i].dy1;
+ glyphdata += 20;
+ }
+
+ self->baseline = -y0;
+ self->ysize = y1 - y0;
+
+ /* keep a reference to the bitmap object */
+ Py_INCREF(imagep);
+ self->ref = imagep;
+
+ return (PyObject*) self;
+}
+
+static void
+_font_dealloc(ImagingFontObject* self)
+{
+ Py_XDECREF(self->ref);
+ PyObject_Del(self);
+}
+
+static inline int
+textwidth(ImagingFontObject* self, const unsigned char* text)
+{
+ int xsize;
+
+ for (xsize = 0; *text; text++)
+ xsize += self->glyphs[*text].dx;
+
+ return xsize;
+}
+
+static PyObject*
+_font_getmask(ImagingFontObject* self, PyObject* args)
+{
+ Imaging im;
+ Imaging bitmap;
+ int x, b;
+ int status;
+ Glyph* glyph;
+
+ unsigned char* text;
+ char* mode = "";
+ if (!PyArg_ParseTuple(args, "s|s:getmask", &text, &mode))
+ return NULL;
+
+ im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
+ if (!im)
+ return NULL;
+
+ b = 0;
+ (void) ImagingFill(im, &b);
+
+ b = self->baseline;
+ for (x = 0; *text; text++) {
+ glyph = &self->glyphs[*text];
+ bitmap = ImagingCrop(
+ self->bitmap,
+ glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1
+ );
+ if (!bitmap)
+ goto failed;
+ status = ImagingPaste(
+ im, bitmap, NULL,
+ glyph->dx0+x, glyph->dy0+b, glyph->dx1+x, glyph->dy1+b
+ );
+ ImagingDelete(bitmap);
+ if (status < 0)
+ goto failed;
+ x = x + glyph->dx;
+ b = b + glyph->dy;
+ }
+
+ return PyImagingNew(im);
+
+ failed:
+ ImagingDelete(im);
+ return NULL;
+}
+
+static PyObject*
+_font_getsize(ImagingFontObject* self, PyObject* args)
+{
+ unsigned char* text;
+ if (!PyArg_ParseTuple(args, "s:getsize", &text))
+ return NULL;
+
+ return Py_BuildValue("ii", textwidth(self, text), self->ysize);
+}
+
+static struct PyMethodDef _font_methods[] = {
+ {"getmask", (PyCFunction)_font_getmask, 1},
+ {"getsize", (PyCFunction)_font_getsize, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+_font_getattr(ImagingFontObject* self, char* name)
+{
+ return Py_FindMethod(_font_methods, (PyObject*) self, name);
+}
+
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_draw_new(PyObject* self_, PyObject* args)
+{
+ ImagingDrawObject *self;
+
+ ImagingObject* imagep;
+ int blend = 0;
+ if (!PyArg_ParseTuple(args, "O!|i", &Imaging_Type, &imagep, &blend))
+ return NULL;
+
+ self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type);
+ if (self == NULL)
+ return NULL;
+
+ /* keep a reference to the image object */
+ Py_INCREF(imagep);
+ self->image = imagep;
+
+ self->ink[0] = self->ink[1] = self->ink[2] = self->ink[3] = 0;
+
+ self->blend = blend;
+
+ return (PyObject*) self;
+}
+
+static void
+_draw_dealloc(ImagingDrawObject* self)
+{
+ Py_XDECREF(self->image);
+ PyObject_Del(self);
+}
+
+extern int PyPath_Flatten(PyObject* data, double **xy);
+
+static PyObject*
+_draw_ink(ImagingDrawObject* self, PyObject* args)
+{
+ INT32 ink = 0;
+ PyObject* color;
+ char* mode = NULL; /* not used in this release */
+ if (!PyArg_ParseTuple(args, "O|s", &color, &mode))
+ return NULL;
+
+ if (!getink(color, self->image->image, (char*) &ink))
+ return NULL;
+
+ return PyInt_FromLong((int) ink);
+}
+
+static PyObject*
+_draw_arc(ImagingDrawObject* self, PyObject* args)
+{
+ int x0, y0, x1, y1;
+ int ink;
+ int start, end;
+ int op = 0;
+ if (!PyArg_ParseTuple(args, "(iiii)iii|i",
+ &x0, &y0, &x1, &y1,
+ &start, &end, &ink))
+ return NULL;
+
+ if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end,
+ &ink, op) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_bitmap(ImagingDrawObject* self, PyObject* args)
+{
+ double *xy;
+ int n;
+
+ PyObject *data;
+ ImagingObject* bitmap;
+ int ink;
+ if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+ if (n != 1) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "coordinate list must contain exactly 1 coordinate"
+ );
+ return NULL;
+ }
+
+ n = ImagingDrawBitmap(
+ self->image->image, (int) xy[0], (int) xy[1], bitmap->image,
+ &ink, self->blend
+ );
+
+ free(xy);
+
+ if (n < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_chord(ImagingDrawObject* self, PyObject* args)
+{
+ int x0, y0, x1, y1;
+ int ink, fill;
+ int start, end;
+ if (!PyArg_ParseTuple(args, "(iiii)iiii",
+ &x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
+ return NULL;
+
+ if (ImagingDrawChord(self->image->image, x0, y0, x1, y1,
+ start, end, &ink, fill, self->blend) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_ellipse(ImagingDrawObject* self, PyObject* args)
+{
+ double* xy;
+ int n;
+
+ PyObject* data;
+ int ink;
+ int fill = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+ if (n != 2) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "coordinate list must contain exactly 2 coordinates"
+ );
+ return NULL;
+ }
+
+ n = 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)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+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;
+
+ if (ImagingDrawLine(self->image->image, x0, y0, x1, y1,
+ &ink, self->blend) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_lines(ImagingDrawObject* self, PyObject* args)
+{
+ double *xy;
+ int i, n;
+
+ PyObject *data;
+ int ink;
+ int width = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+
+ if (width <= 1) {
+ double *p = NULL;
+ for (i = 0; i < n-1; i++) {
+ p = &xy[i+i];
+ if (ImagingDrawLine(
+ self->image->image,
+ (int) p[0], (int) p[1], (int) p[2], (int) p[3],
+ &ink, self->blend) < 0) {
+ free(xy);
+ return NULL;
+ }
+ }
+ if (p) /* draw last point */
+ ImagingDrawPoint(
+ self->image->image,
+ (int) p[2], (int) p[3],
+ &ink, self->blend
+ );
+ } else {
+ for (i = 0; i < n-1; i++) {
+ double *p = &xy[i+i];
+ if (ImagingDrawWideLine(
+ self->image->image,
+ (int) p[0], (int) p[1], (int) p[2], (int) p[3],
+ &ink, width, self->blend) < 0) {
+ free(xy);
+ return NULL;
+ }
+ }
+ }
+
+ free(xy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_point(ImagingDrawObject* self, PyObject* args)
+{
+ int x, y;
+ int ink;
+ if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink))
+ return NULL;
+
+ if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_points(ImagingDrawObject* self, PyObject* args)
+{
+ double *xy;
+ int i, n;
+
+ PyObject *data;
+ int ink;
+ if (!PyArg_ParseTuple(args, "Oi", &data, &ink))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+
+ for (i = 0; i < n; i++) {
+ 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);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#ifdef WITH_ARROW
+
+/* from outline.c */
+extern ImagingOutline PyOutline_AsOutline(PyObject* outline);
+
+static PyObject*
+_draw_outline(ImagingDrawObject* self, PyObject* args)
+{
+ ImagingOutline outline;
+
+ PyObject* outline_;
+ int ink;
+ int fill = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill))
+ return NULL;
+
+ outline = PyOutline_AsOutline(outline_);
+ if (!outline) {
+ PyErr_SetString(PyExc_TypeError, "expected outline object");
+ return NULL;
+ }
+
+ if (ImagingDrawOutline(self->image->image, outline,
+ &ink, fill, self->blend) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
+
+static PyObject*
+_draw_pieslice(ImagingDrawObject* self, PyObject* args)
+{
+ int x0, y0, x1, y1;
+ int ink, fill;
+ int start, end;
+ if (!PyArg_ParseTuple(args, "(iiii)iiii",
+ &x0, &y0, &x1, &y1, &start, &end, &ink, &fill))
+ return NULL;
+
+ if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1,
+ start, end, &ink, fill, self->blend) < 0)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_polygon(ImagingDrawObject* self, PyObject* args)
+{
+ double *xy;
+ int *ixy;
+ int n, i;
+
+ PyObject* data;
+ int ink;
+ int fill = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+ if (n < 2) {
+ 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];
+ }
+
+ free(xy);
+
+ if (ImagingDrawPolygon(self->image->image, n, ixy,
+ &ink, fill, self->blend) < 0) {
+ free(ixy);
+ return NULL;
+ }
+
+ free(ixy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw_rectangle(ImagingDrawObject* self, PyObject* args)
+{
+ double* xy;
+ int n;
+
+ PyObject* data;
+ int ink;
+ int fill = 0;
+ if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill))
+ return NULL;
+
+ n = PyPath_Flatten(data, &xy);
+ if (n < 0)
+ return NULL;
+ if (n != 2) {
+ PyErr_SetString(
+ PyExc_TypeError,
+ "coordinate list must contain exactly 2 coordinates"
+ );
+ return NULL;
+ }
+
+ n = 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)
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef _draw_methods[] = {
+#ifdef WITH_IMAGEDRAW
+ /* Graphics (ImageDraw) */
+ {"draw_line", (PyCFunction)_draw_line, 1},
+ {"draw_lines", (PyCFunction)_draw_lines, 1},
+#ifdef WITH_ARROW
+ {"draw_outline", (PyCFunction)_draw_outline, 1},
+#endif
+ {"draw_polygon", (PyCFunction)_draw_polygon, 1},
+ {"draw_rectangle", (PyCFunction)_draw_rectangle, 1},
+ {"draw_point", (PyCFunction)_draw_point, 1},
+ {"draw_points", (PyCFunction)_draw_points, 1},
+ {"draw_arc", (PyCFunction)_draw_arc, 1},
+ {"draw_bitmap", (PyCFunction)_draw_bitmap, 1},
+ {"draw_chord", (PyCFunction)_draw_chord, 1},
+ {"draw_ellipse", (PyCFunction)_draw_ellipse, 1},
+ {"draw_pieslice", (PyCFunction)_draw_pieslice, 1},
+ {"draw_ink", (PyCFunction)_draw_ink, 1},
+#endif
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+_draw_getattr(ImagingDrawObject* self, char* name)
+{
+ return Py_FindMethod(_draw_methods, (PyObject*) self, name);
+}
+
+#endif
+
+
+static PyObject*
+pixel_access_new(ImagingObject* imagep, PyObject* args)
+{
+ PixelAccessObject *self;
+
+ int readonly = 0;
+ if (!PyArg_ParseTuple(args, "|i", &readonly))
+ return NULL;
+
+ self = PyObject_New(PixelAccessObject, &PixelAccess_Type);
+ if (self == NULL)
+ return NULL;
+
+ /* keep a reference to the image object */
+ Py_INCREF(imagep);
+ self->image = imagep;
+
+ self->readonly = readonly;
+
+ return (PyObject*) self;
+}
+
+static void
+pixel_access_dealloc(PixelAccessObject* self)
+{
+ Py_XDECREF(self->image);
+ PyObject_Del(self);
+}
+
+static PyObject *
+pixel_access_getitem(PixelAccessObject *self, PyObject *xy)
+{
+ int x, y;
+ if (_getxy(xy, &x, &y))
+ return NULL;
+
+ return getpixel(self->image->image, self->image->access, x, y);
+}
+
+static int
+pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color)
+{
+ Imaging im = self->image->image;
+ char ink[4];
+ int x, y;
+
+ if (self->readonly) {
+ (void) ImagingError_ValueError(readonly);
+ return -1;
+ }
+
+ if (_getxy(xy, &x, &y))
+ return -1;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ PyErr_SetString(PyExc_IndexError, outside_image);
+ return -1;
+ }
+
+ if (!color) /* FIXME: raise exception? */
+ return 0;
+
+ if (!getink(color, im, ink))
+ return -1;
+
+ self->image->access->put_pixel(im, x, y, ink);
+
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+/* EFFECTS (experimental) */
+/* -------------------------------------------------------------------- */
+
+#ifdef WITH_EFFECTS
+
+static PyObject*
+_effect_mandelbrot(ImagingObject* self, PyObject* args)
+{
+ int xsize = 512;
+ int ysize = 512;
+ double extent[4];
+ int quality = 100;
+
+ extent[0] = -3; extent[1] = -2.5;
+ extent[2] = 2; extent[3] = 2.5;
+
+ if (!PyArg_ParseTuple(args, "|(ii)(dddd)i", &xsize, &ysize,
+ &extent[0], &extent[1], &extent[2], &extent[3],
+ &quality))
+ return NULL;
+
+ return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality));
+}
+
+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 PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma));
+}
+
+static PyObject*
+_effect_spread(ImagingObject* self, PyObject* args)
+{
+ int dist;
+
+ if (!PyArg_ParseTuple(args, "i", &dist))
+ return NULL;
+
+ return PyImagingNew(ImagingEffectSpread(self->image, dist));
+}
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/* UTILITIES */
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+_crc32(PyObject* self, PyObject* args)
+{
+ unsigned char* buffer;
+ int bytes;
+ int hi, lo;
+ UINT32 crc;
+
+ hi = lo = 0;
+
+ if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo))
+ return NULL;
+
+ crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF);
+
+ crc = ImagingCRC32(crc, (unsigned char *)buffer, bytes);
+
+ return Py_BuildValue("ii", (crc >> 16) & 0xFFFF, crc & 0xFFFF);
+}
+
+static PyObject*
+_getcodecstatus(PyObject* self, PyObject* args)
+{
+ int status;
+ char* msg;
+
+ if (!PyArg_ParseTuple(args, "i", &status))
+ return NULL;
+
+ switch (status) {
+ case IMAGING_CODEC_OVERRUN:
+ msg = "buffer overrun"; break;
+ case IMAGING_CODEC_BROKEN:
+ msg = "broken data stream"; break;
+ case IMAGING_CODEC_UNKNOWN:
+ msg = "unrecognized data stream contents"; break;
+ case IMAGING_CODEC_CONFIG:
+ msg = "codec configuration error"; break;
+ case IMAGING_CODEC_MEMORY:
+ msg = "out of memory"; break;
+ default:
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return PyString_FromString(msg);
+}
+
+/* -------------------------------------------------------------------- */
+/* DEBUGGING HELPERS */
+/* -------------------------------------------------------------------- */
+
+
+#ifdef WITH_DEBUG
+
+static PyObject*
+_save_ppm(ImagingObject* self, PyObject* args)
+{
+ char* filename;
+
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ if (!ImagingSavePPM(self->image, filename))
+ return NULL;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+#endif
+
+/* -------------------------------------------------------------------- */
+
+/* methods */
+
+static struct PyMethodDef methods[] = {
+
+ /* Put commonly used methods first */
+ {"getpixel", (PyCFunction)_getpixel, 1},
+ {"putpixel", (PyCFunction)_putpixel, 1},
+
+ {"pixel_access", (PyCFunction)pixel_access_new, 1},
+
+ /* Standard processing methods (Image) */
+ {"convert", (PyCFunction)_convert, 1},
+ {"convert2", (PyCFunction)_convert2, 1},
+ {"convert_matrix", (PyCFunction)_convert_matrix, 1},
+ {"copy", (PyCFunction)_copy, 1},
+ {"copy2", (PyCFunction)_copy2, 1},
+#ifdef WITH_CRACKCODE
+ {"crackcode", (PyCFunction)_crackcode, 1},
+#endif
+ {"crop", (PyCFunction)_crop, 1},
+ {"expand", (PyCFunction)_expand, 1},
+ {"filter", (PyCFunction)_filter, 1},
+ {"histogram", (PyCFunction)_histogram, 1},
+#ifdef WITH_MODEFILTER
+ {"modefilter", (PyCFunction)_modefilter, 1},
+#endif
+ {"offset", (PyCFunction)_offset, 1},
+ {"paste", (PyCFunction)_paste, 1},
+ {"point", (PyCFunction)_point, 1},
+ {"point_transform", (PyCFunction)_point_transform, 1},
+ {"putdata", (PyCFunction)_putdata, 1},
+#ifdef WITH_QUANTIZE
+ {"quantize", (PyCFunction)_quantize, 1},
+#endif
+#ifdef WITH_RANKFILTER
+ {"rankfilter", (PyCFunction)_rankfilter, 1},
+#endif
+ {"resize", (PyCFunction)_resize, 1},
+ {"rotate", (PyCFunction)_rotate, 1},
+ {"stretch", (PyCFunction)_stretch, 1},
+ {"transpose", (PyCFunction)_transpose, 1},
+ {"transform2", (PyCFunction)_transform2, 1},
+
+ {"isblock", (PyCFunction)_isblock, 1},
+
+ {"getbbox", (PyCFunction)_getbbox, 1},
+ {"getcolors", (PyCFunction)_getcolors, 1},
+ {"getextrema", (PyCFunction)_getextrema, 1},
+ {"getprojection", (PyCFunction)_getprojection, 1},
+
+ {"getband", (PyCFunction)_getband, 1},
+ {"putband", (PyCFunction)_putband, 1},
+ {"fillband", (PyCFunction)_fillband, 1},
+
+ {"setmode", (PyCFunction)im_setmode, 1},
+
+ {"getpalette", (PyCFunction)_getpalette, 1},
+ {"putpalette", (PyCFunction)_putpalette, 1},
+ {"putpalettealpha", (PyCFunction)_putpalettealpha, 1},
+
+#ifdef WITH_IMAGECHOPS
+ /* Channel operations (ImageChops) */
+ {"chop_invert", (PyCFunction)_chop_invert, 1},
+ {"chop_lighter", (PyCFunction)_chop_lighter, 1},
+ {"chop_darker", (PyCFunction)_chop_darker, 1},
+ {"chop_difference", (PyCFunction)_chop_difference, 1},
+ {"chop_multiply", (PyCFunction)_chop_multiply, 1},
+ {"chop_screen", (PyCFunction)_chop_screen, 1},
+ {"chop_add", (PyCFunction)_chop_add, 1},
+ {"chop_subtract", (PyCFunction)_chop_subtract, 1},
+ {"chop_add_modulo", (PyCFunction)_chop_add_modulo, 1},
+ {"chop_subtract_modulo", (PyCFunction)_chop_subtract_modulo, 1},
+ {"chop_and", (PyCFunction)_chop_and, 1},
+ {"chop_or", (PyCFunction)_chop_or, 1},
+ {"chop_xor", (PyCFunction)_chop_xor, 1},
+#endif
+
+#ifdef WITH_UNSHARPMASK
+ /* Kevin Cazabon's unsharpmask extension */
+ {"gaussian_blur", (PyCFunction)_gaussian_blur, 1},
+ {"unsharp_mask", (PyCFunction)_unsharp_mask, 1},
+#endif
+
+#ifdef WITH_EFFECTS
+ /* Special effects */
+ {"effect_spread", (PyCFunction)_effect_spread, 1},
+#endif
+
+ /* Misc. */
+ {"new_array", (PyCFunction)_new_array, 1},
+ {"new_block", (PyCFunction)_new_block, 1},
+
+#ifdef WITH_DEBUG
+ {"save_ppm", (PyCFunction)_save_ppm, 1},
+#endif
+
+ {NULL, NULL} /* sentinel */
+};
+
+
+/* attributes */
+
+static PyObject*
+_getattr(ImagingObject* self, char* name)
+{
+ 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;
+}
+
+
+/* basic sequence semantics */
+
+static Py_ssize_t
+image_length(ImagingObject *self)
+{
+ Imaging im = self->image;
+
+ return im->xsize * im->ysize;
+}
+
+static PyObject *
+image_item(ImagingObject *self, Py_ssize_t i)
+{
+ int x, y;
+ Imaging im = self->image;
+
+ if (im->xsize > 0) {
+ x = i % im->xsize;
+ y = i / im->xsize;
+ } else
+ x = y = 0; /* leave it to getpixel to raise an exception */
+
+ return getpixel(im, self->access, x, y);
+}
+
+static PySequenceMethods image_as_sequence = {
+ (inquiry) image_length, /*sq_length*/
+ (binaryfunc) NULL, /*sq_concat*/
+ (ssizeargfunc) NULL, /*sq_repeat*/
+ (ssizeargfunc) image_item, /*sq_item*/
+ (ssizessizeargfunc) NULL, /*sq_slice*/
+ (ssizeobjargproc) NULL, /*sq_ass_item*/
+ (ssizessizeobjargproc) NULL, /*sq_ass_slice*/
+};
+
+
+/* type description */
+
+statichere PyTypeObject Imaging_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "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*/
+ 0, /*tp_as_number */
+ &image_as_sequence, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0 /*tp_hash*/
+};
+
+#ifdef WITH_IMAGEDRAW
+
+statichere PyTypeObject ImagingFont_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "ImagingFont", /*tp_name*/
+ sizeof(ImagingFontObject), /*tp_size*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_font_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ (getattrfunc)_font_getattr, /*tp_getattr*/
+};
+
+statichere PyTypeObject ImagingDraw_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "ImagingDraw", /*tp_name*/
+ sizeof(ImagingDrawObject), /*tp_size*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)_draw_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ (getattrfunc)_draw_getattr, /*tp_getattr*/
+};
+
+#endif
+
+static PyMappingMethods pixel_access_as_mapping = {
+ (inquiry) 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,
+ /* methods */
+ (destructor)pixel_access_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 */
+ &pixel_access_as_mapping, /*tp_as_mapping */
+ 0 /*tp_hash*/
+};
+
+/* -------------------------------------------------------------------- */
+
+/* FIXME: this is something of a mess. Should replace this with
+ pluggable codecs, but not before PIL 1.2 */
+
+/* Decoders (in decode.c) */
+extern PyObject* PyImaging_BitDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args);
+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_MspDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_PcxDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_RawDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_XbmDecoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args);
+
+/* Encoders (in encode.c) */
+extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args);
+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);
+
+/* Display support etc (in display.c) */
+#ifdef WIN32
+extern PyObject* PyImaging_CreateWindowWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_DisplayWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_DisplayModeWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_GrabScreenWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_ListWindowsWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_EventLoopWin32(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_DrawWmf(PyObject* self, PyObject* args);
+#endif
+
+/* Experimental path stuff (in path.c) */
+extern PyObject* PyPath_Create(ImagingObject* self, PyObject* args);
+
+/* Experimental outline stuff (in outline.c) */
+extern PyObject* PyOutline_Create(ImagingObject* self, PyObject* args);
+
+extern PyObject* PyImaging_Mapper(PyObject* self, PyObject* args);
+extern PyObject* PyImaging_MapBuffer(PyObject* self, PyObject* args);
+
+static PyMethodDef functions[] = {
+
+ /* Object factories */
+ {"blend", (PyCFunction)_blend, 1},
+ {"fill", (PyCFunction)_fill, 1},
+ {"new", (PyCFunction)_new, 1},
+
+ {"getcount", (PyCFunction)_getcount, 1},
+
+ /* Functions */
+ {"convert", (PyCFunction)_convert2, 1},
+ {"copy", (PyCFunction)_copy2, 1},
+
+ /* Codecs */
+ {"bit_decoder", (PyCFunction)PyImaging_BitDecoderNew, 1},
+ {"eps_encoder", (PyCFunction)PyImaging_EpsEncoderNew, 1},
+ {"fli_decoder", (PyCFunction)PyImaging_FliDecoderNew, 1},
+ {"gif_decoder", (PyCFunction)PyImaging_GifDecoderNew, 1},
+ {"gif_encoder", (PyCFunction)PyImaging_GifEncoderNew, 1},
+ {"hex_decoder", (PyCFunction)PyImaging_HexDecoderNew, 1},
+ {"hex_encoder", (PyCFunction)PyImaging_EpsEncoderNew, 1}, /* EPS=HEX! */
+#ifdef HAVE_LIBJPEG
+ {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1},
+ {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1},
+#endif
+ {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
+ {"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
+ {"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},
+ {"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1},
+ {"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, 1},
+ {"pcx_encoder", (PyCFunction)PyImaging_PcxEncoderNew, 1},
+ {"raw_decoder", (PyCFunction)PyImaging_RawDecoderNew, 1},
+ {"raw_encoder", (PyCFunction)PyImaging_RawEncoderNew, 1},
+ {"sun_rle_decoder", (PyCFunction)PyImaging_SunRleDecoderNew, 1},
+ {"tga_rle_decoder", (PyCFunction)PyImaging_TgaRleDecoderNew, 1},
+ {"xbm_decoder", (PyCFunction)PyImaging_XbmDecoderNew, 1},
+ {"xbm_encoder", (PyCFunction)PyImaging_XbmEncoderNew, 1},
+#ifdef HAVE_LIBZ
+ {"zip_decoder", (PyCFunction)PyImaging_ZipDecoderNew, 1},
+ {"zip_encoder", (PyCFunction)PyImaging_ZipEncoderNew, 1},
+#endif
+
+ /* Memory mapping */
+#ifdef WITH_MAPPING
+#ifdef WIN32
+ {"map", (PyCFunction)PyImaging_Mapper, 1},
+#endif
+ {"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1},
+#endif
+
+ /* Display support */
+#ifdef WIN32
+ {"display", (PyCFunction)PyImaging_DisplayWin32, 1},
+ {"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1},
+ {"grabscreen", (PyCFunction)PyImaging_GrabScreenWin32, 1},
+ {"grabclipboard", (PyCFunction)PyImaging_GrabClipboardWin32, 1},
+ {"createwindow", (PyCFunction)PyImaging_CreateWindowWin32, 1},
+ {"eventloop", (PyCFunction)PyImaging_EventLoopWin32, 1},
+ {"listwindows", (PyCFunction)PyImaging_ListWindowsWin32, 1},
+ {"drawwmf", (PyCFunction)PyImaging_DrawWmf, 1},
+#endif
+
+ /* Utilities */
+ {"crc32", (PyCFunction)_crc32, 1},
+ {"getcodecstatus", (PyCFunction)_getcodecstatus, 1},
+
+ /* Debugging stuff */
+ {"open_ppm", (PyCFunction)_open_ppm, 1},
+
+ /* Special effects (experimental) */
+#ifdef WITH_EFFECTS
+ {"effect_mandelbrot", (PyCFunction)_effect_mandelbrot, 1},
+ {"effect_noise", (PyCFunction)_effect_noise, 1},
+ {"linear_gradient", (PyCFunction)_linear_gradient, 1},
+ {"radial_gradient", (PyCFunction)_radial_gradient, 1},
+ {"wedge", (PyCFunction)_linear_gradient, 1}, /* Compatibility */
+#endif
+
+ /* Drawing support stuff */
+#ifdef WITH_IMAGEDRAW
+ {"font", (PyCFunction)_font_new, 1},
+ {"draw", (PyCFunction)_draw_new, 1},
+#endif
+
+ /* Experimental path stuff */
+#ifdef WITH_IMAGEPATH
+ {"path", (PyCFunction)PyPath_Create, 1},
+#endif
+
+ /* Experimental arrow graphics stuff */
+#ifdef WITH_ARROW
+ {"outline", (PyCFunction)PyOutline_Create, 1},
+#endif
+
+ {NULL, NULL} /* sentinel */
+};
+
+DL_EXPORT(void)
+init_imaging(void)
+{
+ PyObject* m;
+ PyObject* d;
+
+ /* Patch object type */
+ Imaging_Type.ob_type = &PyType_Type;
+#ifdef WITH_IMAGEDRAW
+ ImagingFont_Type.ob_type = &PyType_Type;
+ ImagingDraw_Type.ob_type = &PyType_Type;
+#endif
+ PixelAccess_Type.ob_type = &PyType_Type;
+
+ 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()));
+ }
+#endif
+
+#ifdef HAVE_LIBZ
+ {
+ extern const char* ImagingZipVersion(void);
+ PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion()));
+ }
+#endif
+}
diff --git a/_imagingcms.c b/_imagingcms.c
new file mode 100644
index 000000000..e9a3b00c5
--- /dev/null
+++ b/_imagingcms.c
@@ -0,0 +1,608 @@
+/*
+ * pyCMS
+ * a Python / PIL interface to the littleCMS ICC Color Management System
+ * Copyright (C) 2002-2003 Kevin Cazabon
+ * kevin@cazabon.com
+ * 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
+ */
+
+#define COPYRIGHTINFO "\
+pyCMS\n\
+a Python / PIL interface to the littleCMS ICC Color Management System\n\
+Copyright (C) 2002-2003 Kevin Cazabon\n\
+kevin@cazabon.com\n\
+http://www.cazabon.com\n\
+"
+
+#include "Python.h"
+#include "lcms.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
+
+#ifdef WIN32
+#include
+#endif
+
+#define PYCMSVERSION "0.1.0 pil"
+
+/* version history */
+
+/*
+ 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
+ due to improper destructor setup for PyCObjects
+ - added buildProofTransformFromOpenProfiles() function
+ - eliminated some code redundancy, centralizing several common tasks with internal functions
+
+ 0.0.1 alpha: First public release Dec 26, 2002
+
+*/
+
+/* 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
+
+*/
+
+/*
+ INTENT_PERCEPTUAL 0
+ INTENT_RELATIVE_COLORIMETRIC 1
+ INTENT_SATURATION 2
+ INTENT_ABSOLUTE_COLORIMETRIC 3
+*/
+
+/* -------------------------------------------------------------------- */
+/* wrapper classes */
+
+/* a profile represents the ICC characteristics for a specific device */
+
+typedef struct {
+ PyObject_HEAD
+ cmsHPROFILE profile;
+} CmsProfileObject;
+
+staticforward PyTypeObject CmsProfile_Type;
+
+#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type)
+
+static PyObject*
+cms_profile_new(cmsHPROFILE profile)
+{
+ CmsProfileObject* self;
+
+ self = PyObject_New(CmsProfileObject, &CmsProfile_Type);
+ if (!self)
+ return NULL;
+
+ self->profile = profile;
+
+ return (PyObject*) self;
+}
+
+static PyObject*
+cms_profile_open(PyObject* self, PyObject* args)
+{
+ cmsHPROFILE hProfile;
+
+ char* sProfile;
+ 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");
+ return NULL;
+ }
+
+ return cms_profile_new(hProfile);
+}
+
+static PyObject*
+cms_profile_fromstring(PyObject* self, PyObject* args)
+{
+ cmsHPROFILE hProfile;
+
+ char* pProfile;
+ int nProfile;
+ if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile))
+ return NULL;
+
+ cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ hProfile = cmsOpenProfileFromMem(pProfile, nProfile);
+ if (!hProfile)
+ PyErr_SetString(PyExc_IOError, "cannot open profile from string");
+
+ return cms_profile_new(hProfile);
+}
+
+static void
+cms_profile_dealloc(CmsProfileObject* self)
+{
+ (void) cmsCloseProfile(self->profile);
+ PyObject_Del(self);
+}
+
+/* a transform represents the mapping between two profiles */
+
+typedef struct {
+ PyObject_HEAD
+ char mode_in[8];
+ char mode_out[8];
+ cmsHTRANSFORM transform;
+} CmsTransformObject;
+
+staticforward PyTypeObject CmsTransform_Type;
+
+#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type)
+
+static PyObject*
+cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out)
+{
+ CmsTransformObject* self;
+
+ self = PyObject_New(CmsTransformObject, &CmsTransform_Type);
+ if (!self)
+ return NULL;
+
+ self->transform = transform;
+
+ strcpy(self->mode_in, mode_in);
+ strcpy(self->mode_out, mode_out);
+
+ return (PyObject*) self;
+}
+
+static void
+cms_transform_dealloc(CmsTransformObject* self)
+{
+ cmsDeleteTransform(self->transform);
+ PyObject_Del(self);
+}
+
+/* -------------------------------------------------------------------- */
+/* internal functions */
+
+static const char*
+findICmode(icColorSpaceSignature 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";
+ default: return ""; /* other TBA */
+ }
+}
+
+static DWORD
+findLCMStype(char* PILmode)
+{
+ if (strcmp(PILmode, "RGB") == 0) {
+ return TYPE_RGBA_8;
+ }
+ else if (strcmp(PILmode, "RGBA") == 0) {
+ return TYPE_RGBA_8;
+ }
+ else if (strcmp(PILmode, "RGBX") == 0) {
+ return TYPE_RGBA_8;
+ }
+ else if (strcmp(PILmode, "RGBA;16B") == 0) {
+ return TYPE_RGBA_16;
+ }
+ else if (strcmp(PILmode, "CMYK") == 0) {
+ return TYPE_CMYK_8;
+ }
+ else if (strcmp(PILmode, "L") == 0) {
+ return TYPE_GRAY_8;
+ }
+ else if (strcmp(PILmode, "L;16") == 0) {
+ return TYPE_GRAY_16;
+ }
+ else if (strcmp(PILmode, "L;16B") == 0) {
+ return TYPE_GRAY_16_SE;
+ }
+ else if (strcmp(PILmode, "YCCA") == 0) {
+ return TYPE_YCbCr_8;
+ }
+ else if (strcmp(PILmode, "YCC") == 0) {
+ return TYPE_YCbCr_8;
+ }
+
+ else {
+ /* take a wild guess... but you probably should fail instead. */
+ return TYPE_GRAY_8; /* so there's no buffer overrun... */
+ }
+}
+
+static int
+pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform)
+{
+ int i;
+
+ if (im->xsize > imOut->xsize || im->ysize > imOut->ysize)
+ return -1;
+
+ Py_BEGIN_ALLOW_THREADS
+
+ for (i = 0; i < im->ysize; i++)
+ cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize);
+
+ Py_END_ALLOW_THREADS
+
+ return 0;
+}
+
+static cmsHTRANSFORM
+_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS)
+{
+ cmsHTRANSFORM hTransform;
+
+ cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ Py_BEGIN_ALLOW_THREADS
+
+ /* create the transform */
+ hTransform = cmsCreateTransform(hInputProfile,
+ findLCMStype(sInMode),
+ hOutputProfile,
+ findLCMStype(sOutMode),
+ iRenderingIntent, cmsFLAGS);
+
+ Py_END_ALLOW_THREADS
+
+ if (!hTransform)
+ PyErr_SetString(PyExc_ValueError, "cannot build transform");
+
+ return hTransform; /* if NULL, an exception is set */
+}
+
+static cmsHTRANSFORM
+_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS)
+{
+ cmsHTRANSFORM hTransform;
+
+ cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ Py_BEGIN_ALLOW_THREADS
+
+ /* create the transform */
+ hTransform = cmsCreateProofingTransform(hInputProfile,
+ findLCMStype(sInMode),
+ hOutputProfile,
+ findLCMStype(sOutMode),
+ hProofProfile,
+ iRenderingIntent,
+ iProofIntent,
+ cmsFLAGS);
+
+ Py_END_ALLOW_THREADS
+
+ if (!hTransform)
+ PyErr_SetString(PyExc_ValueError, "cannot build proof transform");
+
+ return hTransform; /* if NULL, an exception is set */
+}
+
+/* -------------------------------------------------------------------- */
+/* Python callable functions */
+
+static PyObject *
+buildTransform(PyObject *self, PyObject *args) {
+ CmsProfileObject *pInputProfile;
+ CmsProfileObject *pOutputProfile;
+ char *sInMode;
+ char *sOutMode;
+ int iRenderingIntent = 0;
+ int cmsFLAGS = 0;
+
+ cmsHTRANSFORM transform = NULL;
+
+ 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)
+ return NULL;
+
+ return cms_transform_new(transform, sInMode, sOutMode);
+}
+
+static PyObject *
+buildProofTransform(PyObject *self, PyObject *args)
+{
+ CmsProfileObject *pInputProfile;
+ CmsProfileObject *pOutputProfile;
+ CmsProfileObject *pProofProfile;
+ char *sInMode;
+ char *sOutMode;
+ int iRenderingIntent = 0;
+ int iProofIntent = 0;
+ int cmsFLAGS = 0;
+
+ cmsHTRANSFORM transform = NULL;
+
+ 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;
+
+ return cms_transform_new(transform, sInMode, sOutMode);
+
+}
+
+static PyObject *
+cms_transform_apply(CmsTransformObject *self, PyObject *args)
+{
+ long idIn;
+ long idOut;
+ Imaging im;
+ Imaging imOut;
+
+ int result;
+
+ if (!PyArg_ParseTuple(args, "ll: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);
+}
+
+/* -------------------------------------------------------------------- */
+/* Python-Callable On-The-Fly profile creation functions */
+
+static PyObject *
+createProfile(PyObject *self, PyObject *args)
+{
+ char *sColorSpace;
+ cmsHPROFILE hProfile;
+ int iColorTemp = 0;
+ LPcmsCIExyY whitePoint = NULL;
+ LCMSBOOL result;
+
+ if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp))
+ return NULL;
+
+ cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ if (strcmp(sColorSpace, "LAB") == 0) {
+ if (iColorTemp > 0) {
+ result = cmsWhitePointFromTemp(iColorTemp, whitePoint);
+ if (!result) {
+ PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin");
+ return NULL;
+ }
+ hProfile = cmsCreateLabProfile(whitePoint);
+ } else
+ hProfile = cmsCreateLabProfile(NULL);
+ }
+ else if (strcmp(sColorSpace, "XYZ") == 0)
+ hProfile = cmsCreateXYZProfile();
+ else if (strcmp(sColorSpace, "sRGB") == 0)
+ hProfile = cmsCreate_sRGBProfile();
+ else
+ hProfile = NULL;
+
+ if (!hProfile) {
+ PyErr_SetString(PyExc_ValueError, "failed to create requested color space");
+ return NULL;
+ }
+
+ return cms_profile_new(hProfile);
+}
+
+/* -------------------------------------------------------------------- */
+/* profile methods */
+
+static PyObject *
+cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args)
+{
+ LCMSBOOL result;
+
+ int intent;
+ int direction;
+ if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction))
+ return NULL;
+
+ result = cmsIsIntentSupported(self->profile, intent, direction);
+
+ /* printf("cmsIsIntentSupported(%p, %d, %d) => %d\n", self->profile, intent, direction, result); */
+
+ return PyInt_FromLong(result != 0);
+}
+
+#ifdef WIN32
+static PyObject *
+cms_get_display_profile_win32(PyObject* self, PyObject* args)
+{
+ char filename[MAX_PATH];
+ DWORD filename_size;
+ BOOL ok;
+
+ int handle = 0;
+ int is_dc = 0;
+ if (!PyArg_ParseTuple(args, "|ii:get_display_profile", &handle, &is_dc))
+ return NULL;
+
+ filename_size = sizeof(filename);
+
+ if (is_dc) {
+ ok = GetICMProfile((HDC) handle, &filename_size, filename);
+ } else {
+ HDC dc = GetDC((HWND) handle);
+ ok = GetICMProfile(dc, &filename_size, filename);
+ ReleaseDC((HWND) handle, dc);
+ }
+
+ if (ok)
+ return PyString_FromStringAndSize(filename, filename_size-1);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#endif
+
+/* -------------------------------------------------------------------- */
+/* Python interface setup */
+
+static PyMethodDef pyCMSdll_methods[] = {
+
+ {"profile_open", cms_profile_open, 1},
+ {"profile_fromstring", cms_profile_fromstring, 1},
+
+ /* profile and transform functions */
+ {"buildTransform", buildTransform, 1},
+ {"buildProofTransform", buildProofTransform, 1},
+ {"createProfile", createProfile, 1},
+
+ /* platform specific tools */
+#ifdef WIN32
+ {"get_display_profile_win32", cms_get_display_profile_win32, 1},
+#endif
+
+ {NULL, NULL}
+};
+
+static struct PyMethodDef cms_profile_methods[] = {
+ {"is_intent_supported", (PyCFunction) cms_profile_is_intent_supported, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+cms_profile_getattr(CmsProfileObject* self, char* name)
+{
+ 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);
+}
+
+statichere PyTypeObject CmsProfile_Type = {
+ PyObject_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*/
+};
+
+static struct PyMethodDef cms_transform_methods[] = {
+ {"apply", (PyCFunction) cms_transform_apply, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+cms_transform_getattr(CmsTransformObject* self, char* name)
+{
+ 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);
+}
+
+statichere PyTypeObject CmsTransform_Type = {
+ PyObject_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*/
+};
+
+DL_EXPORT(void)
+init_imagingcms(void)
+{
+ 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
+ PyDict_SetItemString(d, "littlecms_version", v);
+}
diff --git a/_imagingft.c b/_imagingft.c
new file mode 100644
index 000000000..935808718
--- /dev/null
+++ b/_imagingft.c
@@ -0,0 +1,502 @@
+/*
+ * PIL FreeType Driver
+ *
+ * a FreeType 2.X driver for PIL
+ *
+ * history:
+ * 2001-02-17 fl Created (based on old experimental freetype 1.0 code)
+ * 2001-04-18 fl Fixed some egcs compiler nits
+ * 2002-11-08 fl Added unicode support; more font metrics, etc
+ * 2003-05-20 fl Fixed compilation under 1.5.2 and newer non-unicode builds
+ * 2003-09-27 fl Added charmap encoding support
+ * 2004-05-15 fl Fixed compilation for FreeType 2.1.8
+ * 2004-09-10 fl Added support for monochrome bitmaps
+ * 2006-06-18 fl Fixed glyph bearing calculation
+ * 2007-12-23 fl Fixed crash in family/style attribute fetch
+ * 2008-01-02 fl Handle Unicode filenames properly
+ *
+ * Copyright (c) 1998-2007 by Secret Labs AB
+ */
+
+#include "Python.h"
+#include "Imaging.h"
+
+#if !defined(USE_FREETYPE_2_0)
+/* undef/comment out to use freetype 2.0 */
+#define USE_FREETYPE_2_1
+#endif
+
+#if defined(USE_FREETYPE_2_1)
+/* freetype 2.1 and newer */
+#include
+#include FT_FREETYPE_H
+#else
+/* freetype 2.0 */
+#include
+#endif
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#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
+
+#if !defined(FT_LOAD_TARGET_MONO)
+#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
+#endif
+
+/* -------------------------------------------------------------------- */
+/* error table */
+
+#undef FTERRORS_H
+#undef __FTERRORS_H__
+
+#define FT_ERRORDEF( e, v, s ) { e, s },
+#define FT_ERROR_START_LIST {
+#define FT_ERROR_END_LIST { 0, 0 } };
+
+struct {
+ int code;
+ const char* message;
+} ft_errors[] =
+
+#include
+
+/* -------------------------------------------------------------------- */
+/* font objects */
+
+static FT_Library library;
+
+typedef struct {
+ PyObject_HEAD
+ FT_Face face;
+} FontObject;
+
+staticforward PyTypeObject Font_Type;
+
+/* round a 26.6 pixel coordinate to the nearest larger integer */
+#define PIXEL(x) ((((x)+63) & -64)>>6)
+
+static PyObject*
+geterror(int code)
+{
+ int i;
+
+ for (i = 0; ft_errors[i].message; i++)
+ if (ft_errors[i].code == code) {
+ PyErr_SetString(PyExc_IOError, ft_errors[i].message);
+ return NULL;
+ }
+
+ PyErr_SetString(PyExc_IOError, "unknown freetype error");
+ return NULL;
+}
+
+static PyObject*
+getfont(PyObject* self_, PyObject* args, PyObject* kw)
+{
+ /* create a font object from a file name and a size (in pixels) */
+
+ FontObject* self;
+ int error;
+
+ char* filename;
+ int size;
+ int index = 0;
+ unsigned char* encoding = NULL;
+ static char* kwlist[] = {
+ "filename", "size", "index", "encoding", 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,
+ "failed to initialize FreeType library"
+ );
+ return NULL;
+ }
+
+ self = PyObject_New(FontObject, &Font_Type);
+ if (!self)
+ return NULL;
+
+ error = FT_New_Face(library, filename, index, &self->face);
+
+ if (!error)
+ error = FT_Set_Pixel_Sizes(self->face, 0, size);
+
+ if (!error && encoding && strlen((char*) encoding) == 4) {
+ FT_Encoding encoding_tag = FT_MAKE_TAG(
+ encoding[0], encoding[1], encoding[2], encoding[3]
+ );
+ error = FT_Select_Charmap(self->face, encoding_tag);
+ }
+
+ if (error) {
+ PyObject_Del(self);
+ return geterror(error);
+ }
+
+ 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);
+ if (index >= size)
+ return 0;
+ *char_out = p[index];
+ return 1;
+ }
+#endif
+ if (PyString_Check(string)) {
+ unsigned char* p = (unsigned char*) PyString_AS_STRING(string);
+ int size = PyString_GET_SIZE(string);
+ if (index >= size)
+ return 0;
+ *char_out = (unsigned char) p[index];
+ return 1;
+ }
+ return 0;
+}
+
+static PyObject*
+font_getsize(FontObject* self, PyObject* args)
+{
+ int i, x;
+ FT_ULong ch;
+ FT_Face face;
+ int xoffset;
+ FT_Bool kerning = FT_HAS_KERNING(self->face);
+ FT_UInt last_index = 0;
+
+ /* calculate size and bearing for a given string */
+
+ PyObject* string;
+ if (!PyArg_ParseTuple(args, "O:getsize", &string))
+ return NULL;
+
+#if defined(HAVE_UNICODE)
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#else
+ if (!PyString_Check(string)) {
+#endif
+ PyErr_SetString(PyExc_TypeError, "expected string");
+ return NULL;
+ }
+
+ face = NULL;
+ xoffset = 0;
+
+ for (x = i = 0; font_getchar(string, i, &ch); i++) {
+ int index, error;
+ face = self->face;
+ index = FT_Get_Char_Index(face, ch);
+ if (kerning && last_index && index) {
+ FT_Vector delta;
+ FT_Get_Kerning(self->face, last_index, index, ft_kerning_default,
+ &delta);
+ x += delta.x;
+ }
+ error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
+ if (error)
+ return geterror(error);
+ if (i == 0)
+ xoffset = face->glyph->metrics.horiBearingX;
+ x += face->glyph->metrics.horiAdvance;
+ last_index = index;
+ }
+
+ if (face) {
+ int offset;
+ /* left bearing */
+ if (xoffset < 0)
+ x -= xoffset;
+ else
+ xoffset = 0;
+ /* right bearing */
+ offset = face->glyph->metrics.horiAdvance -
+ face->glyph->metrics.width -
+ face->glyph->metrics.horiBearingX;
+ if (offset < 0)
+ x -= offset;
+ }
+
+ return Py_BuildValue(
+ "(ii)(ii)",
+ PIXEL(x), PIXEL(self->face->size->metrics.height),
+ PIXEL(xoffset), 0
+ );
+}
+
+static PyObject*
+font_getabc(FontObject* self, PyObject* args)
+{
+ FT_ULong ch;
+ FT_Face face;
+ double a, b, c;
+
+ /* calculate ABC values for a given string */
+
+ PyObject* string;
+ if (!PyArg_ParseTuple(args, "O:getabc", &string))
+ return NULL;
+
+#if defined(HAVE_UNICODE)
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#else
+ if (!PyString_Check(string)) {
+#endif
+ PyErr_SetString(PyExc_TypeError, "expected string");
+ return NULL;
+ }
+
+ if (font_getchar(string, 0, &ch)) {
+ int index, error;
+ face = self->face;
+ index = FT_Get_Char_Index(face, ch);
+ error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
+ if (error)
+ return geterror(error);
+ a = face->glyph->metrics.horiBearingX / 64.0;
+ b = face->glyph->metrics.width / 64.0;
+ c = (face->glyph->metrics.horiAdvance -
+ face->glyph->metrics.horiBearingX -
+ face->glyph->metrics.width) / 64.0;
+ } else
+ a = b = c = 0.0;
+
+ return Py_BuildValue("ddd", a, b, c);
+}
+
+static PyObject*
+font_render(FontObject* self, PyObject* args)
+{
+ int i, x, y;
+ Imaging im;
+ int index, error, ascender;
+ int load_flags;
+ unsigned char *source;
+ FT_ULong ch;
+ FT_GlyphSlot glyph;
+ FT_Bool kerning = FT_HAS_KERNING(self->face);
+ FT_UInt last_index = 0;
+
+ /* render string into given buffer (the buffer *must* have
+ the right size, or this will crash) */
+ PyObject* string;
+ long id;
+ int mask = 0;
+ if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask))
+ return NULL;
+
+#if defined(HAVE_UNICODE)
+ if (!PyUnicode_Check(string) && !PyString_Check(string)) {
+#else
+ if (!PyString_Check(string)) {
+#endif
+ PyErr_SetString(PyExc_TypeError, "expected string");
+ return NULL;
+ }
+
+ im = (Imaging) id;
+
+ load_flags = FT_LOAD_RENDER;
+ if (mask)
+ load_flags |= FT_LOAD_TARGET_MONO;
+
+ 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);
+ index = FT_Get_Char_Index(self->face, ch);
+ if (kerning && last_index && index) {
+ FT_Vector delta;
+ FT_Get_Kerning(self->face, last_index, index, ft_kerning_default,
+ &delta);
+ x += delta.x >> 6;
+ }
+ error = FT_Load_Glyph(self->face, index, load_flags);
+ if (error)
+ return geterror(error);
+ glyph = self->face->glyph;
+ 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;
+ if (yy >= 0 && yy < im->ysize) {
+ /* blend this glyph into the buffer */
+ unsigned char *target = im->image8[yy] + xx;
+ int i, j, m = 128;
+ for (i = j = 0; j < x1; j++) {
+ if (j >= x0 && (source[i] & m))
+ target[j] = 255;
+ if (!(m >>= 1)) {
+ m = 128;
+ i++;
+ }
+ }
+ }
+ source += glyph->bitmap.pitch;
+ }
+ } 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;
+ if (yy >= 0 && yy < im->ysize) {
+ /* blend this glyph into the buffer */
+ int i;
+ unsigned char *target = im->image8[yy] + xx;
+ for (i = x0; i < x1; i++) {
+ if (target[i] < source[i])
+ target[i] = source[i];
+ }
+ }
+ source += glyph->bitmap.pitch;
+ }
+ }
+ x += PIXEL(glyph->metrics.horiAdvance);
+ last_index = index;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static void
+font_dealloc(FontObject* self)
+{
+ FT_Done_Face(self->face);
+ PyObject_Del(self);
+}
+
+static PyMethodDef font_methods[] = {
+ {"render", (PyCFunction) font_render, METH_VARARGS},
+ {"getsize", (PyCFunction) font_getsize, METH_VARARGS},
+ {"getabc", (PyCFunction) font_getabc, METH_VARARGS},
+ {NULL, NULL}
+};
+
+static PyObject*
+font_getattr(FontObject* self, char* name)
+{
+ 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;
+}
+
+statichere PyTypeObject Font_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0, "Font", sizeof(FontObject), 0,
+ /* methods */
+ (destructor)font_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ (getattrfunc)font_getattr, /* tp_getattr */
+};
+
+static PyMethodDef _functions[] = {
+ {"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS},
+ {NULL, NULL}
+};
+
+DL_EXPORT(void)
+init_imagingft(void)
+{
+ 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);
+
+ if (FT_Init_FreeType(&library))
+ return; /* leave it uninitalized */
+
+ FT_Library_Version(library, &major, &minor, &patch);
+
+#if PY_VERSION_HEX >= 0x02020000
+ v = PyString_FromFormat("%d.%d.%d", major, minor, patch);
+#else
+ {
+ char buffer[100];
+ sprintf(buffer, "%d.%d.%d", major, minor, patch);
+ v = PyString_FromString(buffer);
+ }
+#endif
+ PyDict_SetItemString(d, "freetype2_version", v);
+}
diff --git a/_imagingmath.c b/_imagingmath.c
new file mode 100644
index 000000000..928986bb3
--- /dev/null
+++ b/_imagingmath.c
@@ -0,0 +1,284 @@
+/*
+ * The Python Imaging Library
+ *
+ * a simple math add-on for the Python Imaging Library
+ *
+ * history:
+ * 1999-02-15 fl Created
+ * 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6
+ *
+ * Copyright (c) 1999-2005 by Secret Labs AB
+ * Copyright (c) 2005 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Python.h"
+
+#include "Imaging.h"
+
+#include "math.h"
+#include "float.h"
+
+#define MAX_INT32 2147483647.0
+#define MIN_INT32 -2147483648.0
+
+#if defined(_MSC_VER) && _MSC_VER < 1500
+/* python 2.1/2.2/2.3 = VC98 = VER 1200 */
+/* python 2.4/2.5 = VS.NET 2003 = VER 1310 */
+/* python 2.6 = VS 9.0 = VER 1500 */
+#define powf(a, b) ((float) pow((double) (a), (double) (b)))
+#endif
+
+#define UNOP(name, op, type)\
+void name(Imaging out, Imaging im1)\
+{\
+ int x, y;\
+ for (y = 0; y < out->ysize; y++) {\
+ type* p0 = (type*) out->image[y];\
+ type* p1 = (type*) im1->image[y];\
+ for (x = 0; x < out->xsize; x++) {\
+ *p0 = op(type, *p1);\
+ p0++; p1++;\
+ }\
+ }\
+}
+
+#define BINOP(name, op, type)\
+void name(Imaging out, Imaging im1, Imaging im2)\
+{\
+ int x, y;\
+ for (y = 0; y < out->ysize; y++) {\
+ type* p0 = (type*) out->image[y];\
+ type* p1 = (type*) im1->image[y];\
+ type* p2 = (type*) im2->image[y];\
+ for (x = 0; x < out->xsize; x++) {\
+ *p0 = op(type, *p1, *p2);\
+ p0++; p1++; p2++;\
+ }\
+ }\
+}
+
+#define NEG(type, v1) -(v1)
+#define INVERT(type, v1) ~(v1)
+
+#define ADD(type, v1, v2) (v1)+(v2)
+#define SUB(type, v1, v2) (v1)-(v2)
+#define MUL(type, v1, v2) (v1)*(v2)
+
+#define MIN(type, v1, v2) ((v1)<(v2))?(v1):(v2)
+#define MAX(type, v1, v2) ((v1)>(v2))?(v1):(v2)
+
+#define AND(type, v1, v2) (v1)&(v2)
+#define OR(type, v1, v2) (v1)|(v2)
+#define XOR(type, v1, v2) (v1)^(v2)
+#define LSHIFT(type, v1, v2) (v1)<<(v2)
+#define RSHIFT(type, v1, v2) (v1)>>(v2)
+
+#define ABS_I(type, v1) abs((v1))
+#define ABS_F(type, v1) fabs((v1))
+
+/* --------------------------------------------------------------------
+ * 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)
+ */
+
+#define DIV_I(type, v1, v2) ((v2)!=0)?(v1)/(v2):0
+#define DIV_F(type, v1, v2) ((v2)!=0.0F)?(v1)/(v2):0.0F
+
+#define MOD_I(type, v1, v2) ((v2)!=0)?(v1)%(v2):0
+#define MOD_F(type, v1, v2) ((v2)!=0.0F)?fmod((v1),(v2)):0.0F
+
+static int powi(int x, int y)
+{
+ double v = pow(x, y) + 0.5;
+ if (errno == EDOM)
+ return 0;
+ if (v < MIN_INT32)
+ v = MIN_INT32;
+ else if (v > MAX_INT32)
+ v = MAX_INT32;
+ return (int) v;
+}
+
+#define POW_I(type, v1, v2) powi(v1, v2)
+#define POW_F(type, v1, v2) powf(v1, v2) /* FIXME: EDOM handling */
+
+#define DIFF_I(type, v1, v2) abs((v1)-(v2))
+#define DIFF_F(type, v1, v2) fabs((v1)-(v2))
+
+#define EQ(type, v1, v2) (v1)==(v2)
+#define NE(type, v1, v2) (v1)!=(v2)
+#define LT(type, v1, v2) (v1)<(v2)
+#define LE(type, v1, v2) (v1)<=(v2)
+#define GT(type, v1, v2) (v1)>(v2)
+#define GE(type, v1, v2) (v1)>=(v2)
+
+UNOP(abs_I, ABS_I, INT32)
+UNOP(neg_I, NEG, INT32)
+
+BINOP(add_I, ADD, INT32)
+BINOP(sub_I, SUB, INT32)
+BINOP(mul_I, MUL, INT32)
+BINOP(div_I, DIV_I, INT32)
+BINOP(mod_I, MOD_I, INT32)
+BINOP(pow_I, POW_I, INT32)
+BINOP(diff_I, DIFF_I, INT32)
+
+UNOP(invert_I, INVERT, INT32)
+BINOP(and_I, AND, INT32)
+BINOP(or_I, OR, INT32)
+BINOP(xor_I, XOR, INT32)
+BINOP(lshift_I, LSHIFT, INT32)
+BINOP(rshift_I, RSHIFT, INT32)
+
+BINOP(min_I, MIN, INT32)
+BINOP(max_I, MAX, INT32)
+
+BINOP(eq_I, EQ, INT32)
+BINOP(ne_I, NE, INT32)
+BINOP(lt_I, LT, INT32)
+BINOP(le_I, LE, INT32)
+BINOP(gt_I, GT, INT32)
+BINOP(ge_I, GE, INT32)
+
+UNOP(abs_F, ABS_F, FLOAT32)
+UNOP(neg_F, NEG, FLOAT32)
+
+BINOP(add_F, ADD, FLOAT32)
+BINOP(sub_F, SUB, FLOAT32)
+BINOP(mul_F, MUL, FLOAT32)
+BINOP(div_F, DIV_F, FLOAT32)
+BINOP(mod_F, MOD_F, FLOAT32)
+BINOP(pow_F, POW_F, FLOAT32)
+BINOP(diff_F, DIFF_F, FLOAT32)
+
+BINOP(min_F, MIN, FLOAT32)
+BINOP(max_F, MAX, FLOAT32)
+
+BINOP(eq_F, EQ, FLOAT32)
+BINOP(ne_F, NE, FLOAT32)
+BINOP(lt_F, LT, FLOAT32)
+BINOP(le_F, LE, FLOAT32)
+BINOP(gt_F, GT, FLOAT32)
+BINOP(ge_F, GE, FLOAT32)
+
+static PyObject *
+_unop(PyObject* self, PyObject* args)
+{
+ Imaging out;
+ Imaging im1;
+ void (*unop)(Imaging, Imaging);
+
+ long op, i0, i1;
+ if (!PyArg_ParseTuple(args, "lll", &op, &i0, &i1))
+ return NULL;
+
+ out = (Imaging) i0;
+ im1 = (Imaging) i1;
+
+ unop = (void*) op;
+
+ unop(out, im1);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+_binop(PyObject* self, PyObject* args)
+{
+ Imaging out;
+ Imaging im1;
+ Imaging im2;
+ void (*binop)(Imaging, Imaging, Imaging);
+
+ long op, i0, i1, i2;
+ if (!PyArg_ParseTuple(args, "llll", &op, &i0, &i1, &i2))
+ return NULL;
+
+ out = (Imaging) i0;
+ im1 = (Imaging) i1;
+ im2 = (Imaging) i2;
+
+ binop = (void*) op;
+
+ binop(out, im1, im2);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef _functions[] = {
+ {"unop", _unop, 1},
+ {"binop", _binop, 1},
+ {NULL, NULL}
+};
+
+static void
+install(PyObject *d, char* name, void* value)
+{
+ PyObject *v = PyInt_FromLong((long) 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);
+
+ install(d, "abs_I", abs_I);
+ install(d, "neg_I", neg_I);
+ install(d, "add_I", add_I);
+ install(d, "sub_I", sub_I);
+ install(d, "diff_I", diff_I);
+ install(d, "mul_I", mul_I);
+ install(d, "div_I", div_I);
+ install(d, "mod_I", mod_I);
+ install(d, "min_I", min_I);
+ install(d, "max_I", max_I);
+ install(d, "pow_I", pow_I);
+
+ install(d, "invert_I", invert_I);
+ install(d, "and_I", and_I);
+ install(d, "or_I", or_I);
+ install(d, "xor_I", xor_I);
+ install(d, "lshift_I", lshift_I);
+ install(d, "rshift_I", rshift_I);
+
+ install(d, "eq_I", eq_I);
+ install(d, "ne_I", ne_I);
+ install(d, "lt_I", lt_I);
+ install(d, "le_I", le_I);
+ install(d, "gt_I", gt_I);
+ install(d, "ge_I", ge_I);
+
+ install(d, "abs_F", abs_F);
+ install(d, "neg_F", neg_F);
+ install(d, "add_F", add_F);
+ install(d, "sub_F", sub_F);
+ install(d, "diff_F", diff_F);
+ install(d, "mul_F", mul_F);
+ install(d, "div_F", div_F);
+ install(d, "mod_F", mod_F);
+ install(d, "min_F", min_F);
+ install(d, "max_F", max_F);
+ install(d, "pow_F", pow_F);
+
+ install(d, "eq_F", eq_F);
+ install(d, "ne_F", ne_F);
+ install(d, "lt_F", lt_F);
+ install(d, "le_F", le_F);
+ install(d, "gt_F", gt_F);
+ install(d, "ge_F", ge_F);
+
+}
diff --git a/_imagingtk.c b/_imagingtk.c
new file mode 100644
index 000000000..6165a2acb
--- /dev/null
+++ b/_imagingtk.c
@@ -0,0 +1,70 @@
+/*
+ * The Python Imaging Library.
+ *
+ * tkinter hooks
+ *
+ * history:
+ * 99-07-26 fl created
+ * 99-08-15 fl moved to its own support module
+ *
+ * Copyright (c) Secret Labs AB 1999.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Python.h"
+#include "Imaging.h"
+
+#include "tk.h"
+
+/* must link with Tk/tkImaging.c */
+extern void TkImaging_Init(Tcl_Interp* interp);
+
+/* copied from _tkinter.c (this isn't as bad as it may seem: for new
+ versions, we use _tkinter's interpaddr hook instead, and all older
+ versions use this structure layout) */
+
+typedef struct {
+ PyObject_HEAD
+ Tcl_Interp* interp;
+} TkappObject;
+
+static PyObject*
+_tkinit(PyObject* self, PyObject* args)
+{
+ Tcl_Interp* interp;
+
+ long arg;
+ int is_interp;
+ if (!PyArg_ParseTuple(args, "li", &arg, &is_interp))
+ return NULL;
+
+ if (is_interp)
+ interp = (Tcl_Interp*) arg;
+ else {
+ TkappObject* app;
+ /* Do it the hard way. This will break if the TkappObject
+ layout changes */
+ app = (TkappObject*) arg;
+ interp = app->interp;
+ }
+
+ /* This will bomb if interp is invalid... */
+ TkImaging_Init(interp);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef functions[] = {
+ /* Tkinter interface stuff */
+ {"tkinit", (PyCFunction)_tkinit, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+DL_EXPORT(void)
+init_imagingtk(void)
+{
+ Py_InitModule("_imagingtk", functions);
+}
diff --git a/decode.c b/decode.c
new file mode 100644
index 000000000..6ea8d9c3f
--- /dev/null
+++ b/decode.c
@@ -0,0 +1,689 @@
+/*
+ * The Python Imaging Library.
+ *
+ * standard decoder interfaces for the Imaging library
+ *
+ * history:
+ * 1996-03-28 fl Moved from _imagingmodule.c
+ * 1996-04-15 fl Support subregions in setimage
+ * 1996-04-19 fl Allocate decoder buffer (where appropriate)
+ * 1996-05-02 fl Added jpeg decoder
+ * 1996-05-12 fl Compile cleanly as C++
+ * 1996-05-16 fl Added hex decoder
+ * 1996-05-26 fl Added jpeg configuration parameters
+ * 1996-12-14 fl Added zip decoder
+ * 1996-12-30 fl Plugged potential memory leak for tiled images
+ * 1997-01-03 fl Added fli and msp decoders
+ * 1997-01-04 fl Added sun_rle and tga_rle decoders
+ * 1997-05-31 fl Added bitfield decoder
+ * 1998-09-11 fl Added orientation and pixelsize fields to tga_rle decoder
+ * 1998-12-29 fl Added mode/rawmode argument to decoders
+ * 1998-12-30 fl Added mode argument to *all* decoders
+ * 2002-06-09 fl Added stride argument to pcx decoder
+ *
+ * Copyright (c) 1997-2002 by Secret Labs AB.
+ * Copyright (c) 1995-2002 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+/* FIXME: make these pluggable! */
+
+#include "Python.h"
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#include "Imaging.h"
+
+#include "Gif.h"
+#include "Lzw.h"
+#include "Raw.h"
+#include "Bit.h"
+
+
+/* -------------------------------------------------------------------- */
+/* Common */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ int (*decode)(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+ struct ImagingCodecStateInstance state;
+ Imaging im;
+ PyObject* lock;
+} ImagingDecoderObject;
+
+staticforward PyTypeObject ImagingDecoderType;
+
+static ImagingDecoderObject*
+PyImaging_DecoderNew(int contextsize)
+{
+ ImagingDecoderObject *decoder;
+ void *context;
+
+ ImagingDecoderType.ob_type = &PyType_Type;
+
+ decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType);
+ if (decoder == 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;
+ }
+ } else
+ context = 0;
+
+ /* Initialize decoder context */
+ decoder->state.context = context;
+
+ /* Target image */
+ decoder->lock = NULL;
+ decoder->im = NULL;
+
+ return decoder;
+}
+
+static void
+_dealloc(ImagingDecoderObject* decoder)
+{
+ free(decoder->state.buffer);
+ free(decoder->state.context);
+ Py_XDECREF(decoder->lock);
+ PyObject_Del(decoder);
+}
+
+static PyObject*
+_decode(ImagingDecoderObject* decoder, PyObject* args)
+{
+ UINT8* buffer;
+ int bufsize, status;
+
+ if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize))
+ return NULL;
+
+ status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize);
+
+ return Py_BuildValue("ii", status, decoder->state.errcode);
+}
+
+extern Imaging PyImaging_AsImaging(PyObject *op);
+
+static PyObject*
+_setimage(ImagingDecoderObject* decoder, PyObject* args)
+{
+ PyObject* op;
+ Imaging im;
+ ImagingCodecState state;
+ int x0, y0, x1, y1;
+
+ x0 = y0 = x1 = y1 = 0;
+
+ /* FIXME: should publish the ImagingType descriptor */
+ if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1))
+ return NULL;
+ im = PyImaging_AsImaging(op);
+ if (!im)
+ return NULL;
+
+ decoder->im = im;
+
+ state = &decoder->state;
+
+ /* Setup decoding tile extent */
+ if (x0 == 0 && x1 == 0) {
+ state->xsize = im->xsize;
+ state->ysize = im->ysize;
+ } else {
+ 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;
+ }
+
+ /* 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();
+ }
+
+ /* Keep a reference to the image object, to make sure it doesn't
+ go away before we do */
+ Py_INCREF(op);
+ Py_XDECREF(decoder->lock);
+ decoder->lock = op;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef methods[] = {
+ {"decode", (PyCFunction)_decode, 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*/
+};
+
+/* -------------------------------------------------------------------- */
+
+int
+get_unpacker(ImagingDecoderObject* decoder, const char* mode,
+ const char* rawmode)
+{
+ int bits;
+ ImagingShuffler unpack;
+
+ unpack = ImagingFindUnpacker(mode, rawmode, &bits);
+ if (!unpack) {
+ Py_DECREF(decoder);
+ PyErr_SetString(PyExc_ValueError, "unknown raw mode");
+ return -1;
+ }
+
+ decoder->state.shuffle = unpack;
+ decoder->state.bits = bits;
+
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* BIT (packed fields) */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_BitDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ int bits = 8;
+ int pad = 8;
+ int fill = 0;
+ int sign = 0;
+ int ystep = 1;
+ if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill,
+ &sign, &ystep))
+ return NULL;
+
+ if (strcmp(mode, "F") != 0) {
+ PyErr_SetString(PyExc_ValueError, "bad image mode");
+ return NULL;
+ }
+
+ decoder = PyImaging_DecoderNew(sizeof(BITSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ decoder->decode = ImagingBitDecode;
+
+ decoder->state.ystep = ystep;
+
+ ((BITSTATE*)decoder->state.context)->bits = bits;
+ ((BITSTATE*)decoder->state.context)->pad = pad;
+ ((BITSTATE*)decoder->state.context)->fill = fill;
+ ((BITSTATE*)decoder->state.context)->sign = sign;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* FLI */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_FliDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ decoder->decode = ImagingFliDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* GIF */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_GifDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ int bits = 8;
+ int interlace = 0;
+ if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace))
+ return NULL;
+
+ if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) {
+ PyErr_SetString(PyExc_ValueError, "bad image mode");
+ return NULL;
+ }
+
+ decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ decoder->decode = ImagingGifDecode;
+
+ ((GIFDECODERSTATE*)decoder->state.context)->bits = bits;
+ ((GIFDECODERSTATE*)decoder->state.context)->interlace = interlace;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* HEX */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_HexDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingHexDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* LZW */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ int filter = 0;
+ if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &filter))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(sizeof(LZWSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingLzwDecode;
+
+ ((LZWSTATE*)decoder->state.context)->filter = filter;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* MSP */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_MspDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, "1", "1") < 0)
+ return NULL;
+
+ decoder->decode = ImagingMspDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* PackBits */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingPackbitsDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* PCD */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_PcdDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ /* Unpack from PhotoYCC to RGB */
+ if (get_unpacker(decoder, "RGB", "YCC;P") < 0)
+ return NULL;
+
+ decoder->decode = ImagingPcdDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* PCX */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_PcxDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ int stride;
+ if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->state.bytes = stride;
+
+ decoder->decode = ImagingPcxDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* RAW */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_RawDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ int stride = 0;
+ int ystep = 1;
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(sizeof(RAWSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingRawDecode;
+
+ decoder->state.ystep = ystep;
+
+ ((RAWSTATE*)decoder->state.context)->stride = stride;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* SUN RLE */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingSunRleDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* TGA RLE */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ int ystep = 1;
+ int depth = 8;
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingTgaRleDecode;
+
+ decoder->state.ystep = ystep;
+ decoder->state.count = depth / 8;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* XBM */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_XbmDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ decoder = PyImaging_DecoderNew(0);
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, "1", "1;R") < 0)
+ return NULL;
+
+ decoder->decode = ImagingXbmDecode;
+
+ return (PyObject*) decoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* ZIP */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBZ
+
+#include "Zip.h"
+
+PyObject*
+PyImaging_ZipDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode;
+ int interlaced = 0;
+ if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced))
+ return NULL;
+
+ decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingZipDecode;
+
+ ((ZIPSTATE*)decoder->state.context)->interlaced = interlaced;
+
+ return (PyObject*) decoder;
+}
+#endif
+
+
+/* -------------------------------------------------------------------- */
+/* JPEG */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBJPEG
+
+/* 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
+
+#include "Jpeg.h"
+
+PyObject*
+PyImaging_JpegDecoderNew(PyObject* self, PyObject* args)
+{
+ ImagingDecoderObject* decoder;
+
+ char* mode;
+ char* rawmode; /* what we wan't from the decoder */
+ char* jpegmode; /* what's in the file */
+ int scale = 1;
+ int draft = 0;
+ if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode,
+ &scale, &draft))
+ return NULL;
+
+ if (!jpegmode)
+ jpegmode = "";
+
+ decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE));
+ if (decoder == NULL)
+ return NULL;
+
+ if (get_unpacker(decoder, mode, rawmode) < 0)
+ return NULL;
+
+ decoder->decode = ImagingJpegDecode;
+
+ strncpy(((JPEGSTATE*)decoder->state.context)->rawmode, rawmode, 8);
+ strncpy(((JPEGSTATE*)decoder->state.context)->jpegmode, jpegmode, 8);
+
+ ((JPEGSTATE*)decoder->state.context)->scale = scale;
+ ((JPEGSTATE*)decoder->state.context)->draft = draft;
+
+ return (PyObject*) decoder;
+}
+#endif
diff --git a/display.c b/display.c
new file mode 100644
index 000000000..1a08fadd5
--- /dev/null
+++ b/display.c
@@ -0,0 +1,836 @@
+/*
+ * The Python Imaging Library.
+ *
+ * display support (and other windows-related stuff)
+ *
+ * History:
+ * 1996-05-13 fl Windows DIB support
+ * 1996-05-21 fl Added palette stuff
+ * 1996-05-28 fl Added display_mode stuff
+ * 1997-09-21 fl Added draw primitive
+ * 2001-09-17 fl Added ImagingGrabScreen (from _grabscreen.c)
+ * 2002-05-12 fl Added ImagingListWindows
+ * 2002-11-19 fl Added clipboard support
+ * 2002-11-25 fl Added GetDC/ReleaseDC helpers
+ * 2003-05-21 fl Added create window support (including window callback)
+ * 2003-09-05 fl Added fromstring/tostring methods
+ * 2009-03-14 fl Added WMF support (from pilwmf)
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB.
+ * Copyright (c) 1996-1997 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Python.h"
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#include "Imaging.h"
+
+/* -------------------------------------------------------------------- */
+/* Windows DIB support */
+
+#ifdef WIN32
+
+#include "ImDib.h"
+
+typedef struct {
+ PyObject_HEAD
+ ImagingDIB dib;
+} ImagingDisplayObject;
+
+staticforward PyTypeObject ImagingDisplayType;
+
+static ImagingDisplayObject*
+_new(const char* mode, int xsize, int ysize)
+{
+ ImagingDisplayObject *display;
+
+ display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType);
+ if (display == NULL)
+ return NULL;
+
+ display->dib = ImagingNewDIB(mode, xsize, ysize);
+ if (!display->dib) {
+ Py_DECREF(display);
+ return NULL;
+ }
+
+ return display;
+}
+
+static void
+_delete(ImagingDisplayObject* display)
+{
+ if (display->dib)
+ ImagingDeleteDIB(display->dib);
+ PyObject_Del(display);
+}
+
+static PyObject*
+_expose(ImagingDisplayObject* display, PyObject* args)
+{
+ int hdc;
+ if (!PyArg_ParseTuple(args, "i", &hdc))
+ return NULL;
+
+ ImagingExposeDIB(display->dib, hdc);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_draw(ImagingDisplayObject* display, PyObject* args)
+{
+ int hdc;
+ int dst[4];
+ int src[4];
+ if (!PyArg_ParseTuple(args, "i(iiii)(iiii)", &hdc,
+ dst+0, dst+1, dst+2, dst+3,
+ src+0, src+1, src+2, src+3))
+ return NULL;
+
+ ImagingDrawDIB(display->dib, hdc, dst, src);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+extern Imaging PyImaging_AsImaging(PyObject *op);
+
+static PyObject*
+_paste(ImagingDisplayObject* display, PyObject* args)
+{
+ Imaging im;
+
+ PyObject* op;
+ int xy[4];
+ xy[0] = xy[1] = xy[2] = xy[3] = 0;
+ if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3))
+ return NULL;
+ im = PyImaging_AsImaging(op);
+ if (!im)
+ return NULL;
+
+ if (xy[2] <= xy[0])
+ xy[2] = xy[0] + im->xsize;
+ if (xy[3] <= xy[1])
+ xy[3] = xy[1] + im->ysize;
+
+ ImagingPasteDIB(display->dib, im, xy);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_query_palette(ImagingDisplayObject* display, PyObject* args)
+{
+ int hdc;
+ int status;
+
+ if (!PyArg_ParseTuple(args, "i", &hdc))
+ return NULL;
+
+ status = ImagingQueryPaletteDIB(display->dib, hdc);
+
+ return Py_BuildValue("i", status);
+}
+
+static PyObject*
+_getdc(ImagingDisplayObject* display, PyObject* args)
+{
+ int window;
+ HDC dc;
+
+ if (!PyArg_ParseTuple(args, "i", &window))
+ return NULL;
+
+ dc = GetDC((HWND) window);
+ if (!dc) {
+ PyErr_SetString(PyExc_IOError, "cannot create dc");
+ return NULL;
+ }
+
+ return Py_BuildValue("i", (int) dc);
+}
+
+static PyObject*
+_releasedc(ImagingDisplayObject* display, PyObject* args)
+{
+ int window, dc;
+
+ if (!PyArg_ParseTuple(args, "ii", &window, &dc))
+ return NULL;
+
+ ReleaseDC((HWND) window, (HDC) dc);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_fromstring(ImagingDisplayObject* display, PyObject* args)
+{
+ char* ptr;
+ int bytes;
+ if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes))
+ return NULL;
+
+ if (display->dib->ysize * display->dib->linesize != bytes) {
+ PyErr_SetString(PyExc_ValueError, "wrong size");
+ return NULL;
+ }
+
+ memcpy(display->dib->bits, ptr, bytes);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_tostring(ImagingDisplayObject* display, PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ":tostring"))
+ return NULL;
+
+ return PyString_FromStringAndSize(
+ display->dib->bits, display->dib->ysize * display->dib->linesize
+ );
+}
+
+static struct PyMethodDef methods[] = {
+ {"draw", (PyCFunction)_draw, 1},
+ {"expose", (PyCFunction)_expose, 1},
+ {"paste", (PyCFunction)_paste, 1},
+ {"query_palette", (PyCFunction)_query_palette, 1},
+ {"getdc", (PyCFunction)_getdc, 1},
+ {"releasedc", (PyCFunction)_releasedc, 1},
+ {"fromstring", (PyCFunction)_fromstring, 1},
+ {"tostring", (PyCFunction)_tostring, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+_getattr(ImagingDisplayObject* self, char* name)
+{
+ 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*/
+ "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*/
+};
+
+PyObject*
+PyImaging_DisplayWin32(PyObject* self, PyObject* args)
+{
+ ImagingDisplayObject* display;
+ char *mode;
+ int xsize, ysize;
+
+ if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize))
+ return NULL;
+
+ display = _new(mode, xsize, ysize);
+ if (display == NULL)
+ return NULL;
+
+ return (PyObject*) display;
+}
+
+PyObject*
+PyImaging_DisplayModeWin32(PyObject* self, PyObject* args)
+{
+ char *mode;
+ int size[2];
+
+ mode = ImagingGetModeDIB(size);
+
+ return Py_BuildValue("s(ii)", mode, size[0], size[1]);
+}
+
+/* -------------------------------------------------------------------- */
+/* Windows screen grabber */
+
+PyObject*
+PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
+{
+ int width, height;
+ HBITMAP bitmap;
+ 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);
+
+ width = GetDeviceCaps(screen, HORZRES);
+ height = GetDeviceCaps(screen, VERTRES);
+
+ bitmap = CreateCompatibleBitmap(screen, width, height);
+ if (!bitmap)
+ goto error;
+
+ if (!SelectObject(screen_copy, bitmap))
+ goto error;
+
+ /* step 2: copy bits into memory DC bitmap */
+
+ if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, SRCCOPY))
+ goto error;
+
+ /* step 3: extract bits from bitmap */
+
+ buffer = PyString_FromStringAndSize(NULL, height * ((width*3 + 3) & -4));
+ if (!buffer)
+ return NULL;
+
+ core.bcSize = sizeof(core);
+ core.bcWidth = width;
+ core.bcHeight = height;
+ core.bcPlanes = 1;
+ core.bcBitCount = 24;
+ if (!GetDIBits(screen_copy, bitmap, 0, height, PyString_AS_STRING(buffer),
+ (BITMAPINFO*) &core, DIB_RGB_COLORS))
+ goto error;
+
+ DeleteObject(bitmap);
+ DeleteDC(screen_copy);
+ DeleteDC(screen);
+
+ return Py_BuildValue("(ii)N", width, height, buffer);
+
+error:
+ PyErr_SetString(PyExc_IOError, "screen grab failed");
+
+ DeleteDC(screen_copy);
+ DeleteDC(screen);
+
+ return NULL;
+}
+
+static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam)
+{
+ PyObject* window_list = (PyObject*) lParam;
+ PyObject* item;
+ PyObject* title;
+ 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);
+ if (title)
+ GetWindowText(hwnd, PyString_AS_STRING(title), title_size+1);
+ } else
+ title = PyString_FromString("");
+ if (!title)
+ return 0;
+
+ /* get bounding boxes */
+ GetClientRect(hwnd, &inner);
+ GetWindowRect(hwnd, &outer);
+
+ item = Py_BuildValue(
+ "lN(iiii)(iiii)", (long) hwnd, title,
+ inner.left, inner.top, inner.right, inner.bottom,
+ outer.left, outer.top, outer.right, outer.bottom
+ );
+ if (!item)
+ return 0;
+
+ status = PyList_Append(window_list, item);
+
+ Py_DECREF(item);
+
+ if (status < 0)
+ return 0;
+
+ return 1;
+}
+
+PyObject*
+PyImaging_ListWindowsWin32(PyObject* self, PyObject* args)
+{
+ PyObject* window_list;
+
+ window_list = PyList_New(0);
+ if (!window_list)
+ return NULL;
+
+ EnumWindows(list_windows_callback, (LPARAM) window_list);
+
+ if (PyErr_Occurred()) {
+ Py_DECREF(window_list);
+ return NULL;
+ }
+
+ return window_list;
+}
+
+/* -------------------------------------------------------------------- */
+/* Windows clipboard grabber */
+
+PyObject*
+PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args)
+{
+ int clip;
+ HANDLE handle;
+ int size;
+ void* data;
+ PyObject* result;
+
+ int verbose = 0; /* debugging; will be removed in future versions */
+ if (!PyArg_ParseTuple(args, "|i", &verbose))
+ return NULL;
+
+
+ clip = OpenClipboard(NULL);
+ /* FIXME: check error status */
+
+ if (verbose) {
+ UINT format = EnumClipboardFormats(0);
+ char buffer[200];
+ char* result;
+ while (format != 0) {
+ if (GetClipboardFormatName(format, buffer, sizeof buffer) > 0)
+ result = buffer;
+ else
+ switch (format) {
+ case CF_BITMAP:
+ result = "CF_BITMAP";
+ break;
+ case CF_DIB:
+ result = "CF_DIB";
+ break;
+ case CF_DIF:
+ result = "CF_DIF";
+ break;
+ case CF_ENHMETAFILE:
+ result = "CF_ENHMETAFILE";
+ break;
+ case CF_HDROP:
+ result = "CF_HDROP";
+ break;
+ case CF_LOCALE:
+ result = "CF_LOCALE";
+ break;
+ case CF_METAFILEPICT:
+ result = "CF_METAFILEPICT";
+ break;
+ case CF_OEMTEXT:
+ result = "CF_OEMTEXT";
+ break;
+ case CF_OWNERDISPLAY:
+ result = "CF_OWNERDISPLAY";
+ break;
+ case CF_PALETTE:
+ result = "CF_PALETTE";
+ break;
+ case CF_PENDATA:
+ result = "CF_PENDATA";
+ break;
+ case CF_RIFF:
+ result = "CF_RIFF";
+ break;
+ case CF_SYLK:
+ result = "CF_SYLK";
+ break;
+ case CF_TEXT:
+ result = "CF_TEXT";
+ break;
+ case CF_WAVE:
+ result = "CF_WAVE";
+ break;
+ case CF_TIFF:
+ result = "CF_TIFF";
+ break;
+ case CF_UNICODETEXT:
+ result = "CF_UNICODETEXT";
+ break;
+ default:
+ sprintf(buffer, "[%d]", format);
+ result = buffer;
+ break;
+ }
+ printf("%s (%d)\n", result, format);
+ format = EnumClipboardFormats(format);
+ }
+ }
+
+ handle = GetClipboardData(CF_DIB);
+ if (!handle) {
+ /* FIXME: add CF_HDROP support to allow cut-and-paste from
+ the explorer */
+ CloseClipboard();
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ size = GlobalSize(handle);
+ data = GlobalLock(handle);
+
+#if 0
+ /* calculate proper size for string formats */
+ if (format == CF_TEXT || format == CF_OEMTEXT)
+ size = strlen(data);
+ else if (format == CF_UNICODETEXT)
+ size = wcslen(data) * 2;
+#endif
+
+ result = PyString_FromStringAndSize(data, size);
+
+ GlobalUnlock(handle);
+
+ CloseClipboard();
+
+ return result;
+}
+
+/* -------------------------------------------------------------------- */
+/* Windows class */
+
+#ifndef WM_MOUSEWHEEL
+#define WM_MOUSEWHEEL 522
+#endif
+
+static int mainloop = 0;
+
+static void
+callback_error(const char* handler)
+{
+ PyObject* sys_stderr;
+
+ sys_stderr = PySys_GetObject("stderr");
+
+ if (sys_stderr) {
+ PyFile_WriteString("*** ImageWin: error in ", sys_stderr);
+ PyFile_WriteString((char*) handler, sys_stderr);
+ PyFile_WriteString(":\n", sys_stderr);
+ }
+
+ PyErr_Print();
+ PyErr_Clear();
+}
+
+static LRESULT CALLBACK
+windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ PyObject* callback = NULL;
+ PyObject* result;
+ PyThreadState* threadstate;
+ PyThreadState* current_threadstate;
+ HDC dc;
+ RECT rect;
+ LRESULT status = 0;
+
+ /* set up threadstate for messages that calls back into python */
+ switch (message) {
+ case WM_CREATE:
+ mainloop++;
+ break;
+ case WM_DESTROY:
+ mainloop--;
+ /* fall through... */
+ case WM_PAINT:
+ case WM_SIZE:
+ callback = (PyObject*) GetWindowLong(wnd, 0);
+ if (callback) {
+ threadstate = (PyThreadState*)
+ GetWindowLong(wnd, sizeof(PyObject*));
+ current_threadstate = PyThreadState_Swap(NULL);
+ PyEval_RestoreThread(threadstate);
+ } else
+ return DefWindowProc(wnd, message, wParam, lParam);
+ }
+
+ /* process message */
+ switch (message) {
+
+ case WM_PAINT:
+ /* redraw (part of) window. this generates a WCK-style
+ damage/clear/repair cascade */
+ BeginPaint(wnd, &ps);
+ dc = GetDC(wnd);
+ GetWindowRect(wnd, &rect); /* in screen coordinates */
+
+ result = PyObject_CallFunction(
+ callback, "siiii", "damage",
+ ps.rcPaint.left, ps.rcPaint.top,
+ ps.rcPaint.right, ps.rcPaint.bottom
+ );
+ if (result)
+ Py_DECREF(result);
+ else
+ callback_error("window damage callback");
+
+ result = PyObject_CallFunction(
+ callback, "siiiii", "clear", (int) dc,
+ 0, 0, rect.right-rect.left, rect.bottom-rect.top
+ );
+ if (result)
+ Py_DECREF(result);
+ else
+ callback_error("window clear callback");
+
+ result = PyObject_CallFunction(
+ callback, "siiiii", "repair", (int) dc,
+ 0, 0, rect.right-rect.left, rect.bottom-rect.top
+ );
+ if (result)
+ Py_DECREF(result);
+ else
+ callback_error("window repair callback");
+
+ ReleaseDC(wnd, dc);
+ EndPaint(wnd, &ps);
+ break;
+
+ case WM_SIZE:
+ /* resize window */
+ result = PyObject_CallFunction(
+ callback, "sii", "resize", LOWORD(lParam), HIWORD(lParam)
+ );
+ if (result) {
+ InvalidateRect(wnd, NULL, 1);
+ Py_DECREF(result);
+ } else
+ callback_error("window resize callback");
+ break;
+
+ case WM_DESTROY:
+ /* destroy window */
+ result = PyObject_CallFunction(callback, "s", "destroy");
+ if (result)
+ Py_DECREF(result);
+ else
+ callback_error("window destroy callback");
+ Py_DECREF(callback);
+ break;
+
+ default:
+ status = DefWindowProc(wnd, message, wParam, lParam);
+ }
+
+ if (callback) {
+ /* restore thread state */
+ PyEval_SaveThread();
+ PyThreadState_Swap(threadstate);
+ }
+
+ return status;
+}
+
+PyObject*
+PyImaging_CreateWindowWin32(PyObject* self, PyObject* args)
+{
+ HWND wnd;
+ WNDCLASS windowClass;
+
+ char* title;
+ PyObject* callback;
+ int width = 0, height = 0;
+ if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height))
+ return NULL;
+
+ if (width <= 0)
+ width = CW_USEDEFAULT;
+ if (height <= 0)
+ height = CW_USEDEFAULT;
+
+ /* register toplevel window class */
+ windowClass.style = CS_CLASSDC;
+ windowClass.cbClsExtra = 0;
+ windowClass.cbWndExtra = sizeof(PyObject*) + sizeof(PyThreadState*);
+ windowClass.hInstance = GetModuleHandle(NULL);
+ /* windowClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); */
+ windowClass.hbrBackground = NULL;
+ windowClass.lpszMenuName = NULL;
+ windowClass.lpszClassName = "pilWindow";
+ windowClass.lpfnWndProc = windowCallback;
+ windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
+ windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); /* CROSS? */
+
+ RegisterClass(&windowClass); /* FIXME: check return status */
+
+ wnd = CreateWindowEx(
+ 0, windowClass.lpszClassName, title,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, width, height,
+ HWND_DESKTOP, NULL, NULL, NULL
+ );
+
+ if (!wnd) {
+ PyErr_SetString(PyExc_IOError, "failed to create window");
+ return NULL;
+ }
+
+ /* register window callback */
+ Py_INCREF(callback);
+ SetWindowLong(wnd, 0, (LONG) callback);
+ SetWindowLong(wnd, sizeof(callback), (LONG) 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);
+}
+
+PyObject*
+PyImaging_EventLoopWin32(PyObject* self, PyObject* args)
+{
+ MSG msg;
+
+ Py_BEGIN_ALLOW_THREADS
+ while (mainloop && GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ Py_END_ALLOW_THREADS
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* -------------------------------------------------------------------- */
+/* windows WMF renderer */
+
+#define GET32(p,o) ((DWORD*)(p+o))[0]
+
+PyObject *
+PyImaging_DrawWmf(PyObject* self, PyObject* args)
+{
+ HBITMAP bitmap;
+ HENHMETAFILE meta;
+ BITMAPCOREHEADER core;
+ HDC dc;
+ RECT rect;
+ PyObject* buffer = NULL;
+ char* ptr;
+
+ char* data;
+ int datasize;
+ int width, height;
+ int x0, y0, x1, y1;
+ if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize,
+ &width, &height, &x0, &x1, &y0, &y1))
+ return NULL;
+
+ /* step 1: copy metafile contents into METAFILE object */
+
+ if (datasize > 22 && GET32(data, 0) == 0x9ac6cdd7) {
+
+ /* placeable windows metafile (22-byte aldus header) */
+ meta = SetWinMetaFileBits(datasize-22, data+22, NULL, NULL);
+
+ } else if (datasize > 80 && GET32(data, 0) == 1 &&
+ GET32(data, 40) == 0x464d4520) {
+
+ /* enhanced metafile */
+ meta = SetEnhMetaFileBits(datasize, data);
+
+ } else {
+
+ /* unknown meta format */
+ meta = NULL;
+
+ }
+
+ if (!meta) {
+ PyErr_SetString(PyExc_IOError, "cannot load metafile");
+ return NULL;
+ }
+
+ /* step 2: create bitmap */
+
+ core.bcSize = sizeof(core);
+ core.bcWidth = width;
+ core.bcHeight = height;
+ core.bcPlanes = 1;
+ core.bcBitCount = 24;
+
+ dc = CreateCompatibleDC(NULL);
+
+ bitmap = CreateDIBSection(
+ dc, (BITMAPINFO*) &core, DIB_RGB_COLORS, &ptr, NULL, 0
+ );
+
+ if (!bitmap) {
+ PyErr_SetString(PyExc_IOError, "cannot create bitmap");
+ goto error;
+ }
+
+ if (!SelectObject(dc, bitmap)) {
+ PyErr_SetString(PyExc_IOError, "cannot select bitmap");
+ goto error;
+ }
+
+ /* step 3: render metafile into bitmap */
+
+ rect.left = rect.top = 0;
+ rect.right = width;
+ rect.bottom = height;
+
+ /* FIXME: make background transparent? configurable? */
+ FillRect(dc, &rect, GetStockObject(WHITE_BRUSH));
+
+ if (!PlayEnhMetaFile(dc, meta, &rect)) {
+ PyErr_SetString(PyExc_IOError, "cannot render metafile");
+ goto error;
+ }
+
+ /* step 4: extract bits from bitmap */
+
+ GdiFlush();
+
+ buffer = PyString_FromStringAndSize(ptr, height * ((width*3 + 3) & -4));
+
+error:
+ DeleteEnhMetaFile(meta);
+
+ if (bitmap)
+ DeleteObject(bitmap);
+
+ DeleteDC(dc);
+
+ return buffer;
+}
+
+#endif /* WIN32 */
diff --git a/doctest.py b/doctest.py
new file mode 100644
index 000000000..e8140ecdf
--- /dev/null
+++ b/doctest.py
@@ -0,0 +1,1111 @@
+# 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
new file mode 100644
index 000000000..2ae13ad72
--- /dev/null
+++ b/encode.c
@@ -0,0 +1,540 @@
+/*
+ * The Python Imaging Library.
+ *
+ * standard encoder interfaces for the Imaging library
+ *
+ * History:
+ * 1996-04-19 fl Based on decoders.c
+ * 1996-05-12 fl Compile cleanly as C++
+ * 1996-12-30 fl Plugged potential memory leak for tiled images
+ * 1997-01-03 fl Added GIF encoder
+ * 1997-01-05 fl Plugged encoder buffer leaks
+ * 1997-01-11 fl Added encode_to_file method
+ * 1998-03-09 fl Added mode/rawmode argument to encoders
+ * 1998-07-09 fl Added interlace argument to GIF encoder
+ * 1999-02-07 fl Added PCX encoder
+ *
+ * Copyright (c) 1997-2001 by Secret Labs AB
+ * Copyright (c) 1996-1997 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+/* FIXME: make these pluggable! */
+
+#include "Python.h"
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#include "Imaging.h"
+#include "Gif.h"
+
+#ifdef HAVE_UNISTD_H
+#include /* write */
+#endif
+
+/* -------------------------------------------------------------------- */
+/* Common */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ int (*encode)(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+ struct ImagingCodecStateInstance state;
+ Imaging im;
+ PyObject* lock;
+} ImagingEncoderObject;
+
+staticforward PyTypeObject ImagingEncoderType;
+
+static ImagingEncoderObject*
+PyImaging_EncoderNew(int contextsize)
+{
+ ImagingEncoderObject *encoder;
+ void *context;
+
+ ImagingEncoderType.ob_type = &PyType_Type;
+
+ encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType);
+ if (encoder == NULL)
+ return NULL;
+
+ /* Clear the encoder state */
+ memset(&encoder->state, 0, sizeof(encoder->state));
+
+ /* Allocate encoder context */
+ if (contextsize > 0) {
+ context = (void*) calloc(1, contextsize);
+ if (!context) {
+ Py_DECREF(encoder);
+ (void) PyErr_NoMemory();
+ return NULL;
+ }
+ } else
+ context = 0;
+
+ /* Initialize encoder context */
+ encoder->state.context = context;
+
+ /* Target image */
+ encoder->lock = NULL;
+ encoder->im = NULL;
+
+ return encoder;
+}
+
+static void
+_dealloc(ImagingEncoderObject* encoder)
+{
+ free(encoder->state.buffer);
+ free(encoder->state.context);
+ Py_XDECREF(encoder->lock);
+ PyObject_Del(encoder);
+}
+
+static PyObject*
+_encode(ImagingEncoderObject* encoder, PyObject* args)
+{
+ PyObject* buf;
+ PyObject* result;
+ int status;
+
+ /* Encode to a Python string (allocated by this method) */
+
+ int bufsize = 16384;
+
+ if (!PyArg_ParseTuple(args, "|i", &bufsize))
+ return NULL;
+
+ buf = PyString_FromStringAndSize(NULL, bufsize);
+ if (!buf)
+ return NULL;
+
+ status = encoder->encode(encoder->im, &encoder->state,
+ (UINT8*) PyString_AsString(buf), bufsize);
+
+ /* adjust string length to avoid slicing in encoder */
+ if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0)
+ return NULL;
+
+ result = Py_BuildValue("iiO", status, encoder->state.errcode, buf);
+
+ Py_DECREF(buf); /* must release buffer!!! */
+
+ return result;
+}
+
+static PyObject*
+_encode_to_file(ImagingEncoderObject* encoder, PyObject* args)
+{
+ UINT8* buf;
+ int status;
+ ImagingSectionCookie cookie;
+
+ /* Encode to a file handle */
+
+ int fh;
+ int bufsize = 16384;
+
+ if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize))
+ return NULL;
+
+ /* Allocate an encoder buffer */
+ buf = (UINT8*) malloc(bufsize);
+ if (!buf)
+ return PyErr_NoMemory();
+
+ ImagingSectionEnter(&cookie);
+
+ do {
+
+ /* This replaces the inner loop in the ImageFile _save
+ function. */
+
+ status = encoder->encode(encoder->im, &encoder->state, buf, bufsize);
+
+ if (status > 0)
+ if (write(fh, buf, status) < 0) {
+ ImagingSectionLeave(&cookie);
+ free(buf);
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ } while (encoder->state.errcode == 0);
+
+ ImagingSectionLeave(&cookie);
+
+ free(buf);
+
+ return Py_BuildValue("i", encoder->state.errcode);
+}
+
+extern Imaging PyImaging_AsImaging(PyObject *op);
+
+static PyObject*
+_setimage(ImagingEncoderObject* encoder, PyObject* args)
+{
+ PyObject* op;
+ Imaging im;
+ ImagingCodecState state;
+ int x0, y0, x1, y1;
+
+ /* Define where image data should be stored */
+
+ x0 = y0 = x1 = y1 = 0;
+
+ /* FIXME: should publish the ImagingType descriptor */
+ if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1))
+ return NULL;
+ im = PyImaging_AsImaging(op);
+ if (!im)
+ return NULL;
+
+ encoder->im = im;
+
+ state = &encoder->state;
+
+ if (x0 == 0 && x1 == 0) {
+ state->xsize = im->xsize;
+ state->ysize = im->ysize;
+ } else {
+ state->xoff = x0;
+ state->yoff = y0;
+ state->xsize = x1 - x0;
+ state->ysize = y1 - y0;
+ }
+
+ if (state->xsize <= 0 ||
+ state->xsize + state->xoff > im->xsize ||
+ state->ysize <= 0 ||
+ state->ysize + state->yoff > im->ysize) {
+ PyErr_SetString(PyExc_SystemError, "tile cannot extend outside image");
+ return NULL;
+ }
+
+ /* Allocate memory buffer (if bits field is set) */
+ if (state->bits > 0) {
+ state->bytes = (state->bits * state->xsize+7)/8;
+ 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
+ go away before we do */
+ Py_INCREF(op);
+ Py_XDECREF(encoder->lock);
+ encoder->lock = op;
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef methods[] = {
+ {"encode", (PyCFunction)_encode, 1},
+ {"encode_to_file", (PyCFunction)_encode_to_file, 1},
+ {"setimage", (PyCFunction)_setimage, 1},
+ {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*/
+ "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*/
+};
+
+/* -------------------------------------------------------------------- */
+
+int
+get_packer(ImagingEncoderObject* encoder, const char* mode,
+ const char* rawmode)
+{
+ int bits;
+ ImagingShuffler pack;
+
+ pack = ImagingFindPacker(mode, rawmode, &bits);
+ if (!pack) {
+ Py_DECREF(encoder);
+ PyErr_SetString(PyExc_SystemError, "unknown raw mode");
+ return -1;
+ }
+
+ encoder->state.shuffle = pack;
+ encoder->state.bits = bits;
+
+ return 0;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* EPS */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_EpsEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ encoder = PyImaging_EncoderNew(0);
+ if (encoder == NULL)
+ return NULL;
+
+ encoder->encode = ImagingEpsEncode;
+
+ return (PyObject*) encoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* GIF */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_GifEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char *mode;
+ char *rawmode;
+ int bits = 8;
+ int interlace = 0;
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace))
+ return NULL;
+
+ encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE));
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, mode, rawmode) < 0)
+ return NULL;
+
+ encoder->encode = ImagingGifEncode;
+
+ ((GIFENCODERSTATE*)encoder->state.context)->bits = bits;
+ ((GIFENCODERSTATE*)encoder->state.context)->interlace = interlace;
+
+ return (PyObject*) encoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* PCX */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_PcxEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char *mode;
+ char *rawmode;
+ int bits = 8;
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits))
+ return NULL;
+
+ encoder = PyImaging_EncoderNew(0);
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, mode, rawmode) < 0)
+ return NULL;
+
+ encoder->encode = ImagingPcxEncode;
+
+ return (PyObject*) encoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* RAW */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_RawEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char *mode;
+ char *rawmode;
+ int stride = 0;
+ int ystep = 1;
+
+ if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep))
+ return NULL;
+
+ encoder = PyImaging_EncoderNew(0);
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, mode, rawmode) < 0)
+ return NULL;
+
+ encoder->encode = ImagingRawEncode;
+
+ encoder->state.ystep = ystep;
+ encoder->state.count = stride;
+
+ return (PyObject*) encoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* XBM */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyImaging_XbmEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ encoder = PyImaging_EncoderNew(0);
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, "1", "1;R") < 0)
+ return NULL;
+
+ encoder->encode = ImagingXbmEncode;
+
+ return (PyObject*) encoder;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* ZIP */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBZ
+
+#include "Zip.h"
+
+PyObject*
+PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char* mode;
+ char* rawmode;
+ int optimize = 0;
+ char* dictionary = NULL;
+ int dictionary_size = 0;
+ if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize,
+ &dictionary, &dictionary_size))
+ return NULL;
+
+ encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE));
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, mode, rawmode) < 0)
+ return NULL;
+
+ encoder->encode = ImagingZipEncode;
+
+ if (rawmode[0] == 'P')
+ /* disable filtering */
+ ((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE;
+
+ ((ZIPSTATE*)encoder->state.context)->optimize = optimize;
+ ((ZIPSTATE*)encoder->state.context)->dictionary = dictionary;
+ ((ZIPSTATE*)encoder->state.context)->dictionary_size = dictionary_size;
+
+ return (PyObject*) encoder;
+}
+#endif
+
+
+/* -------------------------------------------------------------------- */
+/* JPEG */
+/* -------------------------------------------------------------------- */
+
+#ifdef HAVE_LIBJPEG
+
+/* We better define this encoder 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
+
+#include "Jpeg.h"
+
+PyObject*
+PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
+{
+ ImagingEncoderObject* encoder;
+
+ char *mode;
+ char *rawmode;
+ int quality = 0;
+ int progressive = 0;
+ int smooth = 0;
+ int optimize = 0;
+ 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))
+ return NULL;
+
+ encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE));
+ if (encoder == NULL)
+ return NULL;
+
+ if (get_packer(encoder, mode, rawmode) < 0)
+ return NULL;
+
+ if (extra && extra_size > 0) {
+ char* p = malloc(extra_size);
+ if (!p)
+ return PyErr_NoMemory();
+ memcpy(p, extra, extra_size);
+ extra = p;
+ } else
+ extra = NULL;
+
+ encoder->encode = ImagingJpegEncode;
+
+ ((JPEGENCODERSTATE*)encoder->state.context)->quality = quality;
+ ((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling;
+ ((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive;
+ ((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth;
+ ((JPEGENCODERSTATE*)encoder->state.context)->optimize = optimize;
+ ((JPEGENCODERSTATE*)encoder->state.context)->streamtype = streamtype;
+ ((JPEGENCODERSTATE*)encoder->state.context)->xdpi = xdpi;
+ ((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi;
+ ((JPEGENCODERSTATE*)encoder->state.context)->extra = extra;
+ ((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size;
+
+ return (PyObject*) encoder;
+}
+
+#endif
diff --git a/libImaging/Access.c b/libImaging/Access.c
new file mode 100644
index 000000000..5ebc9b6f3
--- /dev/null
+++ b/libImaging/Access.c
@@ -0,0 +1,255 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * imaging access objects
+ *
+ * Copyright (c) Fredrik Lundh 2009.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+/* use Tests/make_hash.py to calculate these values */
+#define ACCESS_TABLE_SIZE 21
+#define ACCESS_TABLE_HASH 30197
+
+static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE];
+
+static inline UINT32
+hash(const char* mode)
+{
+ UINT32 i = ACCESS_TABLE_HASH;
+ while (*mode)
+ i = ((i<<5) + i) ^ (UINT8) *mode++;
+ return i % ACCESS_TABLE_SIZE;
+}
+
+static ImagingAccess
+add_item(const char* mode)
+{
+ UINT32 i = hash(mode);
+ /* printf("hash %s => %d\n", mode, i); */
+ if (access_table[i].mode) {
+ fprintf(stderr, "AccessInit: hash collision: %d for both %s and %s\n",
+ i, mode, access_table[i].mode);
+ exit(1);
+ }
+ access_table[i].mode = mode;
+ return &access_table[i];
+}
+
+/* fetch pointer to pixel line */
+
+static void*
+line_8(Imaging im, int x, int y)
+{
+ return &im->image8[y][x];
+}
+
+static void*
+line_16(Imaging im, int x, int y)
+{
+ return &im->image8[y][x+x];
+}
+
+static void*
+line_32(Imaging im, int x, int y)
+{
+ return &im->image32[y][x];
+}
+
+/* fetch individual pixel */
+
+static void
+get_pixel(Imaging im, int x, int y, void* color)
+{
+ char* out = color;
+
+ /* generic pixel access*/
+
+ if (im->image8) {
+ out[0] = im->image8[y][x];
+ } else {
+ UINT8* p = (UINT8*) &im->image32[y][x];
+ if (im->type == IMAGING_TYPE_UINT8 && im->bands == 2) {
+ out[0] = p[0];
+ out[1] = p[3];
+ return;
+ }
+ memcpy(out, p, im->pixelsize);
+ }
+}
+
+static void
+get_pixel_8(Imaging im, int x, int y, void* color)
+{
+ char* out = color;
+ out[0] = im->image8[y][x];
+}
+
+static void
+get_pixel_16L(Imaging im, int x, int y, void* color)
+{
+ UINT8* in = (UINT8*) &im->image[y][x+x];
+ INT16* out = color;
+#ifdef WORDS_BIGENDIAN
+ out[0] = in[0] + (in[1]<<8);
+#else
+ out[0] = *(INT16*) in;
+#endif
+}
+
+static void
+get_pixel_16B(Imaging im, int x, int y, void* color)
+{
+ UINT8* in = (UINT8*) &im->image[y][x+x];
+ INT16* out = color;
+#ifdef WORDS_BIGENDIAN
+ out[0] = *(INT16*) in;
+#else
+ out[0] = in[1] + (in[0]<<8);
+#endif
+}
+
+static void
+get_pixel_32(Imaging im, int x, int y, void* color)
+{
+ INT32* out = color;
+ out[0] = im->image32[y][x];
+}
+
+static void
+get_pixel_32L(Imaging im, int x, int y, void* color)
+{
+ UINT8* in = (UINT8*) &im->image[y][x*4];
+ INT32* out = color;
+#ifdef WORDS_BIGENDIAN
+ out[0] = in[0] + (in[1]<<8) + (in[2]<<16) + (in[3]<<24);
+#else
+ out[0] = *(INT32*) in;
+#endif
+}
+
+static void
+get_pixel_32B(Imaging im, int x, int y, void* color)
+{
+ UINT8* in = (UINT8*) &im->image[y][x*4];
+ INT32* out = color;
+#ifdef WORDS_BIGENDIAN
+ out[0] = *(INT32*) in;
+#else
+ out[0] = in[3] + (in[2]<<8) + (in[1]<<16) + (in[0]<<24);
+#endif
+}
+
+/* store individual pixel */
+
+static void
+put_pixel(Imaging im, int x, int y, const void* color)
+{
+ if (im->image8)
+ im->image8[y][x] = *((UINT8*) color);
+ else
+ im->image32[y][x] = *((INT32*) color);
+}
+
+static void
+put_pixel_8(Imaging im, int x, int y, const void* color)
+{
+ im->image8[y][x] = *((UINT8*) color);
+}
+
+static void
+put_pixel_16L(Imaging im, int x, int y, const void* color)
+{
+ const char* in = color;
+ UINT8* out = (UINT8*) &im->image8[y][x+x];
+ out[0] = in[0];
+ out[1] = in[1];
+}
+
+static void
+put_pixel_16B(Imaging im, int x, int y, const void* color)
+{
+ const char* in = color;
+ UINT8* out = (UINT8*) &im->image8[y][x+x];
+ out[0] = in[1];
+ out[1] = in[0];
+}
+
+static void
+put_pixel_32L(Imaging im, int x, int y, const void* color)
+{
+ const char* in = color;
+ UINT8* out = (UINT8*) &im->image8[y][x*4];
+ out[0] = in[0];
+ out[1] = in[1];
+ out[2] = in[2];
+ out[3] = in[3];
+}
+
+static void
+put_pixel_32B(Imaging im, int x, int y, const void* color)
+{
+ const char* in = color;
+ UINT8* out = (UINT8*) &im->image8[y][x*4];
+ out[0] = in[3];
+ out[1] = in[2];
+ out[2] = in[1];
+ out[3] = in[0];
+}
+
+static void
+put_pixel_32(Imaging im, int x, int y, const void* color)
+{
+ im->image32[y][x] = *((INT32*) color);
+}
+
+void
+ImagingAccessInit()
+{
+#define ADD(mode_, line_, get_pixel_, put_pixel_) \
+ { ImagingAccess access = add_item(mode_); \
+ access->line = line_; \
+ access->get_pixel = get_pixel_; \
+ access->put_pixel = put_pixel_; \
+ }
+
+ /* populate access table */
+ ADD("1", line_8, get_pixel_8, put_pixel_8);
+ ADD("L", line_8, get_pixel_8, put_pixel_8);
+ ADD("LA", line_32, get_pixel, put_pixel);
+ ADD("I", line_32, get_pixel_32, put_pixel_32);
+ ADD("I;16", line_16, get_pixel_16L, put_pixel_16L);
+ ADD("I;16L", line_16, get_pixel_16L, put_pixel_16L);
+ ADD("I;16B", line_16, get_pixel_16B, put_pixel_16B);
+ ADD("I;32L", line_32, get_pixel_32L, put_pixel_32L);
+ ADD("I;32B", line_32, get_pixel_32B, put_pixel_32B);
+ ADD("F", line_32, get_pixel_32, put_pixel_32);
+ ADD("P", line_8, get_pixel_8, put_pixel_8);
+ ADD("PA", line_32, get_pixel, put_pixel);
+ ADD("RGB", line_32, get_pixel_32, put_pixel_32);
+ ADD("RGBA", line_32, get_pixel_32, put_pixel_32);
+ ADD("RGBa", line_32, get_pixel_32, put_pixel_32);
+ 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);
+}
+
+ImagingAccess
+ImagingAccessNew(Imaging im)
+{
+ ImagingAccess access = &access_table[hash(im->mode)];
+ if (im->mode[0] != access->mode[0] || strcmp(im->mode, access->mode) != 0)
+ return NULL;
+ return access;
+}
+
+void
+_ImagingAccessDelete(Imaging im, ImagingAccess access)
+{
+
+}
diff --git a/libImaging/Antialias.c b/libImaging/Antialias.c
new file mode 100644
index 000000000..53dfa3522
--- /dev/null
+++ b/libImaging/Antialias.c
@@ -0,0 +1,307 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * pilopen antialiasing support
+ *
+ * history:
+ * 2002-03-09 fl Created (for PIL 1.1.3)
+ * 2002-03-10 fl Added support for mode "F"
+ *
+ * Copyright (c) 1997-2002 by Secret Labs AB
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+#include
+
+/* resampling filters (from antialias.py) */
+
+struct filter {
+ float (*filter)(float x);
+ float support;
+};
+
+static inline float sinc_filter(float x)
+{
+ if (x == 0.0)
+ return 1.0;
+ x = x * M_PI;
+ return sin(x) / x;
+}
+
+static inline float antialias_filter(float x)
+{
+ /* lanczos (truncated sinc) */
+ if (-3.0 <= x && x < 3.0)
+ return sinc_filter(x) * sinc_filter(x/3);
+ return 0.0;
+}
+
+static struct filter ANTIALIAS = { antialias_filter, 3.0 };
+
+static inline float nearest_filter(float x)
+{
+ if (-0.5 <= x && x < 0.5)
+ return 1.0;
+ return 0.0;
+}
+
+static struct filter NEAREST = { nearest_filter, 0.5 };
+
+static inline float bilinear_filter(float x)
+{
+ if (x < 0.0)
+ x = -x;
+ if (x < 1.0)
+ return 1.0-x;
+ return 0.0;
+}
+
+static struct filter BILINEAR = { bilinear_filter, 1.0 };
+
+static inline float bicubic_filter(float x)
+{
+ /* FIXME: double-check this algorithm */
+ /* FIXME: for best results, "a" should be -0.5 to -1.0, but we'll
+ set it to zero for now, to match the 1.1 magnifying filter */
+#define a 0.0
+ if (x < 0.0)
+ x = -x;
+ if (x < 1.0)
+ return (((a + 2.0) * x) - (a + 3.0)) * x*x + 1;
+ if (x < 2.0)
+ return (((a * x) - 5*a) * x + 8) * x - 4*a;
+ return 0.0;
+#undef a
+}
+
+static struct filter BICUBIC = { bicubic_filter, 2.0 };
+
+Imaging
+ImagingStretch(Imaging imOut, Imaging imIn, int filter)
+{
+ /* FIXME: this is a quick and straightforward translation from a
+ python prototype. might need some further C-ification... */
+
+ ImagingSectionCookie cookie;
+ struct filter *filterp;
+ float support, scale, filterscale;
+ float center, ww, ss, ymin, ymax, xmin, xmax;
+ int xx, yy, x, y, b;
+ float *k;
+
+ /* check modes */
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+
+ /* check filter */
+ switch (filter) {
+ case IMAGING_TRANSFORM_NEAREST:
+ filterp = &NEAREST;
+ break;
+ case IMAGING_TRANSFORM_ANTIALIAS:
+ filterp = &ANTIALIAS;
+ break;
+ case IMAGING_TRANSFORM_BILINEAR:
+ filterp = &BILINEAR;
+ break;
+ case IMAGING_TRANSFORM_BICUBIC:
+ filterp = &BICUBIC;
+ break;
+ default:
+ return (Imaging) ImagingError_ValueError(
+ "unsupported resampling filter"
+ );
+ }
+
+ if (imIn->ysize == imOut->ysize) {
+ /* prepare for horizontal stretch */
+ filterscale = scale = (float) imIn->xsize / imOut->xsize;
+ } else if (imIn->xsize == imOut->xsize) {
+ /* prepare for vertical stretch */
+ filterscale = scale = (float) imIn->ysize / imOut->ysize;
+ } else
+ return (Imaging) ImagingError_Mismatch();
+
+ /* determine support size (length of resampling filter) */
+ support = filterp->support;
+
+ if (filterscale < 1.0) {
+ filterscale = 1.0;
+ support = 0.5;
+ }
+
+ support = support * filterscale;
+
+ /* coefficient buffer (with rounding safety margin) */
+ k = malloc(((int) support * 2 + 10) * sizeof(float));
+ if (!k)
+ return (Imaging) ImagingError_MemoryError();
+
+ ImagingSectionEnter(&cookie);
+ if (imIn->xsize == imOut->xsize) {
+ /* vertical stretch */
+ for (yy = 0; yy < imOut->ysize; yy++) {
+ center = (yy + 0.5) * scale;
+ ww = 0.0;
+ ss = 1.0 / filterscale;
+ /* calculate filter weights */
+ ymin = floor(center - support);
+ if (ymin < 0.0)
+ ymin = 0.0;
+ ymax = ceil(center + support);
+ if (ymax > (float) imIn->ysize)
+ ymax = (float) imIn->ysize;
+ for (y = (int) ymin; y < (int) ymax; y++) {
+ float w = filterp->filter((y - center + 0.5) * ss) * ss;
+ k[y - (int) ymin] = w;
+ ww = ww + w;
+ }
+ if (ww == 0.0)
+ ww = 1.0;
+ else
+ ww = 1.0 / ww;
+ if (imIn->image8) {
+ /* 8-bit grayscale */
+ for (xx = 0; xx < imOut->xsize; xx++) {
+ ss = 0.0;
+ for (y = (int) ymin; y < (int) ymax; y++)
+ ss = ss + imIn->image8[y][xx] * k[y - (int) ymin];
+ ss = ss * ww + 0.5;
+ if (ss < 0.5)
+ imOut->image8[yy][xx] = 0;
+ else if (ss >= 255.0)
+ imOut->image8[yy][xx] = 255;
+ else
+ imOut->image8[yy][xx] = (UINT8) ss;
+ }
+ } else
+ switch(imIn->type) {
+ case IMAGING_TYPE_UINT8:
+ /* n-bit grayscale */
+ for (xx = 0; xx < imOut->xsize*4; xx++) {
+ /* FIXME: skip over unused pixels */
+ ss = 0.0;
+ for (y = (int) ymin; y < (int) ymax; y++)
+ ss = ss + (UINT8) imIn->image[y][xx] * k[y-(int) ymin];
+ ss = ss * ww + 0.5;
+ if (ss < 0.5)
+ imOut->image[yy][xx] = (UINT8) 0;
+ else if (ss >= 255.0)
+ imOut->image[yy][xx] = (UINT8) 255;
+ else
+ imOut->image[yy][xx] = (UINT8) ss;
+ }
+ break;
+ case IMAGING_TYPE_INT32:
+ /* 32-bit integer */
+ for (xx = 0; xx < imOut->xsize; xx++) {
+ ss = 0.0;
+ for (y = (int) ymin; y < (int) ymax; y++)
+ ss = ss + IMAGING_PIXEL_I(imIn, xx, y) * k[y - (int) ymin];
+ IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww;
+ }
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ /* 32-bit float */
+ for (xx = 0; xx < imOut->xsize; xx++) {
+ ss = 0.0;
+ for (y = (int) ymin; y < (int) ymax; y++)
+ ss = ss + IMAGING_PIXEL_F(imIn, xx, y) * k[y - (int) ymin];
+ IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww;
+ }
+ break;
+ default:
+ ImagingSectionLeave(&cookie);
+ return (Imaging) ImagingError_ModeError();
+ }
+ }
+ } else {
+ /* horizontal stretch */
+ for (xx = 0; xx < imOut->xsize; xx++) {
+ center = (xx + 0.5) * scale;
+ ww = 0.0;
+ ss = 1.0 / filterscale;
+ xmin = floor(center - support);
+ if (xmin < 0.0)
+ xmin = 0.0;
+ xmax = ceil(center + support);
+ if (xmax > (float) imIn->xsize)
+ xmax = (float) imIn->xsize;
+ for (x = (int) xmin; x < (int) xmax; x++) {
+ float w = filterp->filter((x - center + 0.5) * ss) * ss;
+ k[x - (int) xmin] = w;
+ ww = ww + w;
+ }
+ if (ww == 0.0)
+ ww = 1.0;
+ else
+ ww = 1.0 / ww;
+ if (imIn->image8) {
+ /* 8-bit grayscale */
+ for (yy = 0; yy < imOut->ysize; yy++) {
+ ss = 0.0;
+ for (x = (int) xmin; x < (int) xmax; x++)
+ ss = ss + imIn->image8[yy][x] * k[x - (int) xmin];
+ ss = ss * ww + 0.5;
+ if (ss < 0.5)
+ imOut->image8[yy][xx] = (UINT8) 0;
+ else if (ss >= 255.0)
+ imOut->image8[yy][xx] = (UINT8) 255;
+ else
+ imOut->image8[yy][xx] = (UINT8) ss;
+ }
+ } else
+ switch(imIn->type) {
+ case IMAGING_TYPE_UINT8:
+ /* n-bit grayscale */
+ for (yy = 0; yy < imOut->ysize; yy++) {
+ for (b = 0; b < imIn->bands; b++) {
+ if (imIn->bands == 2 && b)
+ b = 3; /* hack to deal with LA images */
+ ss = 0.0;
+ for (x = (int) xmin; x < (int) xmax; x++)
+ ss = ss + (UINT8) imIn->image[yy][x*4+b] * k[x - (int) xmin];
+ ss = ss * ww + 0.5;
+ if (ss < 0.5)
+ imOut->image[yy][xx*4+b] = (UINT8) 0;
+ else if (ss >= 255.0)
+ imOut->image[yy][xx*4+b] = (UINT8) 255;
+ else
+ imOut->image[yy][xx*4+b] = (UINT8) ss;
+ }
+ }
+ break;
+ case IMAGING_TYPE_INT32:
+ /* 32-bit integer */
+ for (yy = 0; yy < imOut->ysize; yy++) {
+ ss = 0.0;
+ for (x = (int) xmin; x < (int) xmax; x++)
+ ss = ss + IMAGING_PIXEL_I(imIn, x, yy) * k[x - (int) xmin];
+ IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww;
+ }
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ /* 32-bit float */
+ for (yy = 0; yy < imOut->ysize; yy++) {
+ ss = 0.0;
+ for (x = (int) xmin; x < (int) xmax; x++)
+ ss = ss + IMAGING_PIXEL_F(imIn, x, yy) * k[x - (int) xmin];
+ IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww;
+ }
+ break;
+ default:
+ ImagingSectionLeave(&cookie);
+ return (Imaging) ImagingError_ModeError();
+ }
+ }
+ }
+ ImagingSectionLeave(&cookie);
+
+ free(k);
+
+ return imOut;
+}
diff --git a/libImaging/Bands.c b/libImaging/Bands.c
new file mode 100644
index 000000000..fdfc6ae95
--- /dev/null
+++ b/libImaging/Bands.c
@@ -0,0 +1,129 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * stuff to extract and paste back individual bands
+ *
+ * history:
+ * 1996-03-20 fl Created
+ * 1997-08-27 fl Fixed putband for single band targets.
+ * 2003-09-26 fl Fixed getband/putband for 2-band images (LA, PA).
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB.
+ * Copyright (c) 1996-1997 by Fredrik Lundh.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
+
+
+Imaging
+ImagingGetBand(Imaging imIn, int band)
+{
+ Imaging imOut;
+ int x, y;
+
+ /* Check arguments */
+ if (!imIn || imIn->type != IMAGING_TYPE_UINT8)
+ return (Imaging) ImagingError_ModeError();
+
+ if (band < 0 || band >= imIn->bands)
+ return (Imaging) ImagingError_ValueError("band index out of range");
+
+ /* Shortcuts */
+ if (imIn->bands == 1)
+ return ImagingCopy(imIn);
+
+ /* Special case for LXXA etc */
+ if (imIn->bands == 2 && band == 1)
+ band = 3;
+
+ imOut = ImagingNew("L", imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+
+ /* Extract band from image */
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = (UINT8*) imIn->image[y] + band;
+ UINT8* out = imOut->image8[y];
+ for (x = 0; x < imIn->xsize; x++) {
+ out[x] = *in;
+ in += 4;
+ }
+ }
+
+ return imOut;
+}
+
+Imaging
+ImagingPutBand(Imaging imOut, Imaging imIn, int band)
+{
+ int x, y;
+
+ /* Check arguments */
+ if (!imIn || imIn->bands != 1 || !imOut)
+ return (Imaging) ImagingError_ModeError();
+
+ if (band < 0 || band >= imOut->bands)
+ return (Imaging) ImagingError_ValueError("band index out of range");
+
+ if (imIn->type != imOut->type ||
+ imIn->xsize != imOut->xsize ||
+ imIn->ysize != imOut->ysize)
+ return (Imaging) ImagingError_Mismatch();
+
+ /* Shortcuts */
+ if (imOut->bands == 1)
+ return ImagingCopy2(imOut, imIn);
+
+ /* Special case for LXXA etc */
+ if (imOut->bands == 2 && band == 1)
+ band = 3;
+
+ /* Insert band into image */
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = imIn->image8[y];
+ UINT8* out = (UINT8*) imOut->image[y] + band;
+ for (x = 0; x < imIn->xsize; x++) {
+ *out = in[x];
+ out += 4;
+ }
+ }
+
+ return imOut;
+}
+
+Imaging
+ImagingFillBand(Imaging imOut, int band, int color)
+{
+ int x, y;
+
+ /* Check arguments */
+ if (!imOut || imOut->type != IMAGING_TYPE_UINT8)
+ return (Imaging) ImagingError_ModeError();
+
+ if (band < 0 || band >= imOut->bands)
+ return (Imaging) ImagingError_ValueError("band index out of range");
+
+ /* Special case for LXXA etc */
+ if (imOut->bands == 2 && band == 1)
+ band = 3;
+
+ color = CLIP(color);
+
+ /* Insert color into image */
+ for (y = 0; y < imOut->ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y] + band;
+ for (x = 0; x < imOut->xsize; x++) {
+ *out = (UINT8) color;
+ out += 4;
+ }
+ }
+
+ return imOut;
+}
diff --git a/libImaging/Bit.h b/libImaging/Bit.h
new file mode 100644
index 000000000..56e3a17d2
--- /dev/null
+++ b/libImaging/Bit.h
@@ -0,0 +1,30 @@
+/* Bit.h */
+
+typedef struct {
+
+ /* CONFIGURATION */
+
+ /* Number of bits per pixel */
+ int bits;
+
+ /* Line padding (0 or 8) */
+ int pad;
+
+ /* Fill order */
+ /* 0=msb/msb, 1=msbfill/lsbshift, 2=lsbfill/msbshift, 3=lsb/lsb */
+ int fill;
+
+ /* Signed integers (0=unsigned, 1=signed) */
+ int sign;
+
+ /* Lookup table (not implemented) */
+ unsigned long lutsize;
+ FLOAT32* lut;
+
+ /* INTERNAL */
+ unsigned long mask;
+ unsigned long signmask;
+ unsigned long bitbuffer;
+ int bitcount;
+
+} BITSTATE;
diff --git a/libImaging/BitDecode.c b/libImaging/BitDecode.c
new file mode 100644
index 000000000..572926f00
--- /dev/null
+++ b/libImaging/BitDecode.c
@@ -0,0 +1,138 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for packed bitfields (converts to floating point)
+ *
+ * history:
+ * 97-05-31 fl created (much more than originally intended)
+ *
+ * Copyright (c) Fredrik Lundh 1997.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include "Bit.h"
+
+
+int
+ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ BITSTATE* bitstate = state->context;
+ UINT8* ptr;
+
+ if (state->state == 0) {
+
+ /* Initialize context variables */
+
+ /* this decoder only works for float32 image buffers */
+ if (im->type != IMAGING_TYPE_FLOAT32) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ /* sanity check */
+ if (bitstate->bits < 1 || bitstate->bits >= 32) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ bitstate->mask = (1<bits)-1;
+
+ if (bitstate->sign)
+ bitstate->signmask = (1<<(bitstate->bits-1));
+
+ /* check image orientation */
+ if (state->ystep < 0) {
+ state->y = state->ysize-1;
+ state->ystep = -1;
+ } else
+ state->ystep = 1;
+
+ state->state = 1;
+
+ }
+
+ ptr = buf;
+
+ while (bytes > 0) {
+
+ UINT8 byte = *ptr;
+
+ ptr++;
+ bytes--;
+
+ /* get a byte from the input stream and insert in the bit buffer */
+ if (bitstate->fill&1)
+ /* fill MSB first */
+ bitstate->bitbuffer |= (unsigned long) byte << bitstate->bitcount;
+ else
+ /* fill LSB first */
+ bitstate->bitbuffer = (bitstate->bitbuffer << 8) | byte;
+
+ bitstate->bitcount += 8;
+
+ while (bitstate->bitcount >= bitstate->bits) {
+
+ /* get a pixel from the bit buffer */
+ unsigned long data;
+ FLOAT32 pixel;
+
+ if (bitstate->fill&2) {
+ /* store LSB first */
+ data = bitstate->bitbuffer & bitstate->mask;
+ if (bitstate->bitcount > 32)
+ /* bitbuffer overflow; restore it from last input byte */
+ bitstate->bitbuffer = byte >> (8 - (bitstate->bitcount -
+ bitstate->bits));
+ else
+ bitstate->bitbuffer >>= bitstate->bits;
+ } else
+ /* store MSB first */
+ data = (bitstate->bitbuffer >> (bitstate->bitcount -
+ bitstate->bits))
+ & bitstate->mask;
+
+ bitstate->bitcount -= bitstate->bits;
+
+ if (bitstate->lutsize > 0) {
+ /* map through lookup table */
+ if (data <= 0)
+ pixel = bitstate->lut[0];
+ else if (data >= bitstate->lutsize)
+ pixel = bitstate->lut[bitstate->lutsize-1];
+ else
+ pixel = bitstate->lut[data];
+ } else {
+ /* convert */
+ if (data & bitstate->signmask)
+ /* image memory contains signed data */
+ pixel = (FLOAT32) (INT32) (data | ~bitstate->mask);
+ else
+ pixel = (FLOAT32) data;
+ }
+
+ *(FLOAT32*)(&im->image32[state->y][state->x]) = pixel;
+
+ /* step forward */
+ if (++state->x >= state->xsize) {
+ /* new line */
+ state->y += state->ystep;
+ if (state->y < 0 || state->y >= state->ysize) {
+ /* end of file (errcode = 0) */
+ return -1;
+ }
+ state->x = 0;
+ /* reset bit buffer */
+ if (bitstate->pad > 0)
+ bitstate->bitcount = 0;
+ }
+ }
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/Blend.c b/libImaging/Blend.c
new file mode 100644
index 000000000..0861c8ef4
--- /dev/null
+++ b/libImaging/Blend.c
@@ -0,0 +1,79 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * interpolate between two existing images
+ *
+ * history:
+ * 96-03-20 fl Created
+ * 96-05-18 fl Simplified blend expression
+ * 96-10-05 fl Fixed expression bug, special case for interpolation
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+Imaging
+ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha)
+{
+ Imaging imOut;
+ int x, y;
+
+ /* Check arguments */
+ if (!imIn1 || !imIn2 || imIn1->type != IMAGING_TYPE_UINT8)
+ return ImagingError_ModeError();
+ if (imIn1->type != imIn2->type ||
+ imIn1->bands != imIn2->bands ||
+ imIn1->xsize != imIn2->xsize ||
+ imIn1->ysize != imIn2->ysize)
+ return ImagingError_Mismatch();
+
+ /* Shortcuts */
+ if (alpha == 0.0)
+ return ImagingCopy(imIn1);
+ else if (alpha == 1.0)
+ return ImagingCopy(imIn2);
+
+ imOut = ImagingNew(imIn1->mode, imIn1->xsize, imIn1->ysize);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, imIn1);
+
+ if (alpha >= 0 && alpha <= 1.0) {
+ /* Interpolate between bands */
+ for (y = 0; y < imIn1->ysize; y++) {
+ UINT8* in1 = (UINT8*) imIn1->image[y];
+ UINT8* in2 = (UINT8*) imIn2->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+ for (x = 0; x < imIn1->linesize; x++)
+ out[x] = (UINT8)
+ ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x]));
+ }
+ } else {
+ /* Extrapolation; must make sure to clip resulting values */
+ for (y = 0; y < imIn1->ysize; y++) {
+ UINT8* in1 = (UINT8*) imIn1->image[y];
+ UINT8* in2 = (UINT8*) imIn2->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+ for (x = 0; x < imIn1->linesize; x++) {
+ float temp = (float)
+ ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x]));
+ if (temp <= 0.0)
+ out[x] = 0;
+ else if (temp >= 255.0)
+ out[x] = 255;
+ else
+ out[x] = (UINT8) temp;
+ }
+ }
+ }
+
+ return imOut;
+}
diff --git a/libImaging/Chops.c b/libImaging/Chops.c
new file mode 100644
index 000000000..e5993195a
--- /dev/null
+++ b/libImaging/Chops.c
@@ -0,0 +1,148 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * basic channel operations
+ *
+ * history:
+ * 1996-03-28 fl Created
+ * 1996-08-13 fl Added and/or/xor for "1" images
+ * 1996-12-14 fl Added add_modulo, sub_modulo
+ * 2005-09-10 fl Fixed output values from and/or/xor
+ *
+ * Copyright (c) 1996 by Fredrik Lundh.
+ * Copyright (c) 1997 by Secret Labs AB.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define CHOP(operation, mode)\
+ int x, y;\
+ Imaging imOut;\
+ imOut = create(imIn1, imIn2, mode);\
+ if (!imOut)\
+ return NULL;\
+ for (y = 0; y < imOut->ysize; y++) {\
+ UINT8* out = (UINT8*) imOut->image[y];\
+ UINT8* in1 = (UINT8*) imIn1->image[y];\
+ UINT8* in2 = (UINT8*) imIn2->image[y];\
+ for (x = 0; x < imOut->linesize; x++) {\
+ int temp = operation;\
+ if (temp <= 0)\
+ out[x] = 0;\
+ else if (temp >= 255)\
+ out[x] = 255;\
+ else\
+ out[x] = temp;\
+ }\
+ }\
+ return imOut;
+
+#define CHOP2(operation, mode)\
+ int x, y;\
+ Imaging imOut;\
+ imOut = create(imIn1, imIn2, mode);\
+ if (!imOut)\
+ return NULL;\
+ for (y = 0; y < imOut->ysize; y++) {\
+ UINT8* out = (UINT8*) imOut->image[y];\
+ UINT8* in1 = (UINT8*) imIn1->image[y];\
+ UINT8* in2 = (UINT8*) imIn2->image[y];\
+ for (x = 0; x < imOut->linesize; x++) {\
+ out[x] = operation;\
+ }\
+ }\
+ return imOut;
+
+static Imaging
+create(Imaging im1, Imaging im2, char* mode)
+{
+ int xsize, ysize;
+
+ if (!im1 || !im2 || im1->type != IMAGING_TYPE_UINT8 ||
+ (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1"))))
+ return (Imaging) ImagingError_ModeError();
+ if (im1->type != im2->type ||
+ im1->bands != im2->bands)
+ return (Imaging) ImagingError_Mismatch();
+
+ xsize = (im1->xsize < im2->xsize) ? im1->xsize : im2->xsize;
+ ysize = (im1->ysize < im2->ysize) ? im1->ysize : im2->ysize;
+
+ return ImagingNew(im1->mode, xsize, ysize);
+}
+
+Imaging
+ImagingChopLighter(Imaging imIn1, Imaging imIn2)
+{
+ CHOP((in1[x] > in2[x]) ? in1[x] : in2[x], NULL);
+}
+
+Imaging
+ImagingChopDarker(Imaging imIn1, Imaging imIn2)
+{
+ CHOP((in1[x] < in2[x]) ? in1[x] : in2[x], NULL);
+}
+
+Imaging
+ImagingChopDifference(Imaging imIn1, Imaging imIn2)
+{
+ CHOP(abs((int) in1[x] - (int) in2[x]), NULL);
+}
+
+Imaging
+ImagingChopMultiply(Imaging imIn1, Imaging imIn2)
+{
+ CHOP((int) in1[x] * (int) in2[x] / 255, NULL);
+}
+
+Imaging
+ImagingChopScreen(Imaging imIn1, Imaging imIn2)
+{
+ CHOP(255 - ((int) (255 - in1[x]) * (int) (255 - in2[x])) / 255, NULL);
+}
+
+Imaging
+ImagingChopAdd(Imaging imIn1, Imaging imIn2, float scale, int offset)
+{
+ CHOP(((int) in1[x] + (int) in2[x]) / scale + offset, NULL);
+}
+
+Imaging
+ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset)
+{
+ CHOP(((int) in1[x] - (int) in2[x]) / scale + offset, NULL);
+}
+
+Imaging
+ImagingChopAnd(Imaging imIn1, Imaging imIn2)
+{
+ CHOP2((in1[x] && in2[x]) ? 255 : 0, "1");
+}
+
+Imaging
+ImagingChopOr(Imaging imIn1, Imaging imIn2)
+{
+ CHOP2((in1[x] || in2[x]) ? 255 : 0, "1");
+}
+
+Imaging
+ImagingChopXor(Imaging imIn1, Imaging imIn2)
+{
+ CHOP2(((in1[x] != 0) ^ (in2[x] != 0)) ? 255 : 0, "1");
+}
+
+Imaging
+ImagingChopAddModulo(Imaging imIn1, Imaging imIn2)
+{
+ CHOP2(in1[x] + in2[x], NULL);
+}
+
+Imaging
+ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2)
+{
+ CHOP2(in1[x] - in2[x], NULL);
+}
diff --git a/libImaging/Convert.c b/libImaging/Convert.c
new file mode 100644
index 000000000..25299aa82
--- /dev/null
+++ b/libImaging/Convert.c
@@ -0,0 +1,1132 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * convert images
+ *
+ * history:
+ * 1995-06-15 fl created
+ * 1995-11-28 fl added some "RGBA" and "CMYK" conversions
+ * 1996-04-22 fl added "1" conversions (same as "L")
+ * 1996-05-05 fl added palette conversions (hack)
+ * 1996-07-23 fl fixed "1" conversions to zero/non-zero convention
+ * 1996-11-01 fl fixed "P" to "L" and "RGB" to "1" conversions
+ * 1996-12-29 fl set alpha byte in RGB converters
+ * 1997-05-12 fl added ImagingConvert2
+ * 1997-05-30 fl added floating point support
+ * 1997-08-27 fl added "P" to "1" and "P" to "F" conversions
+ * 1998-01-11 fl added integer support
+ * 1998-07-01 fl added "YCbCr" support
+ * 1998-07-02 fl added "RGBX" conversions (sort of)
+ * 1998-07-04 fl added floyd-steinberg dithering
+ * 1998-07-12 fl changed "YCrCb" to "YCbCr" (!)
+ * 1998-12-29 fl added basic "I;16" and "I;16B" conversions
+ * 1999-02-03 fl added "RGBa", and "BGR" conversions (experimental)
+ * 2003-09-26 fl added "LA" and "PA" conversions (experimental)
+ * 2005-05-05 fl fixed "P" to "1" threshold
+ * 2005-12-08 fl fixed palette memory leak in topalette
+ *
+ * Copyright (c) 1997-2005 by Secret Labs AB.
+ * Copyright (c) 1995-1997 by Fredrik Lundh.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define CLIP(v) ((v) <= 0 ? 0 : (v) >= 255 ? 255 : (v))
+#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))
+
+/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */
+#define L(rgb)\
+ ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114)
+
+/* ------------------- */
+/* 1 (bit) conversions */
+/* ------------------- */
+
+static void
+bit2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ != 0) ? 255 : 0;
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+bit2ycbcr(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = (*in++ != 0) ? 255 : 0;
+ *out++ = 128;
+ *out++ = 128;
+ *out++ = 255;
+ }
+}
+
+/* ----------------- */
+/* RGB/L conversions */
+/* ----------------- */
+
+static void
+l2bit(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++)
+ *out++ = (*in++ >= 128) ? 255 : 0;
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+la2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = in[0];
+}
+
+static void
+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];
+ }
+}
+
+static void
+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;
+}
+
+static void
+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;
+}
+
+static void
+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;
+ out[3] = 255;
+ }
+}
+
+static void
+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;
+}
+
+static void
+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;
+}
+
+static void
+rgb2bgr15(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ UINT16* out = (UINT16*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ =
+ ((((UINT16)in[0])<<7)&0x7c00) +
+ ((((UINT16)in[1])<<2)&0x03e0) +
+ ((((UINT16)in[2])>>3)&0x001f);
+}
+
+static void
+rgb2bgr16(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ UINT16* out = (UINT16*) out_;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ =
+ ((((UINT16)in[0])<<8)&0xf800) +
+ ((((UINT16)in[1])<<3)&0x07e0) +
+ ((((UINT16)in[2])>>3)&0x001f);
+}
+
+static void
+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];
+ }
+}
+
+/* ---------------- */
+/* RGBA conversions */
+/* ---------------- */
+
+static void
+rgb2rgba(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = 255; in++;
+ }
+}
+
+static void
+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;
+ out[3] = in[3];
+ }
+}
+
+static void
+rgba2rgb(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = *in++;
+ *out++ = 255; in++;
+ }
+}
+
+static void
+rgba2rgba(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ unsigned int alpha, tmp;
+ for (x = 0; x < xsize; x++) {
+ alpha = in[3];
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = MULDIV255(*in++, alpha, tmp);
+ *out++ = *in++;
+ }
+}
+
+/* ---------------- */
+/* CMYK conversions */
+/* ---------------- */
+
+static void
+l2cmyk(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = ~(*in++);
+ }
+}
+
+static void
+rgb2cmyk(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ /* Note: no undercolour removal */
+ *out++ = ~(*in++);
+ *out++ = ~(*in++);
+ *out++ = ~(*in++);
+ *out++ = 0; in++;
+ }
+}
+
+static void
+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;
+ }
+}
+
+/* ------------- */
+/* I conversions */
+/* ------------- */
+
+static void
+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;
+}
+
+static void
+l2i(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (INT32) *in++;
+}
+
+static void
+i2l(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++, out++) {
+ if (*in <= 0)
+ *out = 0;
+ else if (*in >= 255)
+ *out = 255;
+ else
+ *out = (UINT8) *in;
+ }
+}
+
+static void
+i2f(UINT8* out_, const UINT8* in_, int xsize)
+{
+ int x;
+ INT32* in = (INT32*) in_;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (FLOAT32) *in++;
+}
+
+/* ------------- */
+/* F conversions */
+/* ------------- */
+
+static void
+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;
+}
+
+static void
+l2f(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ FLOAT32* out = (FLOAT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (FLOAT32) *in++;
+}
+
+static void
+f2l(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x;
+ FLOAT32* in = (FLOAT32*) in_;
+ for (x = 0; x < xsize; x++, in++, out++) {
+ if (*in <= 0.0)
+ *out = 0;
+ else if (*in >= 255.0)
+ *out = 255;
+ else
+ *out = (UINT8) *in;
+ }
+}
+
+static void
+f2i(UINT8* out_, const UINT8* in_, int xsize)
+{
+ int x;
+ FLOAT32* in = (FLOAT32*) in_;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++)
+ *out++ = (INT32) *in++;
+}
+
+/* ----------------- */
+/* YCbCr conversions */
+/* ----------------- */
+
+/* See ConvertYCbCr.c for RGB/YCbCr tables */
+
+static void
+l2ycbcr(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++) {
+ *out++ = *in++;
+ *out++ = 128;
+ *out++ = 128;
+ *out++ = 255;
+ }
+}
+
+static void
+ycbcr2l(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 4)
+ *out++ = in[0];
+}
+
+/* ------------------------- */
+/* I;16 (16-bit) conversions */
+/* ------------------------- */
+
+static void
+I_I16L(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x, v;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++) {
+ v = CLIP16(*in);
+ *out++ = (UINT8) v;
+ *out++ = (UINT8) (v >> 8);
+ }
+}
+
+static void
+I_I16B(UINT8* out, const UINT8* in_, int xsize)
+{
+ int x, v;
+ INT32* in = (INT32*) in_;
+ for (x = 0; x < xsize; x++, in++) {
+ v = CLIP16(*in);
+ *out++ = (UINT8) (v >> 8);
+ *out++ = (UINT8) v;
+ }
+}
+
+
+static void
+I16L_I(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = in[0] + ((int) in[1] << 8);
+}
+
+
+static void
+I16B_I(UINT8* out_, const UINT8* in, int xsize)
+{
+ int x;
+ INT32* out = (INT32*) out_;
+ for (x = 0; x < xsize; x++, in += 2)
+ *out++ = in[1] + ((int) in[0] << 8);
+}
+
+static void
+L_I16L(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in++) {
+ *out++ = *in;
+ *out++ = 0;
+ }
+}
+
+static void
+L_I16B(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in++) {
+ *out++ = 0;
+ *out++ = *in;
+ }
+}
+
+static void
+I16L_L(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 2)
+ if (in[1] != 0)
+ *out++ = 255;
+ else
+ *out++ = in[0];
+}
+
+static void
+I16B_L(UINT8* out, const UINT8* in, int xsize)
+{
+ int x;
+ for (x = 0; x < xsize; x++, in += 2)
+ if (in[0] != 0)
+ *out++ = 255;
+ else
+ *out++ = in[1];
+}
+
+static struct {
+ const char* from;
+ const char* to;
+ ImagingShuffler convert;
+} converters[] = {
+
+ { "1", "L", bit2l },
+ { "1", "I", bit2i },
+ { "1", "F", bit2f },
+ { "1", "RGB", bit2rgb },
+ { "1", "RGBA", bit2rgb },
+ { "1", "RGBX", bit2rgb },
+ { "1", "CMYK", bit2cmyk },
+ { "1", "YCbCr", bit2ycbcr },
+
+ { "L", "1", l2bit },
+ { "L", "LA", l2la },
+ { "L", "I", l2i },
+ { "L", "F", l2f },
+ { "L", "RGB", l2rgb },
+ { "L", "RGBA", l2rgb },
+ { "L", "RGBX", l2rgb },
+ { "L", "CMYK", l2cmyk },
+ { "L", "YCbCr", l2ycbcr },
+
+ { "LA", "L", la2l },
+ { "LA", "RGB", la2rgb },
+ { "LA", "RGBX", la2rgb },
+ { "LA", "RGBA", la2rgb },
+
+ { "I", "L", i2l },
+ { "I", "F", i2f },
+
+ { "F", "L", f2l },
+ { "F", "I", f2i },
+
+ { "RGB", "1", rgb2bit },
+ { "RGB", "L", rgb2l },
+ { "RGB", "LA", rgb2la },
+ { "RGB", "I", rgb2i },
+ { "RGB", "F", rgb2f },
+ { "RGB", "BGR;15", rgb2bgr15 },
+ { "RGB", "BGR;16", rgb2bgr16 },
+ { "RGB", "BGR;24", rgb2bgr24 },
+ { "RGB", "RGBA", rgb2rgba },
+ { "RGB", "RGBX", rgb2rgba },
+ { "RGB", "CMYK", rgb2cmyk },
+ { "RGB", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "RGBA", "1", rgb2bit },
+ { "RGBA", "L", rgb2l },
+ { "RGBA", "LA", rgba2la },
+ { "RGBA", "I", rgb2i },
+ { "RGBA", "F", rgb2f },
+ { "RGBA", "RGB", rgba2rgb },
+ { "RGBA", "RGBa", rgba2rgba },
+ { "RGBA", "RGBX", rgb2rgba },
+ { "RGBA", "CMYK", rgb2cmyk },
+ { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "RGBX", "1", rgb2bit },
+ { "RGBX", "L", rgb2l },
+ { "RGBA", "I", rgb2i },
+ { "RGBA", "F", rgb2f },
+ { "RGBX", "RGB", rgba2rgb },
+ { "RGBX", "CMYK", rgb2cmyk },
+ { "RGBX", "YCbCr", ImagingConvertRGB2YCbCr },
+
+ { "CMYK", "RGB", cmyk2rgb },
+ { "CMYK", "RGBA", cmyk2rgb },
+ { "CMYK", "RGBX", cmyk2rgb },
+
+ { "YCbCr", "L", ycbcr2l },
+ { "YCbCr", "RGB", ImagingConvertYCbCr2RGB },
+
+ { "I", "I;16", I_I16L },
+ { "I;16", "I", I16L_I },
+ { "L", "I;16", L_I16L },
+ { "I;16", "L", I16L_L },
+
+ { "I", "I;16L", I_I16L },
+ { "I;16L", "I", I16L_I },
+ { "I", "I;16B", I_I16B },
+ { "I;16B", "I", I16B_I },
+
+ { "L", "I;16L", L_I16L },
+ { "I;16L", "L", I16L_L },
+ { "L", "I;16B", L_I16B },
+ { "I;16B", "L", I16B_L },
+
+ { NULL }
+};
+
+/* FIXME: translate indexed versions to pointer versions below this line */
+
+/* ------------------- */
+/* Palette conversions */
+/* ------------------- */
+
+static void
+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;
+}
+
+static void
+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;
+}
+
+static void
+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++ = in[1];
+ }
+}
+
+static void
+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;
+}
+
+static void
+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;
+}
+
+static void
+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;
+ }
+}
+
+static void
+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];
+ }
+}
+
+static void
+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];
+ }
+}
+
+static void
+p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ p2rgb(out, in, xsize, palette);
+ rgb2cmyk(out, out, xsize);
+}
+
+static void
+p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette)
+{
+ p2rgb(out, in, xsize, palette);
+ ImagingConvertRGB2YCbCr(out, out, xsize);
+}
+
+static Imaging
+frompalette(Imaging imOut, Imaging imIn, const char *mode)
+{
+ ImagingSectionCookie cookie;
+ int alpha;
+ int y;
+ void (*convert)(UINT8*, const UINT8*, int, const UINT8*);
+
+ /* Map palette image to L, RGB, RGBA, or CMYK */
+
+ if (!imIn->palette)
+ return (Imaging) ImagingError_ValueError("no palette");
+
+ alpha = !strcmp(imIn->mode, "PA");
+
+ if (strcmp(mode, "1") == 0)
+ convert = p2bit;
+ else if (strcmp(mode, "L") == 0)
+ convert = p2l;
+ else if (strcmp(mode, "LA") == 0)
+ convert = (alpha) ? pa2la : p2l;
+ else if (strcmp(mode, "I") == 0)
+ convert = p2i;
+ else if (strcmp(mode, "F") == 0)
+ convert = p2f;
+ else if (strcmp(mode, "RGB") == 0)
+ convert = p2rgb;
+ else if (strcmp(mode, "RGBA") == 0)
+ convert = (alpha) ? pa2rgba : p2rgba;
+ else if (strcmp(mode, "RGBX") == 0)
+ convert = p2rgba;
+ else if (strcmp(mode, "CMYK") == 0)
+ convert = p2cmyk;
+ else if (strcmp(mode, "YCbCr") == 0)
+ convert = p2ycbcr;
+ else
+ return (Imaging) ImagingError_ValueError("conversion not supported");
+
+ 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, imIn->palette->palette);
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+static Imaging
+topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither)
+{
+ ImagingSectionCookie cookie;
+ int x, y;
+ ImagingPalette palette = inpalette;;
+
+ /* 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");
+
+ if (palette == NULL) {
+ /* FIXME: make user configurable */
+ if (imIn->bands == 1)
+ palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */
+ else
+ palette = ImagingPaletteNewBrowser(); /* Standard colour cube */
+ }
+
+ if (!palette)
+ return (Imaging) ImagingError_ValueError("no palette");
+
+ imOut = ImagingNew2("P", imOut, imIn);
+ if (!imOut) {
+ if (palette != inpalette)
+ ImagingPaletteDelete(palette);
+ return NULL;
+ }
+
+ ImagingPaletteDelete(imOut->palette);
+ imOut->palette = ImagingPaletteDuplicate(palette);
+
+ if (imIn->bands == 1) {
+ /* greyscale image */
+
+ /* Greyscale palette: copy data as is */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++)
+ memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
+ ImagingSectionLeave(&cookie);
+
+ } else {
+ /* colour image */
+
+ /* Create mapping cache */
+ if (ImagingPaletteCachePrepare(palette) < 0) {
+ ImagingDelete(imOut);
+ if (palette != inpalette)
+ ImagingPaletteDelete(palette);
+ return NULL;
+ }
+
+ if (dither) {
+ /* floyd-steinberg dither */
+
+ int* errors;
+ errors = calloc(imIn->xsize + 1, sizeof(int) * 3);
+ if (!errors) {
+ ImagingDelete(imOut);
+ return ImagingError_MemoryError();
+ }
+
+ /* Map each pixel to the nearest palette entry */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int r, r0, r1, r2;
+ int g, g0, g1, g2;
+ int b, b0, b1, b2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+ int* e = errors;
+
+ r = r0 = r1 = 0;
+ g = g0 = g1 = 0;
+ b = b0 = b1 = b2 = 0;
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+ int d2;
+ INT16* cache;
+
+ r = CLIP(in[0] + (r + e[3+0])/16);
+ g = CLIP(in[1] + (g + e[3+1])/16);
+ b = CLIP(in[2] + (b + e[3+2])/16);
+
+ /* get closest colour */
+ cache = &ImagingPaletteCache(palette, r, g, b);
+ if (cache[0] == 0x100)
+ ImagingPaletteCacheUpdate(palette, r, g, b);
+ out[x] = (UINT8) cache[0];
+
+ r -= (int) palette->palette[cache[0]*4];
+ g -= (int) palette->palette[cache[0]*4+1];
+ b -= (int) palette->palette[cache[0]*4+2];
+
+ /* propagate errors (don't ask ;-) */
+ r2 = r; d2 = r + r; r += d2; e[0] = r + r0;
+ r += d2; r0 = r + r1; r1 = r2; r += d2;
+ g2 = g; d2 = g + g; g += d2; e[1] = g + g0;
+ g += d2; g0 = g + g1; g1 = g2; g += d2;
+ b2 = b; d2 = b + b; b += d2; e[2] = b + b0;
+ b += d2; b0 = b + b1; b1 = b2; b += d2;
+
+ e += 3;
+
+ }
+
+ e[0] = b0;
+ e[1] = b1;
+ e[2] = b2;
+
+ }
+ ImagingSectionLeave(&cookie);
+ free(errors);
+
+ } else {
+
+ /* closest colour */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int r, g, b;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+ INT16* cache;
+
+ r = in[0]; g = in[1]; b = in[2];
+
+ /* get closest colour */
+ cache = &ImagingPaletteCache(palette, r, g, b);
+ if (cache[0] == 0x100)
+ ImagingPaletteCacheUpdate(palette, r, g, b);
+ out[x] = (UINT8) cache[0];
+
+ }
+ }
+ ImagingSectionLeave(&cookie);
+
+ }
+ if (inpalette != palette)
+ ImagingPaletteCacheDelete(palette);
+ }
+
+ if (inpalette != palette)
+ ImagingPaletteDelete(palette);
+
+ return imOut;
+}
+
+static Imaging
+tobilevel(Imaging imOut, Imaging imIn, int dither)
+{
+ ImagingSectionCookie cookie;
+ int x, y;
+ int* errors;
+
+ /* 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");
+
+ imOut = ImagingNew2("1", imOut, imIn);
+ if (!imOut)
+ return NULL;
+
+ errors = calloc(imIn->xsize + 1, sizeof(int));
+ if (!errors) {
+ ImagingDelete(imOut);
+ return ImagingError_MemoryError();
+ }
+
+ if (imIn->bands == 1) {
+
+ /* map each pixel to black or white, using error diffusion */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int l, l0, l1, l2, d2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ l = l0 = l1 = 0;
+
+ for (x = 0; x < imIn->xsize; x++) {
+
+ /* pick closest colour */
+ l = CLIP(in[x] + (l + errors[x+1])/16);
+ out[x] = (l > 128) ? 255 : 0;
+
+ /* propagate errors */
+ l -= (int) out[x];
+ l2 = l; d2 = l + l; l += d2; errors[x] = l + l0;
+ l += d2; l0 = l + l1; l1 = l2; l += d2;
+ }
+
+ errors[x] = l0;
+
+ }
+ ImagingSectionLeave(&cookie);
+
+ } else {
+
+ /* map each pixel to black or white, using error diffusion */
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ int l, l0, l1, l2, d2;
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = imOut->image8[y];
+
+ l = l0 = l1 = 0;
+
+ for (x = 0; x < imIn->xsize; x++, in += 4) {
+
+ /* pick closest colour */
+ l = CLIP(L(in)/1000 + (l + errors[x+1])/16);
+ out[x] = (l > 128) ? 255 : 0;
+
+ /* propagate errors */
+ l -= (int) out[x];
+ l2 = l; d2 = l + l; l += d2; errors[x] = l + l0;
+ l += d2; l0 = l + l1; l1 = l2; l += d2;
+
+ }
+
+ errors[x] = l0;
+
+ }
+ ImagingSectionLeave(&cookie);
+ }
+
+ free(errors);
+
+ return imOut;
+}
+
+
+static Imaging
+convert(Imaging imOut, Imaging imIn, const char *mode,
+ ImagingPalette palette, int dither)
+{
+ ImagingSectionCookie cookie;
+ ImagingShuffler convert;
+ int y;
+
+ if (!imIn)
+ return (Imaging) ImagingError_ModeError();
+
+ if (!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);
+
+
+ /* test for special conversions */
+
+ if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0)
+ return frompalette(imOut, imIn, mode);
+
+ if (strcmp(mode, "P") == 0)
+ return topalette(imOut, imIn, palette, dither);
+
+ if (dither && strcmp(mode, "1") == 0)
+ return tobilevel(imOut, imIn, dither);
+
+
+ /* standard conversion machinery */
+
+ 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 (!convert)
+#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", imIn->mode, mode);
+ return (Imaging) ImagingError_ValueError(buf);
+ }
+#endif
+
+ 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);
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+Imaging
+ImagingConvert(Imaging imIn, const char *mode,
+ ImagingPalette palette, int dither)
+{
+ return convert(NULL, imIn, mode, palette, dither);
+}
+
+Imaging
+ImagingConvert2(Imaging imOut, Imaging imIn)
+{
+ return convert(imOut, imIn, imOut->mode, NULL, 0);
+}
+
+Imaging
+ImagingConvertInPlace(Imaging imIn, const char* mode)
+{
+ ImagingSectionCookie cookie;
+ ImagingShuffler convert;
+ int y;
+
+ /* limited support for inplace conversion */
+ if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0)
+ convert = l2bit;
+ else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0)
+ convert = bit2l;
+ else
+ return ImagingError_ModeError();
+
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++)
+ (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y],
+ imIn->xsize);
+ ImagingSectionLeave(&cookie);
+
+ return imIn;
+}
diff --git a/libImaging/ConvertYCbCr.c b/libImaging/ConvertYCbCr.c
new file mode 100644
index 000000000..40cd01780
--- /dev/null
+++ b/libImaging/ConvertYCbCr.c
@@ -0,0 +1,387 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * code to convert YCbCr data
+ *
+ * history:
+ * 98-07-01 hk Created
+ *
+ * Copyright (c) Secret Labs AB 1998
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+/* JPEG/JFIF YCbCr conversions
+
+ Y = R * 0.29900 + G * 0.58700 + B * 0.11400
+ Cb = R * -0.16874 + G * -0.33126 + B * 0.50000 + 128
+ Cr = R * 0.50000 + G * -0.41869 + B * -0.08131 + 128
+
+ R = Y + + (Cr - 128) * 1.40200
+ G = Y + (Cb - 128) * -0.34414 + (Cr - 128) * -0.71414
+ B = Y + (Cb - 128) * 1.77200
+
+*/
+
+#define SCALE 6 /* bits */
+
+static INT16 Y_R[] = { 0, 19, 38, 57, 77, 96, 115, 134, 153, 172, 191,
+210, 230, 249, 268, 287, 306, 325, 344, 364, 383, 402, 421, 440, 459,
+478, 498, 517, 536, 555, 574, 593, 612, 631, 651, 670, 689, 708, 727,
+746, 765, 785, 804, 823, 842, 861, 880, 899, 919, 938, 957, 976, 995,
+1014, 1033, 1052, 1072, 1091, 1110, 1129, 1148, 1167, 1186, 1206,
+1225, 1244, 1263, 1282, 1301, 1320, 1340, 1359, 1378, 1397, 1416,
+1435, 1454, 1473, 1493, 1512, 1531, 1550, 1569, 1588, 1607, 1627,
+1646, 1665, 1684, 1703, 1722, 1741, 1761, 1780, 1799, 1818, 1837,
+1856, 1875, 1894, 1914, 1933, 1952, 1971, 1990, 2009, 2028, 2048,
+2067, 2086, 2105, 2124, 2143, 2162, 2182, 2201, 2220, 2239, 2258,
+2277, 2296, 2315, 2335, 2354, 2373, 2392, 2411, 2430, 2449, 2469,
+2488, 2507, 2526, 2545, 2564, 2583, 2602, 2622, 2641, 2660, 2679,
+2698, 2717, 2736, 2756, 2775, 2794, 2813, 2832, 2851, 2870, 2890,
+2909, 2928, 2947, 2966, 2985, 3004, 3023, 3043, 3062, 3081, 3100,
+3119, 3138, 3157, 3177, 3196, 3215, 3234, 3253, 3272, 3291, 3311,
+3330, 3349, 3368, 3387, 3406, 3425, 3444, 3464, 3483, 3502, 3521,
+3540, 3559, 3578, 3598, 3617, 3636, 3655, 3674, 3693, 3712, 3732,
+3751, 3770, 3789, 3808, 3827, 3846, 3865, 3885, 3904, 3923, 3942,
+3961, 3980, 3999, 4019, 4038, 4057, 4076, 4095, 4114, 4133, 4153,
+4172, 4191, 4210, 4229, 4248, 4267, 4286, 4306, 4325, 4344, 4363,
+4382, 4401, 4420, 4440, 4459, 4478, 4497, 4516, 4535, 4554, 4574,
+4593, 4612, 4631, 4650, 4669, 4688, 4707, 4727, 4746, 4765, 4784,
+4803, 4822, 4841, 4861, 4880 };
+
+static INT16 Y_G[] = { 0, 38, 75, 113, 150, 188, 225, 263, 301, 338,
+376, 413, 451, 488, 526, 564, 601, 639, 676, 714, 751, 789, 826, 864,
+902, 939, 977, 1014, 1052, 1089, 1127, 1165, 1202, 1240, 1277, 1315,
+1352, 1390, 1428, 1465, 1503, 1540, 1578, 1615, 1653, 1691, 1728,
+1766, 1803, 1841, 1878, 1916, 1954, 1991, 2029, 2066, 2104, 2141,
+2179, 2217, 2254, 2292, 2329, 2367, 2404, 2442, 2479, 2517, 2555,
+2592, 2630, 2667, 2705, 2742, 2780, 2818, 2855, 2893, 2930, 2968,
+3005, 3043, 3081, 3118, 3156, 3193, 3231, 3268, 3306, 3344, 3381,
+3419, 3456, 3494, 3531, 3569, 3607, 3644, 3682, 3719, 3757, 3794,
+3832, 3870, 3907, 3945, 3982, 4020, 4057, 4095, 4132, 4170, 4208,
+4245, 4283, 4320, 4358, 4395, 4433, 4471, 4508, 4546, 4583, 4621,
+4658, 4696, 4734, 4771, 4809, 4846, 4884, 4921, 4959, 4997, 5034,
+5072, 5109, 5147, 5184, 5222, 5260, 5297, 5335, 5372, 5410, 5447,
+5485, 5522, 5560, 5598, 5635, 5673, 5710, 5748, 5785, 5823, 5861,
+5898, 5936, 5973, 6011, 6048, 6086, 6124, 6161, 6199, 6236, 6274,
+6311, 6349, 6387, 6424, 6462, 6499, 6537, 6574, 6612, 6650, 6687,
+6725, 6762, 6800, 6837, 6875, 6913, 6950, 6988, 7025, 7063, 7100,
+7138, 7175, 7213, 7251, 7288, 7326, 7363, 7401, 7438, 7476, 7514,
+7551, 7589, 7626, 7664, 7701, 7739, 7777, 7814, 7852, 7889, 7927,
+7964, 8002, 8040, 8077, 8115, 8152, 8190, 8227, 8265, 8303, 8340,
+8378, 8415, 8453, 8490, 8528, 8566, 8603, 8641, 8678, 8716, 8753,
+8791, 8828, 8866, 8904, 8941, 8979, 9016, 9054, 9091, 9129, 9167,
+9204, 9242, 9279, 9317, 9354, 9392, 9430, 9467, 9505, 9542, 9580 };
+
+static INT16 Y_B[] = { 0, 7, 15, 22, 29, 36, 44, 51, 58, 66, 73, 80,
+88, 95, 102, 109, 117, 124, 131, 139, 146, 153, 161, 168, 175, 182,
+190, 197, 204, 212, 219, 226, 233, 241, 248, 255, 263, 270, 277, 285,
+292, 299, 306, 314, 321, 328, 336, 343, 350, 358, 365, 372, 379, 387,
+394, 401, 409, 416, 423, 430, 438, 445, 452, 460, 467, 474, 482, 489,
+496, 503, 511, 518, 525, 533, 540, 547, 554, 562, 569, 576, 584, 591,
+598, 606, 613, 620, 627, 635, 642, 649, 657, 664, 671, 679, 686, 693,
+700, 708, 715, 722, 730, 737, 744, 751, 759, 766, 773, 781, 788, 795,
+803, 810, 817, 824, 832, 839, 846, 854, 861, 868, 876, 883, 890, 897,
+905, 912, 919, 927, 934, 941, 948, 956, 963, 970, 978, 985, 992, 1000,
+1007, 1014, 1021, 1029, 1036, 1043, 1051, 1058, 1065, 1073, 1080,
+1087, 1094, 1102, 1109, 1116, 1124, 1131, 1138, 1145, 1153, 1160,
+1167, 1175, 1182, 1189, 1197, 1204, 1211, 1218, 1226, 1233, 1240,
+1248, 1255, 1262, 1270, 1277, 1284, 1291, 1299, 1306, 1313, 1321,
+1328, 1335, 1342, 1350, 1357, 1364, 1372, 1379, 1386, 1394, 1401,
+1408, 1415, 1423, 1430, 1437, 1445, 1452, 1459, 1466, 1474, 1481,
+1488, 1496, 1503, 1510, 1518, 1525, 1532, 1539, 1547, 1554, 1561,
+1569, 1576, 1583, 1591, 1598, 1605, 1612, 1620, 1627, 1634, 1642,
+1649, 1656, 1663, 1671, 1678, 1685, 1693, 1700, 1707, 1715, 1722,
+1729, 1736, 1744, 1751, 1758, 1766, 1773, 1780, 1788, 1795, 1802,
+1809, 1817, 1824, 1831, 1839, 1846, 1853, 1860 };
+
+static INT16 Cb_R[] = { 0, -10, -21, -31, -42, -53, -64, -75, -85,
+-96, -107, -118, -129, -139, -150, -161, -172, -183, -193, -204, -215,
+-226, -237, -247, -258, -269, -280, -291, -301, -312, -323, -334,
+-345, -355, -366, -377, -388, -399, -409, -420, -431, -442, -453,
+-463, -474, -485, -496, -507, -517, -528, -539, -550, -561, -571,
+-582, -593, -604, -615, -625, -636, -647, -658, -669, -679, -690,
+-701, -712, -723, -733, -744, -755, -766, -777, -787, -798, -809,
+-820, -831, -841, -852, -863, -874, -885, -895, -906, -917, -928,
+-939, -949, -960, -971, -982, -993, -1003, -1014, -1025, -1036, -1047,
+-1057, -1068, -1079, -1090, -1101, -1111, -1122, -1133, -1144, -1155,
+-1165, -1176, -1187, -1198, -1209, -1219, -1230, -1241, -1252, -1263,
+-1273, -1284, -1295, -1306, -1317, -1327, -1338, -1349, -1360, -1371,
+-1381, -1392, -1403, -1414, -1425, -1435, -1446, -1457, -1468, -1479,
+-1489, -1500, -1511, -1522, -1533, -1543, -1554, -1565, -1576, -1587,
+-1597, -1608, -1619, -1630, -1641, -1651, -1662, -1673, -1684, -1694,
+-1705, -1716, -1727, -1738, -1748, -1759, -1770, -1781, -1792, -1802,
+-1813, -1824, -1835, -1846, -1856, -1867, -1878, -1889, -1900, -1910,
+-1921, -1932, -1943, -1954, -1964, -1975, -1986, -1997, -2008, -2018,
+-2029, -2040, -2051, -2062, -2072, -2083, -2094, -2105, -2116, -2126,
+-2137, -2148, -2159, -2170, -2180, -2191, -2202, -2213, -2224, -2234,
+-2245, -2256, -2267, -2278, -2288, -2299, -2310, -2321, -2332, -2342,
+-2353, -2364, -2375, -2386, -2396, -2407, -2418, -2429, -2440, -2450,
+-2461, -2472, -2483, -2494, -2504, -2515, -2526, -2537, -2548, -2558,
+-2569, -2580, -2591, -2602, -2612, -2623, -2634, -2645, -2656, -2666,
+-2677, -2688, -2699, -2710, -2720, -2731, -2742, -2753 };
+
+static INT16 Cb_G[] = { 0, -20, -41, -63, -84, -105, -126, -147, -169,
+-190, -211, -232, -253, -275, -296, -317, -338, -359, -381, -402,
+-423, -444, -465, -487, -508, -529, -550, -571, -593, -614, -635,
+-656, -677, -699, -720, -741, -762, -783, -805, -826, -847, -868,
+-889, -911, -932, -953, -974, -995, -1017, -1038, -1059, -1080, -1101,
+-1123, -1144, -1165, -1186, -1207, -1229, -1250, -1271, -1292, -1313,
+-1335, -1356, -1377, -1398, -1419, -1441, -1462, -1483, -1504, -1525,
+-1547, -1568, -1589, -1610, -1631, -1653, -1674, -1695, -1716, -1737,
+-1759, -1780, -1801, -1822, -1843, -1865, -1886, -1907, -1928, -1949,
+-1971, -1992, -2013, -2034, -2055, -2077, -2098, -2119, -2140, -2161,
+-2183, -2204, -2225, -2246, -2267, -2289, -2310, -2331, -2352, -2373,
+-2395, -2416, -2437, -2458, -2479, -2501, -2522, -2543, -2564, -2585,
+-2607, -2628, -2649, -2670, -2691, -2713, -2734, -2755, -2776, -2797,
+-2819, -2840, -2861, -2882, -2903, -2925, -2946, -2967, -2988, -3009,
+-3031, -3052, -3073, -3094, -3115, -3137, -3158, -3179, -3200, -3221,
+-3243, -3264, -3285, -3306, -3328, -3349, -3370, -3391, -3412, -3434,
+-3455, -3476, -3497, -3518, -3540, -3561, -3582, -3603, -3624, -3646,
+-3667, -3688, -3709, -3730, -3752, -3773, -3794, -3815, -3836, -3858,
+-3879, -3900, -3921, -3942, -3964, -3985, -4006, -4027, -4048, -4070,
+-4091, -4112, -4133, -4154, -4176, -4197, -4218, -4239, -4260, -4282,
+-4303, -4324, -4345, -4366, -4388, -4409, -4430, -4451, -4472, -4494,
+-4515, -4536, -4557, -4578, -4600, -4621, -4642, -4663, -4684, -4706,
+-4727, -4748, -4769, -4790, -4812, -4833, -4854, -4875, -4896, -4918,
+-4939, -4960, -4981, -5002, -5024, -5045, -5066, -5087, -5108, -5130,
+-5151, -5172, -5193, -5214, -5236, -5257, -5278, -5299, -5320, -5342,
+-5363, -5384, -5405 };
+
+static INT16 Cb_B[] = { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
+320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736,
+768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152,
+1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408, 1440, 1472, 1504,
+1536, 1568, 1600, 1632, 1664, 1696, 1728, 1760, 1792, 1824, 1856,
+1888, 1920, 1952, 1984, 2016, 2048, 2080, 2112, 2144, 2176, 2208,
+2240, 2272, 2304, 2336, 2368, 2400, 2432, 2464, 2496, 2528, 2560,
+2592, 2624, 2656, 2688, 2720, 2752, 2784, 2816, 2848, 2880, 2912,
+2944, 2976, 3008, 3040, 3072, 3104, 3136, 3168, 3200, 3232, 3264,
+3296, 3328, 3360, 3392, 3424, 3456, 3488, 3520, 3552, 3584, 3616,
+3648, 3680, 3712, 3744, 3776, 3808, 3840, 3872, 3904, 3936, 3968,
+4000, 4032, 4064, 4096, 4128, 4160, 4192, 4224, 4256, 4288, 4320,
+4352, 4384, 4416, 4448, 4480, 4512, 4544, 4576, 4608, 4640, 4672,
+4704, 4736, 4768, 4800, 4832, 4864, 4896, 4928, 4960, 4992, 5024,
+5056, 5088, 5120, 5152, 5184, 5216, 5248, 5280, 5312, 5344, 5376,
+5408, 5440, 5472, 5504, 5536, 5568, 5600, 5632, 5664, 5696, 5728,
+5760, 5792, 5824, 5856, 5888, 5920, 5952, 5984, 6016, 6048, 6080,
+6112, 6144, 6176, 6208, 6240, 6272, 6304, 6336, 6368, 6400, 6432,
+6464, 6496, 6528, 6560, 6592, 6624, 6656, 6688, 6720, 6752, 6784,
+6816, 6848, 6880, 6912, 6944, 6976, 7008, 7040, 7072, 7104, 7136,
+7168, 7200, 7232, 7264, 7296, 7328, 7360, 7392, 7424, 7456, 7488,
+7520, 7552, 7584, 7616, 7648, 7680, 7712, 7744, 7776, 7808, 7840,
+7872, 7904, 7936, 7968, 8000, 8032, 8064, 8096, 8128, 8160 };
+
+#define Cr_R Cb_B
+
+static INT16 Cr_G[] = { 0, -26, -53, -79, -106, -133, -160, -187,
+-213, -240, -267, -294, -321, -347, -374, -401, -428, -455, -481,
+-508, -535, -562, -589, -615, -642, -669, -696, -722, -749, -776,
+-803, -830, -856, -883, -910, -937, -964, -990, -1017, -1044, -1071,
+-1098, -1124, -1151, -1178, -1205, -1232, -1258, -1285, -1312, -1339,
+-1366, -1392, -1419, -1446, -1473, -1500, -1526, -1553, -1580, -1607,
+-1634, -1660, -1687, -1714, -1741, -1768, -1794, -1821, -1848, -1875,
+-1902, -1928, -1955, -1982, -2009, -2036, -2062, -2089, -2116, -2143,
+-2169, -2196, -2223, -2250, -2277, -2303, -2330, -2357, -2384, -2411,
+-2437, -2464, -2491, -2518, -2545, -2571, -2598, -2625, -2652, -2679,
+-2705, -2732, -2759, -2786, -2813, -2839, -2866, -2893, -2920, -2947,
+-2973, -3000, -3027, -3054, -3081, -3107, -3134, -3161, -3188, -3215,
+-3241, -3268, -3295, -3322, -3349, -3375, -3402, -3429, -3456, -3483,
+-3509, -3536, -3563, -3590, -3616, -3643, -3670, -3697, -3724, -3750,
+-3777, -3804, -3831, -3858, -3884, -3911, -3938, -3965, -3992, -4018,
+-4045, -4072, -4099, -4126, -4152, -4179, -4206, -4233, -4260, -4286,
+-4313, -4340, -4367, -4394, -4420, -4447, -4474, -4501, -4528, -4554,
+-4581, -4608, -4635, -4662, -4688, -4715, -4742, -4769, -4796, -4822,
+-4849, -4876, -4903, -4929, -4956, -4983, -5010, -5037, -5063, -5090,
+-5117, -5144, -5171, -5197, -5224, -5251, -5278, -5305, -5331, -5358,
+-5385, -5412, -5439, -5465, -5492, -5519, -5546, -5573, -5599, -5626,
+-5653, -5680, -5707, -5733, -5760, -5787, -5814, -5841, -5867, -5894,
+-5921, -5948, -5975, -6001, -6028, -6055, -6082, -6109, -6135, -6162,
+-6189, -6216, -6243, -6269, -6296, -6323, -6350, -6376, -6403, -6430,
+-6457, -6484, -6510, -6537, -6564, -6591, -6618, -6644, -6671, -6698,
+-6725, -6752, -6778, -6805, -6832 };
+
+static INT16 Cr_B[] = { 0, -4, -9, -15, -20, -25, -30, -35, -41, -46,
+-51, -56, -61, -67, -72, -77, -82, -87, -93, -98, -103, -108, -113,
+-119, -124, -129, -134, -140, -145, -150, -155, -160, -166, -171,
+-176, -181, -186, -192, -197, -202, -207, -212, -218, -223, -228,
+-233, -238, -244, -249, -254, -259, -264, -270, -275, -280, -285,
+-290, -296, -301, -306, -311, -316, -322, -327, -332, -337, -342,
+-348, -353, -358, -363, -368, -374, -379, -384, -389, -394, -400,
+-405, -410, -415, -421, -426, -431, -436, -441, -447, -452, -457,
+-462, -467, -473, -478, -483, -488, -493, -499, -504, -509, -514,
+-519, -525, -530, -535, -540, -545, -551, -556, -561, -566, -571,
+-577, -582, -587, -592, -597, -603, -608, -613, -618, -623, -629,
+-634, -639, -644, -649, -655, -660, -665, -670, -675, -681, -686,
+-691, -696, -702, -707, -712, -717, -722, -728, -733, -738, -743,
+-748, -754, -759, -764, -769, -774, -780, -785, -790, -795, -800,
+-806, -811, -816, -821, -826, -832, -837, -842, -847, -852, -858,
+-863, -868, -873, -878, -884, -889, -894, -899, -904, -910, -915,
+-920, -925, -930, -936, -941, -946, -951, -957, -962, -967, -972,
+-977, -983, -988, -993, -998, -1003, -1009, -1014, -1019, -1024,
+-1029, -1035, -1040, -1045, -1050, -1055, -1061, -1066, -1071, -1076,
+-1081, -1087, -1092, -1097, -1102, -1107, -1113, -1118, -1123, -1128,
+-1133, -1139, -1144, -1149, -1154, -1159, -1165, -1170, -1175, -1180,
+-1185, -1191, -1196, -1201, -1206, -1211, -1217, -1222, -1227, -1232,
+-1238, -1243, -1248, -1253, -1258, -1264, -1269, -1274, -1279, -1284,
+-1290, -1295, -1300, -1305, -1310, -1316, -1321, -1326 };
+
+static INT16 R_Cr[] = { -11484, -11394, -11305, -11215, -11125,
+-11036, -10946, -10856, -10766, -10677, -10587, -10497, -10407,
+-10318, -10228, -10138, -10049, -9959, -9869, -9779, -9690, -9600,
+-9510, -9420, -9331, -9241, -9151, -9062, -8972, -8882, -8792, -8703,
+-8613, -8523, -8433, -8344, -8254, -8164, -8075, -7985, -7895, -7805,
+-7716, -7626, -7536, -7446, -7357, -7267, -7177, -7088, -6998, -6908,
+-6818, -6729, -6639, -6549, -6459, -6370, -6280, -6190, -6101, -6011,
+-5921, -5831, -5742, -5652, -5562, -5472, -5383, -5293, -5203, -5113,
+-5024, -4934, -4844, -4755, -4665, -4575, -4485, -4396, -4306, -4216,
+-4126, -4037, -3947, -3857, -3768, -3678, -3588, -3498, -3409, -3319,
+-3229, -3139, -3050, -2960, -2870, -2781, -2691, -2601, -2511, -2422,
+-2332, -2242, -2152, -2063, -1973, -1883, -1794, -1704, -1614, -1524,
+-1435, -1345, -1255, -1165, -1076, -986, -896, -807, -717, -627, -537,
+-448, -358, -268, -178, -89, 0, 90, 179, 269, 359, 449, 538, 628, 718,
+808, 897, 987, 1077, 1166, 1256, 1346, 1436, 1525, 1615, 1705, 1795,
+1884, 1974, 2064, 2153, 2243, 2333, 2423, 2512, 2602, 2692, 2782,
+2871, 2961, 3051, 3140, 3230, 3320, 3410, 3499, 3589, 3679, 3769,
+3858, 3948, 4038, 4127, 4217, 4307, 4397, 4486, 4576, 4666, 4756,
+4845, 4935, 5025, 5114, 5204, 5294, 5384, 5473, 5563, 5653, 5743,
+5832, 5922, 6012, 6102, 6191, 6281, 6371, 6460, 6550, 6640, 6730,
+6819, 6909, 6999, 7089, 7178, 7268, 7358, 7447, 7537, 7627, 7717,
+7806, 7896, 7986, 8076, 8165, 8255, 8345, 8434, 8524, 8614, 8704,
+8793, 8883, 8973, 9063, 9152, 9242, 9332, 9421, 9511, 9601, 9691,
+9780, 9870, 9960, 10050, 10139, 10229, 10319, 10408, 10498, 10588,
+10678, 10767, 10857, 10947, 11037, 11126, 11216, 11306, 11395 };
+
+static INT16 G_Cb[] = { 2819, 2797, 2775, 2753, 2731, 2709, 2687,
+2665, 2643, 2621, 2599, 2577, 2555, 2533, 2511, 2489, 2467, 2445,
+2423, 2401, 2379, 2357, 2335, 2313, 2291, 2269, 2247, 2225, 2202,
+2180, 2158, 2136, 2114, 2092, 2070, 2048, 2026, 2004, 1982, 1960,
+1938, 1916, 1894, 1872, 1850, 1828, 1806, 1784, 1762, 1740, 1718,
+1696, 1674, 1652, 1630, 1608, 1586, 1564, 1542, 1520, 1498, 1476,
+1454, 1432, 1410, 1388, 1366, 1344, 1321, 1299, 1277, 1255, 1233,
+1211, 1189, 1167, 1145, 1123, 1101, 1079, 1057, 1035, 1013, 991, 969,
+947, 925, 903, 881, 859, 837, 815, 793, 771, 749, 727, 705, 683, 661,
+639, 617, 595, 573, 551, 529, 507, 485, 463, 440, 418, 396, 374, 352,
+330, 308, 286, 264, 242, 220, 198, 176, 154, 132, 110, 88, 66, 44, 22,
+0, -21, -43, -65, -87, -109, -131, -153, -175, -197, -219, -241, -263,
+-285, -307, -329, -351, -373, -395, -417, -439, -462, -484, -506,
+-528, -550, -572, -594, -616, -638, -660, -682, -704, -726, -748,
+-770, -792, -814, -836, -858, -880, -902, -924, -946, -968, -990,
+-1012, -1034, -1056, -1078, -1100, -1122, -1144, -1166, -1188, -1210,
+-1232, -1254, -1276, -1298, -1320, -1343, -1365, -1387, -1409, -1431,
+-1453, -1475, -1497, -1519, -1541, -1563, -1585, -1607, -1629, -1651,
+-1673, -1695, -1717, -1739, -1761, -1783, -1805, -1827, -1849, -1871,
+-1893, -1915, -1937, -1959, -1981, -2003, -2025, -2047, -2069, -2091,
+-2113, -2135, -2157, -2179, -2201, -2224, -2246, -2268, -2290, -2312,
+-2334, -2356, -2378, -2400, -2422, -2444, -2466, -2488, -2510, -2532,
+-2554, -2576, -2598, -2620, -2642, -2664, -2686, -2708, -2730, -2752,
+-2774, -2796 };
+
+static INT16 G_Cr[] = { 5850, 5805, 5759, 5713, 5667, 5622, 5576,
+5530, 5485, 5439, 5393, 5347, 5302, 5256, 5210, 5165, 5119, 5073,
+5028, 4982, 4936, 4890, 4845, 4799, 4753, 4708, 4662, 4616, 4570,
+4525, 4479, 4433, 4388, 4342, 4296, 4251, 4205, 4159, 4113, 4068,
+4022, 3976, 3931, 3885, 3839, 3794, 3748, 3702, 3656, 3611, 3565,
+3519, 3474, 3428, 3382, 3336, 3291, 3245, 3199, 3154, 3108, 3062,
+3017, 2971, 2925, 2879, 2834, 2788, 2742, 2697, 2651, 2605, 2559,
+2514, 2468, 2422, 2377, 2331, 2285, 2240, 2194, 2148, 2102, 2057,
+2011, 1965, 1920, 1874, 1828, 1782, 1737, 1691, 1645, 1600, 1554,
+1508, 1463, 1417, 1371, 1325, 1280, 1234, 1188, 1143, 1097, 1051,
+1006, 960, 914, 868, 823, 777, 731, 686, 640, 594, 548, 503, 457, 411,
+366, 320, 274, 229, 183, 137, 91, 46, 0, -45, -90, -136, -182, -228,
+-273, -319, -365, -410, -456, -502, -547, -593, -639, -685, -730,
+-776, -822, -867, -913, -959, -1005, -1050, -1096, -1142, -1187,
+-1233, -1279, -1324, -1370, -1416, -1462, -1507, -1553, -1599, -1644,
+-1690, -1736, -1781, -1827, -1873, -1919, -1964, -2010, -2056, -2101,
+-2147, -2193, -2239, -2284, -2330, -2376, -2421, -2467, -2513, -2558,
+-2604, -2650, -2696, -2741, -2787, -2833, -2878, -2924, -2970, -3016,
+-3061, -3107, -3153, -3198, -3244, -3290, -3335, -3381, -3427, -3473,
+-3518, -3564, -3610, -3655, -3701, -3747, -3793, -3838, -3884, -3930,
+-3975, -4021, -4067, -4112, -4158, -4204, -4250, -4295, -4341, -4387,
+-4432, -4478, -4524, -4569, -4615, -4661, -4707, -4752, -4798, -4844,
+-4889, -4935, -4981, -5027, -5072, -5118, -5164, -5209, -5255, -5301,
+-5346, -5392, -5438, -5484, -5529, -5575, -5621, -5666, -5712, -5758,
+-5804 };
+
+static INT16 B_Cb[] = { -14515, -14402, -14288, -14175, -14062,
+-13948, -13835, -13721, -13608, -13495, -13381, -13268, -13154,
+-13041, -12928, -12814, -12701, -12587, -12474, -12360, -12247,
+-12134, -12020, -11907, -11793, -11680, -11567, -11453, -11340,
+-11226, -11113, -11000, -10886, -10773, -10659, -10546, -10433,
+-10319, -10206, -10092, -9979, -9865, -9752, -9639, -9525, -9412,
+-9298, -9185, -9072, -8958, -8845, -8731, -8618, -8505, -8391, -8278,
+-8164, -8051, -7938, -7824, -7711, -7597, -7484, -7371, -7257, -7144,
+-7030, -6917, -6803, -6690, -6577, -6463, -6350, -6236, -6123, -6010,
+-5896, -5783, -5669, -5556, -5443, -5329, -5216, -5102, -4989, -4876,
+-4762, -4649, -4535, -4422, -4309, -4195, -4082, -3968, -3855, -3741,
+-3628, -3515, -3401, -3288, -3174, -3061, -2948, -2834, -2721, -2607,
+-2494, -2381, -2267, -2154, -2040, -1927, -1814, -1700, -1587, -1473,
+-1360, -1246, -1133, -1020, -906, -793, -679, -566, -453, -339, -226,
+-112, 0, 113, 227, 340, 454, 567, 680, 794, 907, 1021, 1134, 1247,
+1361, 1474, 1588, 1701, 1815, 1928, 2041, 2155, 2268, 2382, 2495,
+2608, 2722, 2835, 2949, 3062, 3175, 3289, 3402, 3516, 3629, 3742,
+3856, 3969, 4083, 4196, 4310, 4423, 4536, 4650, 4763, 4877, 4990,
+5103, 5217, 5330, 5444, 5557, 5670, 5784, 5897, 6011, 6124, 6237,
+6351, 6464, 6578, 6691, 6804, 6918, 7031, 7145, 7258, 7372, 7485,
+7598, 7712, 7825, 7939, 8052, 8165, 8279, 8392, 8506, 8619, 8732,
+8846, 8959, 9073, 9186, 9299, 9413, 9526, 9640, 9753, 9866, 9980,
+10093, 10207, 10320, 10434, 10547, 10660, 10774, 10887, 11001, 11114,
+11227, 11341, 11454, 11568, 11681, 11794, 11908, 12021, 12135, 12248,
+12361, 12475, 12588, 12702, 12815, 12929, 13042, 13155, 13269, 13382,
+13496, 13609, 13722, 13836, 13949, 14063, 14176, 14289, 14403 };
+
+
+void
+ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels)
+{
+ int x;
+ UINT8 a;
+ int r, g, b;
+ int y, cr, cb;
+
+ for (x = 0; x < pixels; x++, in +=4, out += 4) {
+
+ r = in[0];
+ g = in[1];
+ b = in[2];
+ a = in[3];
+
+ 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;
+ out[3] = a;
+ }
+}
+
+void
+ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels)
+{
+ int x;
+ UINT8 a;
+ int r, g, b;
+ int y, cr, cb;
+
+ for (x = 0; x < pixels; x++, in += 4, out += 4) {
+
+ y = in[0];
+ cb = in[1];
+ cr = in[2];
+ a = in[3];
+
+ r = y + (( R_Cr[cr]) >> SCALE);
+ g = y + ((G_Cb[cb] + G_Cr[cr]) >> SCALE);
+ b = y + ((B_Cb[cb] ) >> SCALE);
+
+ out[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r;
+ out[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g;
+ out[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b;
+ out[3] = a;
+ }
+}
diff --git a/libImaging/Copy.c b/libImaging/Copy.c
new file mode 100644
index 000000000..4a1fc2489
--- /dev/null
+++ b/libImaging/Copy.c
@@ -0,0 +1,58 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * copy image
+ *
+ * history:
+ * 95-11-26 fl Moved from Imaging.c
+ * 97-05-12 fl Added ImagingCopy2
+ * 97-08-28 fl Allow imOut == NULL in ImagingCopy2
+ *
+ * Copyright (c) Fredrik Lundh 1995-97.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+static Imaging
+_copy(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int y;
+
+ if (!imIn)
+ return (Imaging) ImagingError_ValueError(NULL);
+
+ imOut = ImagingNew2(imIn->mode, imOut, imIn);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ ImagingSectionEnter(&cookie);
+ if (imIn->block != NULL && imOut->block != NULL)
+ memcpy(imOut->block, imIn->block, imIn->ysize * imIn->linesize);
+ else
+ for (y = 0; y < imIn->ysize; y++)
+ memcpy(imOut->image[y], imIn->image[y], imIn->linesize);
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+Imaging
+ImagingCopy(Imaging imIn)
+{
+ return _copy(NULL, imIn);
+}
+
+Imaging
+ImagingCopy2(Imaging imOut, Imaging imIn)
+{
+ return _copy(imOut, imIn);
+}
diff --git a/libImaging/Crc32.c b/libImaging/Crc32.c
new file mode 100644
index 000000000..d83d0fbac
--- /dev/null
+++ b/libImaging/Crc32.c
@@ -0,0 +1,87 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * calculate ISO 3307 checksum
+ *
+ * history:
+ * 96-12-10 fl: Created (based on example in the PNG spec)
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+/* Precalculated CRC values (created by makecrctable.py) */
+
+static UINT32 crc32table[] = { 0x0, 0x77073096L, 0xEE0E612CL,
+0x990951BAL, 0x76DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+0xEDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x9B64C2BL,
+0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L,
+0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L,
+0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L,
+0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L,
+0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L,
+0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L,
+0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L,
+0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL,
+0xB6662D3DL, 0x76DC4190L, 0x1DB7106L, 0x98D220BCL, 0xEFD5102AL,
+0x71B18589L, 0x6B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, 0x7807C9A2L,
+0xF00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x86D3D2DL,
+0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L,
+0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL,
+0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL,
+0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL,
+0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL,
+0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L,
+0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L,
+0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+0xEDB88320L, 0x9ABFB3B6L, 0x3B6E20CL, 0x74B1D29AL, 0xEAD54739L,
+0x9DD277AFL, 0x4DB2615L, 0x73DC1683L, 0xE3630B12L, 0x94643B84L,
+0xD6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0xA00AE27L,
+0x7D079EB1L, 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L,
+0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L,
+0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L,
+0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L,
+0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL,
+0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L,
+0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L,
+0xEC63F226L, 0x756AA39CL, 0x26D930AL, 0x9C0906A9L, 0xEB0E363FL,
+0x72076785L, 0x5005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL,
+0xCB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0xBDBDF21L,
+0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL,
+0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, 0x88085AE6L, 0xFF0F6A70L,
+0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L,
+0x166CCF45L, 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL,
+0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L,
+0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L,
+0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L,
+0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL };
+
+
+UINT32
+ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes)
+{
+ int i;
+
+ crc ^= 0xFFFFFFFFL;
+
+ for (i = 0; i < bytes; i++)
+ crc = crc32table[(UINT8) crc ^ buffer[i]] ^ (crc >> 8);
+
+ return crc ^ 0xFFFFFFFFL;
+}
diff --git a/libImaging/Crop.c b/libImaging/Crop.c
new file mode 100644
index 000000000..5acacd4c4
--- /dev/null
+++ b/libImaging/Crop.c
@@ -0,0 +1,61 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * cut region from image
+ *
+ * history:
+ * 95-11-27 fl Created
+ * 98-07-10 fl Fixed "null result" error
+ * 99-02-05 fl Rewritten to use Paste primitive
+ *
+ * Copyright (c) Secret Labs AB 1997-99.
+ * Copyright (c) Fredrik Lundh 1995.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+Imaging
+ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1)
+{
+ Imaging imOut;
+ int xsize, ysize;
+ int dx0, dy0, dx1, dy1;
+ INT32 zero = 0;
+
+ if (!imIn)
+ return (Imaging) ImagingError_ModeError();
+
+ xsize = sx1 - sx0;
+ if (xsize < 0)
+ xsize = 0;
+ ysize = sy1 - sy0;
+ if (ysize < 0)
+ ysize = 0;
+
+ imOut = ImagingNew(imIn->mode, xsize, ysize);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ if (sx0 < 0 || sy0 < 0 || sx1 > imIn->xsize || sy1 > imIn->ysize)
+ (void) ImagingFill(imOut, &zero);
+
+ dx0 = -sx0;
+ dy0 = -sy0;
+ dx1 = imIn->xsize - sx0;
+ dy1 = imIn->ysize - sy0;
+
+ /* paste the source image on top of the output image!!! */
+ if (ImagingPaste(imOut, imIn, NULL, dx0, dy0, dx1, dy1) < 0) {
+ ImagingDelete(imOut);
+ return NULL;
+ }
+
+ return imOut;
+}
diff --git a/libImaging/Dib.c b/libImaging/Dib.c
new file mode 100644
index 000000000..36dc24ef3
--- /dev/null
+++ b/libImaging/Dib.c
@@ -0,0 +1,311 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * imaging display object for Windows
+ *
+ * history:
+ * 1996-05-12 fl Created
+ * 1996-05-17 fl Up and running
+ * 1996-05-21 fl Added palette stuff
+ * 1996-05-26 fl Added query palette and mode inquery
+ * 1997-09-21 fl Added draw primitive
+ * 1998-01-20 fl Use StretchDIBits instead of StretchBlt
+ * 1998-12-30 fl Plugged a resource leak in DeleteDIB (from Roger Burnham)
+ *
+ * Copyright (c) Secret Labs AB 1997-2001.
+ * Copyright (c) Fredrik Lundh 1996.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#ifdef WIN32
+
+#include "ImDib.h"
+
+
+char*
+ImagingGetModeDIB(int size_out[2])
+{
+ /* Get device characteristics */
+
+ HDC dc;
+ char* mode;
+
+ dc = CreateCompatibleDC(NULL);
+
+ mode = "P";
+ if (!(GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)) {
+ mode = "RGB";
+ if (GetDeviceCaps(dc, BITSPIXEL) == 1)
+ mode = "1";
+ }
+
+ if (size_out) {
+ size_out[0] = GetDeviceCaps(dc, HORZRES);
+ size_out[1] = GetDeviceCaps(dc, VERTRES);
+ }
+
+ DeleteDC(dc);
+
+ return mode;
+}
+
+
+ImagingDIB
+ImagingNewDIB(const char *mode, int xsize, int ysize)
+{
+ /* Create a Windows bitmap */
+
+ ImagingDIB dib;
+ RGBQUAD *palette;
+ int i;
+
+ /* Check mode */
+ if (strcmp(mode, "1") != 0 && strcmp(mode, "L") != 0 &&
+ strcmp(mode, "RGB") != 0)
+ return (ImagingDIB) ImagingError_ModeError();
+
+ /* Create DIB context and info header */
+ dib = (ImagingDIB) malloc(sizeof(*dib));
+ if (!dib)
+ return (ImagingDIB) ImagingError_MemoryError();
+ dib->info = (BITMAPINFO*) malloc(sizeof(BITMAPINFOHEADER) +
+ 256 * sizeof(RGBQUAD));
+ if (!dib->info) {
+ free(dib);
+ return (ImagingDIB) ImagingError_MemoryError();
+ }
+
+ memset(dib->info, 0, sizeof(BITMAPINFOHEADER));
+ dib->info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ dib->info->bmiHeader.biWidth = xsize;
+ dib->info->bmiHeader.biHeight = ysize;
+ dib->info->bmiHeader.biPlanes = 1;
+ dib->info->bmiHeader.biBitCount = strlen(mode)*8;
+ dib->info->bmiHeader.biCompression = BI_RGB;
+
+ /* Create DIB */
+ dib->dc = CreateCompatibleDC(NULL);
+ if (!dib->dc) {
+ free(dib->info);
+ free(dib);
+ return (ImagingDIB) ImagingError_MemoryError();
+ }
+
+ dib->bitmap = CreateDIBSection(dib->dc, dib->info, DIB_RGB_COLORS,
+ &dib->bits, NULL, 0);
+ if (!dib->bitmap) {
+ free(dib->info);
+ free(dib);
+ return (ImagingDIB) ImagingError_MemoryError();
+ }
+
+ strcpy(dib->mode, mode);
+ dib->xsize = xsize;
+ dib->ysize = ysize;
+
+ dib->pixelsize = strlen(mode);
+ dib->linesize = (xsize * dib->pixelsize + 3) & -4;
+
+ if (dib->pixelsize == 1)
+ dib->pack = dib->unpack = (ImagingShuffler) memcpy;
+ else {
+ dib->pack = ImagingPackBGR;
+ dib->unpack = ImagingPackBGR;
+ }
+
+ /* Bind the DIB to the device context */
+ dib->old_bitmap = SelectObject(dib->dc, dib->bitmap);
+
+ palette = dib->info->bmiColors;
+
+ /* 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].rgbGreen =
+ palette[i].rgbBlue = i;
+ palette[i].rgbReserved = 0;
+ }
+ SetDIBColorTable(dib->dc, 0, 256, palette);
+ }
+
+ /* Create an associated palette (for 8-bit displays only) */
+ if (strcmp(ImagingGetModeDIB(NULL), "P") == 0) {
+
+ char palbuf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+ LPLOGPALETTE pal = (LPLOGPALETTE) palbuf;
+ int i, r, g, b;
+
+ /* Load system palette */
+ pal->palVersion = 0x300;
+ pal->palNumEntries = 256;
+ GetSystemPaletteEntries(dib->dc, 0, 256, pal->palPalEntry);
+
+ if (strcmp(mode, "L") == 0) {
+
+ /* Greyscale DIB. Fill all 236 slots with a greyscale ramp
+ * (this is usually overkill on Windows since VGA only offers
+ * 6 bits greyscale resolution). Ignore the slots already
+ * allocated by Windows */
+
+ i = 10;
+ for (r = 0; r < 236; r++) {
+ pal->palPalEntry[i].peRed =
+ pal->palPalEntry[i].peGreen =
+ pal->palPalEntry[i].peBlue = i;
+ i++;
+ }
+
+ dib->palette = CreatePalette(pal);
+
+ } else if (strcmp(mode, "RGB") == 0) {
+
+#ifdef CUBE216
+
+ /* Colour DIB. Create a 6x6x6 colour cube (216 entries) and
+ * add 20 extra greylevels for best result with greyscale
+ * images. */
+
+ i = 10;
+ for (r = 0; r < 256; r += 51)
+ for (g = 0; g < 256; g += 51)
+ for (b = 0; b < 256; b += 51) {
+ pal->palPalEntry[i].peRed = r;
+ pal->palPalEntry[i].peGreen = g;
+ pal->palPalEntry[i].peBlue = b;
+ i++;
+ }
+ for (r = 1; r < 22-1; r++) {
+ /* 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);
+ i++;
+ }
+
+#else
+
+ /* Colour DIB. Alternate palette. */
+
+ i = 10;
+ for (r = 0; r < 256; r += 37)
+ for (g = 0; g < 256; g += 32)
+ for (b = 0; b < 256; b += 64) {
+ pal->palPalEntry[i].peRed = r;
+ pal->palPalEntry[i].peGreen = g;
+ pal->palPalEntry[i].peBlue = b;
+ i++;
+ }
+
+#endif
+
+#if 0
+ {
+ /* DEBUG: dump palette to file */
+ FILE *err = fopen("dib.pal", "w");
+ for (i = 0; i < 256; i++)
+ fprintf(err, "%d: %d/%d/%d\n", i,
+ pal->palPalEntry[i].peRed,
+ pal->palPalEntry[i].peGreen,
+ pal->palPalEntry[i].peBlue);
+ fclose(err);
+ }
+#endif
+
+ dib->palette = CreatePalette(pal);
+
+ }
+
+ }
+
+ return dib;
+}
+
+void
+ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4])
+{
+ /* Paste image data into a bitmap */
+
+ /* FIXME: check size! */
+
+ int y;
+ for (y = 0; y < im->ysize; y++)
+ dib->pack(dib->bits + dib->linesize*(dib->ysize-(xy[1]+y)-1) +
+ xy[0]*dib->pixelsize, im->image[y], im->xsize);
+
+}
+
+void
+ImagingExposeDIB(ImagingDIB dib, int dc)
+{
+ /* Copy bitmap to display */
+
+ if (dib->palette != 0)
+ SelectPalette((HDC) dc, dib->palette, FALSE);
+ BitBlt((HDC) dc, 0, 0, dib->xsize, dib->ysize, dib->dc, 0, 0, SRCCOPY);
+}
+
+void
+ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4])
+{
+ /* Copy bitmap to printer/display */
+
+ if (GetDeviceCaps((HDC) dc, RASTERCAPS) & RC_STRETCHDIB) {
+ /* stretchdib (printers) */
+ StretchDIBits((HDC) dc, dst[0], dst[1], dst[2]-dst[0], dst[3]-dst[1],
+ src[0], src[1], src[2]-src[0], src[3]-src[1], dib->bits,
+ dib->info, DIB_RGB_COLORS, SRCCOPY);
+ } else {
+ /* stretchblt (displays) */
+ if (dib->palette != 0)
+ SelectPalette((HDC) dc, dib->palette, FALSE);
+ StretchBlt((HDC) dc, dst[0], dst[1], dst[2]-dst[0], dst[3]-dst[1],
+ dib->dc, src[0], src[1], src[2]-src[0], src[3]-src[1],
+ SRCCOPY);
+ }
+}
+
+int
+ImagingQueryPaletteDIB(ImagingDIB dib, int dc)
+{
+ /* Install bitmap palette */
+
+ int n;
+
+ if (dib->palette != 0) {
+
+ /* Realize associated palette */
+ HPALETTE now = SelectPalette((HDC) dc, dib->palette, FALSE);
+ n = RealizePalette((HDC) dc);
+
+ /* Restore palette */
+ SelectPalette((HDC) dc, now, FALSE);
+
+ } else
+ n = 0;
+
+ return n; /* number of colours that was changed */
+}
+
+void
+ImagingDeleteDIB(ImagingDIB dib)
+{
+ /* Clean up */
+
+ if (dib->palette)
+ DeleteObject(dib->palette);
+ if (dib->bitmap) {
+ SelectObject(dib->dc, dib->old_bitmap);
+ DeleteObject(dib->bitmap);
+ }
+ if (dib->dc)
+ DeleteDC(dib->dc);
+ free(dib->info);
+}
+
+#endif /* WIN32 */
diff --git a/libImaging/Draw.c b/libImaging/Draw.c
new file mode 100644
index 000000000..bdf17e18a
--- /dev/null
+++ b/libImaging/Draw.c
@@ -0,0 +1,1167 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * a simple drawing package for the Imaging library
+ *
+ * history:
+ * 1996-04-13 fl Created.
+ * 1996-04-30 fl Added transforms and polygon support.
+ * 1996-08-12 fl Added filled polygons.
+ * 1996-11-05 fl Fixed float/int confusion in polygon filler
+ * 1997-07-04 fl Support 32-bit images (C++ would have been nice)
+ * 1998-09-09 fl Eliminated qsort casts; improved rectangle clipping
+ * 1998-09-10 fl Fixed fill rectangle to include lower edge (!)
+ * 1998-12-29 fl Added arc, chord, and pieslice primitives
+ * 1999-01-10 fl Added some level 2 ("arrow") stuff (experimental)
+ * 1999-02-06 fl Added bitmap primitive
+ * 1999-07-26 fl Eliminated a compiler warning
+ * 1999-07-31 fl Pass ink as void* instead of int
+ * 2002-12-10 fl Added experimental RGBA-on-RGB drawing
+ * 2004-09-04 fl Support simple wide lines (no joins)
+ * 2005-05-25 fl Fixed line width calculation
+ *
+ * Copyright (c) 1996-2006 by Fredrik Lundh
+ * Copyright (c) 1997-2006 by Secret Labs AB.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+/* FIXME: support fill/outline attribute for all filled shapes */
+/* FIXME: support zero-winding fill */
+/* FIXME: add drawing context, support affine transforms */
+/* FIXME: support clip window (and mask?) */
+
+#include "Imaging.h"
+
+#include
+
+#define CEIL(v) (int) ceil(v)
+#define FLOOR(v) ((v) >= 0.0 ? (int) (v) : (int) floor(v))
+
+#define INK8(ink) (*(UINT8*)ink)
+#define INK32(ink) (*(INT32*)ink)
+
+/* 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 BLEND(mask, in1, in2, tmp1, tmp2)\
+ (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2))
+
+/* -------------------------------------------------------------------- */
+/* Primitives */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ /* edge descriptor for polygon engine */
+ int d;
+ int x0, y0;
+ int xmin, ymin, xmax, ymax;
+ float dx;
+} Edge;
+
+static inline void
+point8(Imaging im, int x, int y, int ink)
+{
+ if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize)
+ im->image8[y][x] = (UINT8) ink;
+}
+
+static inline void
+point32(Imaging im, int x, int y, int ink)
+{
+ if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize)
+ im->image32[y][x] = ink;
+}
+
+static inline void
+point32rgba(Imaging im, int x, int y, int ink)
+{
+ unsigned int tmp1, tmp2;
+
+ if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) {
+ UINT8* out = (UINT8*) im->image[y]+x*4;
+ UINT8* in = (UINT8*) &ink;
+ out[0] = BLEND(in[3], out[0], in[0], tmp1, tmp2);
+ out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2);
+ out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2);
+ }
+}
+
+static inline void
+hline8(Imaging im, int x0, int y0, int x1, int ink)
+{
+ int tmp;
+
+ if (y0 >= 0 && y0 < im->ysize) {
+ if (x0 > x1)
+ tmp = x0, x0 = x1, x1 = tmp;
+ if (x0 < 0)
+ x0 = 0;
+ else if (x0 >= im->xsize)
+ return;
+ if (x1 < 0)
+ return;
+ else if (x1 >= im->xsize)
+ x1 = im->xsize-1;
+ if (x0 <= x1)
+ memset(im->image8[y0] + x0, (UINT8) ink, x1 - x0 + 1);
+ }
+}
+
+static inline void
+hline32(Imaging im, int x0, int y0, int x1, int ink)
+{
+ int tmp;
+ INT32* p;
+
+ if (y0 >= 0 && y0 < im->ysize) {
+ if (x0 > x1)
+ tmp = x0, x0 = x1, x1 = tmp;
+ if (x0 < 0)
+ x0 = 0;
+ else if (x0 >= im->xsize)
+ return;
+ if (x1 < 0)
+ return;
+ else if (x1 >= im->xsize)
+ x1 = im->xsize-1;
+ p = im->image32[y0];
+ while (x0 <= x1)
+ p[x0++] = ink;
+ }
+}
+
+static inline void
+hline32rgba(Imaging im, int x0, int y0, int x1, int ink)
+{
+ int tmp;
+ unsigned int tmp1, tmp2;
+
+ if (y0 >= 0 && y0 < im->ysize) {
+ if (x0 > x1)
+ tmp = x0, x0 = x1, x1 = tmp;
+ if (x0 < 0)
+ x0 = 0;
+ else if (x0 >= im->xsize)
+ return;
+ if (x1 < 0)
+ return;
+ else if (x1 >= im->xsize)
+ x1 = im->xsize-1;
+ if (x0 <= x1) {
+ UINT8* out = (UINT8*) im->image[y0]+x0*4;
+ UINT8* in = (UINT8*) &ink;
+ while (x0 <= x1) {
+ out[0] = BLEND(in[3], out[0], in[0], tmp1, tmp2);
+ out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2);
+ out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2);
+ x0++; out += 4;
+ }
+ }
+ }
+}
+
+static inline void
+line8(Imaging im, int x0, int y0, int x1, int y1, int ink)
+{
+ int i, n, e;
+ int dx, dy;
+ int xs, ys;
+
+ /* normalize coordinates */
+ dx = x1-x0;
+ if (dx < 0)
+ dx = -dx, xs = -1;
+ else
+ xs = 1;
+ dy = y1-y0;
+ if (dy < 0)
+ dy = -dy, ys = -1;
+ else
+ ys = 1;
+
+ n = (dx > dy) ? dx : dy;
+
+ if (dx == 0)
+
+ /* vertical */
+ for (i = 0; i < dy; i++) {
+ point8(im, x0, y0, ink);
+ y0 += ys;
+ }
+
+ else if (dy == 0)
+
+ /* horizontal */
+ for (i = 0; i < dx; i++) {
+ point8(im, x0, y0, ink);
+ x0 += xs;
+ }
+
+ else if (dx > dy) {
+
+ /* bresenham, horizontal slope */
+ n = dx;
+ dy += dy;
+ e = dy - dx;
+ dx += dx;
+
+ for (i = 0; i < n; i++) {
+ point8(im, x0, y0, ink);
+ if (e >= 0) {
+ y0 += ys;
+ e -= dx;
+ }
+ e += dy;
+ x0 += xs;
+ }
+
+ } else {
+
+ /* bresenham, vertical slope */
+ n = dy;
+ dx += dx;
+ e = dx - dy;
+ dy += dy;
+
+ for (i = 0; i < n; i++) {
+ point8(im, x0, y0, ink);
+ if (e >= 0) {
+ x0 += xs;
+ e -= dy;
+ }
+ e += dx;
+ y0 += ys;
+ }
+
+ }
+}
+
+static inline void
+line32(Imaging im, int x0, int y0, int x1, int y1, int ink)
+{
+ int i, n, e;
+ int dx, dy;
+ int xs, ys;
+
+ /* normalize coordinates */
+ dx = x1-x0;
+ if (dx < 0)
+ dx = -dx, xs = -1;
+ else
+ xs = 1;
+ dy = y1-y0;
+ if (dy < 0)
+ dy = -dy, ys = -1;
+ else
+ ys = 1;
+
+ n = (dx > dy) ? dx : dy;
+
+ if (dx == 0)
+
+ /* vertical */
+ for (i = 0; i < dy; i++) {
+ point32(im, x0, y0, ink);
+ y0 += ys;
+ }
+
+ else if (dy == 0)
+
+ /* horizontal */
+ for (i = 0; i < dx; i++) {
+ point32(im, x0, y0, ink);
+ x0 += xs;
+ }
+
+ else if (dx > dy) {
+
+ /* bresenham, horizontal slope */
+ n = dx;
+ dy += dy;
+ e = dy - dx;
+ dx += dx;
+
+ for (i = 0; i < n; i++) {
+ point32(im, x0, y0, ink);
+ if (e >= 0) {
+ y0 += ys;
+ e -= dx;
+ }
+ e += dy;
+ x0 += xs;
+ }
+
+ } else {
+
+ /* bresenham, vertical slope */
+ n = dy;
+ dx += dx;
+ e = dx - dy;
+ dy += dy;
+
+ for (i = 0; i < n; i++) {
+ point32(im, x0, y0, ink);
+ if (e >= 0) {
+ x0 += xs;
+ e -= dy;
+ }
+ e += dx;
+ y0 += ys;
+ }
+
+ }
+}
+
+static inline void
+line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink)
+{
+ int i, n, e;
+ int dx, dy;
+ int xs, ys;
+
+ /* normalize coordinates */
+ dx = x1-x0;
+ if (dx < 0)
+ dx = -dx, xs = -1;
+ else
+ xs = 1;
+ dy = y1-y0;
+ if (dy < 0)
+ dy = -dy, ys = -1;
+ else
+ ys = 1;
+
+ n = (dx > dy) ? dx : dy;
+
+ if (dx == 0)
+
+ /* vertical */
+ for (i = 0; i < dy; i++) {
+ point32rgba(im, x0, y0, ink);
+ y0 += ys;
+ }
+
+ else if (dy == 0)
+
+ /* horizontal */
+ for (i = 0; i < dx; i++) {
+ point32rgba(im, x0, y0, ink);
+ x0 += xs;
+ }
+
+ else if (dx > dy) {
+
+ /* bresenham, horizontal slope */
+ n = dx;
+ dy += dy;
+ e = dy - dx;
+ dx += dx;
+
+ for (i = 0; i < n; i++) {
+ point32rgba(im, x0, y0, ink);
+ if (e >= 0) {
+ y0 += ys;
+ e -= dx;
+ }
+ e += dy;
+ x0 += xs;
+ }
+
+ } else {
+
+ /* bresenham, vertical slope */
+ n = dy;
+ dx += dx;
+ e = dx - dy;
+ dy += dy;
+
+ for (i = 0; i < n; i++) {
+ point32rgba(im, x0, y0, ink);
+ if (e >= 0) {
+ x0 += xs;
+ e -= dy;
+ }
+ e += dx;
+ y0 += ys;
+ }
+
+ }
+}
+
+static int
+x_cmp(const void *x0, const void *x1)
+{
+ float diff = *((float*)x0) - *((float*)x1);
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return 1;
+ else
+ return 0;
+}
+
+static inline int
+polygon8(Imaging im, int n, Edge *e, int ink, int eofill)
+{
+ int i, j;
+ float *xx;
+ int ymin, ymax;
+ float y;
+
+ if (n <= 0)
+ return 0;
+
+ /* Find upper and lower polygon boundary (within image) */
+
+ ymin = e[0].ymin;
+ ymax = e[0].ymax;
+ for (i = 1; i < n; i++) {
+ if (e[i].ymin < ymin) ymin = e[i].ymin;
+ if (e[i].ymax > ymax) ymax = e[i].ymax;
+ }
+
+ if (ymin < 0)
+ ymin = 0;
+ if (ymax >= im->ysize)
+ ymax = im->ysize-1;
+
+ /* Process polygon edges */
+
+ xx = malloc(n * sizeof(float));
+ if (!xx)
+ return -1;
+
+ for (;ymin <= ymax; ymin++) {
+ y = ymin+0.5F;
+ 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);
+ else
+ xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0;
+ }
+ if (j == 2) {
+ if (xx[0] < xx[1])
+ hline8(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink);
+ else
+ hline8(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink);
+ } else {
+ qsort(xx, j, sizeof(float), x_cmp);
+ for (i = 0; i < j-1 ; i += 2)
+ hline8(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink);
+ }
+ }
+
+ free(xx);
+
+ return 0;
+}
+
+static inline int
+polygon32(Imaging im, int n, Edge *e, int ink, int eofill)
+{
+ int i, j;
+ float *xx;
+ int ymin, ymax;
+ float y;
+
+ if (n <= 0)
+ return 0;
+
+ /* Find upper and lower polygon boundary (within image) */
+
+ ymin = e[0].ymin;
+ ymax = e[0].ymax;
+ for (i = 1; i < n; i++) {
+ if (e[i].ymin < ymin) ymin = e[i].ymin;
+ if (e[i].ymax > ymax) ymax = e[i].ymax;
+ }
+
+ if (ymin < 0)
+ ymin = 0;
+ if (ymax >= im->ysize)
+ ymax = im->ysize-1;
+
+ /* Process polygon edges */
+
+ xx = malloc(n * sizeof(float));
+ if (!xx)
+ return -1;
+
+ for (;ymin <= ymax; ymin++) {
+ y = ymin+0.5F;
+ for (i = j = 0; i < n; i++) {
+ if (y >= e[i].ymin && y <= e[i].ymax) {
+ if (e[i].d == 0)
+ hline32(im, e[i].xmin, ymin, e[i].xmax, ink);
+ else
+ xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0;
+ }
+ }
+ if (j == 2) {
+ if (xx[0] < xx[1])
+ hline32(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink);
+ else
+ hline32(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink);
+ } else {
+ qsort(xx, j, sizeof(float), x_cmp);
+ for (i = 0; i < j-1 ; i += 2)
+ hline32(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink);
+ }
+ }
+
+ free(xx);
+
+ return 0;
+}
+
+static inline int
+polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill)
+{
+ int i, j;
+ float *xx;
+ int ymin, ymax;
+ float y;
+
+ if (n <= 0)
+ return 0;
+
+ /* Find upper and lower polygon boundary (within image) */
+
+ ymin = e[0].ymin;
+ ymax = e[0].ymax;
+ for (i = 1; i < n; i++) {
+ if (e[i].ymin < ymin) ymin = e[i].ymin;
+ if (e[i].ymax > ymax) ymax = e[i].ymax;
+ }
+
+ if (ymin < 0)
+ ymin = 0;
+ if (ymax >= im->ysize)
+ ymax = im->ysize-1;
+
+ /* Process polygon edges */
+
+ xx = malloc(n * sizeof(float));
+ if (!xx)
+ return -1;
+
+ for (;ymin <= ymax; ymin++) {
+ y = ymin+0.5F;
+ for (i = j = 0; i < n; i++) {
+ if (y >= e[i].ymin && y <= e[i].ymax) {
+ if (e[i].d == 0)
+ hline32rgba(im, e[i].xmin, ymin, e[i].xmax, ink);
+ else
+ xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0;
+ }
+ }
+ if (j == 2) {
+ if (xx[0] < xx[1])
+ hline32rgba(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink);
+ else
+ hline32rgba(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink);
+ } else {
+ qsort(xx, j, sizeof(float), x_cmp);
+ for (i = 0; i < j-1 ; i += 2)
+ hline32rgba(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink);
+ }
+ }
+
+ free(xx);
+
+ return 0;
+}
+
+static inline void
+add_edge(Edge *e, int x0, int y0, int x1, int y1)
+{
+ /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */
+
+ if (x0 <= x1)
+ e->xmin = x0, e->xmax = x1;
+ else
+ e->xmin = x1, e->xmax = x0;
+
+ if (y0 <= y1)
+ e->ymin = y0, e->ymax = y1;
+ else
+ e->ymin = y1, e->ymax = y0;
+
+ if (y0 == y1) {
+ e->d = 0;
+ e->dx = 0.0;
+ } else {
+ e->dx = ((float)(x1-x0)) / (y1-y0);
+ if (y0 == e->ymin)
+ e->d = 1;
+ else
+ e->d = -1;
+ }
+
+ e->x0 = x0;
+ e->y0 = y0;
+}
+
+typedef struct {
+ void (*point)(Imaging im, int x, int y, int ink);
+ void (*hline)(Imaging im, int x0, int y0, int x1, int ink);
+ void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink);
+ int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill);
+} DRAW;
+
+DRAW draw8 = { point8, hline8, line8, polygon8 };
+DRAW draw32 = { point32, hline32, line32, polygon32 };
+DRAW draw32rgba = { point32rgba, hline32rgba, line32rgba, polygon32rgba };
+
+/* -------------------------------------------------------------------- */
+/* Interface */
+/* -------------------------------------------------------------------- */
+
+#define DRAWINIT()\
+ if (im->image8) {\
+ draw = &draw8;\
+ ink = INK8(ink_);\
+ } else {\
+ draw = (op) ? &draw32rgba : &draw32; \
+ ink = INK32(ink_);\
+ }
+
+int
+ImagingDrawPoint(Imaging im, int x0, int y0, const void* ink_, int op)
+{
+ DRAW* draw;
+ INT32 ink;
+
+ DRAWINIT();
+
+ draw->point(im, x0, y0, ink);
+
+ return 0;
+}
+
+int
+ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink_,
+ int op)
+{
+ DRAW* draw;
+ INT32 ink;
+
+ DRAWINIT();
+
+ draw->line(im, x0, y0, x1, y1, ink);
+
+ return 0;
+}
+
+int
+ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink_, int width, int op)
+{
+ DRAW* draw;
+ INT32 ink;
+
+ Edge e[4];
+
+ int dx, dy;
+ double d;
+
+ DRAWINIT();
+
+ if (width <= 1) {
+ draw->line(im, x0, y0, x1, y1, ink);
+ return 0;
+ }
+
+ dx = x1-x0;
+ dy = y1-y0;
+
+ if (dx == 0 && dy == 0) {
+ draw->point(im, x0, y0, ink);
+ return 0;
+ }
+
+ d = width / sqrt((float) (dx*dx + dy*dy)) / 2.0;
+
+ dx = (int) floor(d * (y1-y0) + 0.5);
+ dy = (int) floor(d * (x1-x0) + 0.5);
+
+ add_edge(e+0, x0 - dx, y0 + dy, x1 - dx, y1 + dy);
+ add_edge(e+1, x1 - dx, y1 + dy, x1 + dx, y1 - dy);
+ add_edge(e+2, x1 + dx, y1 - dy, x0 + dx, y0 - dy);
+ add_edge(e+3, x0 + dx, y0 - dy, x0 - dx, y0 + dy);
+
+ draw->polygon(im, 4, e, ink, 0);
+
+ return 0;
+}
+
+int
+ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink_, int fill, int op)
+{
+ int y;
+ int tmp;
+ DRAW* draw;
+ INT32 ink;
+
+ DRAWINIT();
+
+ if (y0 > y1)
+ tmp = y0, y0 = y1, y1 = tmp;
+
+ if (fill) {
+
+ if (y0 < 0)
+ y0 = 0;
+ else if (y0 >= im->ysize)
+ return 0;
+
+ if (y1 < 0)
+ return 0;
+ else if (y1 > im->ysize)
+ y1 = im->ysize;
+
+ for (y = y0; y <= y1; y++)
+ draw->hline(im, x0, y, x1, ink);
+
+ } else {
+
+ /* outline */
+ draw->line(im, x0, y0, x1, y0, ink);
+ draw->line(im, x1, y0, x1, y1, ink);
+ draw->line(im, x1, y1, x0, y1, ink);
+ draw->line(im, x0, y1, x0, y0, ink);
+
+ }
+
+ return 0;
+}
+
+int
+ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_,
+ int fill, int op)
+{
+ int i, n;
+ DRAW* draw;
+ INT32 ink;
+
+ if (count <= 0)
+ return 0;
+
+ DRAWINIT();
+
+ if (fill) {
+
+ /* Build edge list */
+ Edge* e = malloc(count * sizeof(Edge));
+ if (!e) {
+ (void) ImagingError_MemoryError();
+ return -1;
+ }
+ for (i = n = 0; i < count-1; i++)
+ add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3]);
+ if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1])
+ add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[0], xy[1]);
+ draw->polygon(im, n, e, ink, 0);
+ free(e);
+
+ } else {
+
+ /* Outline */
+ for (i = 0; i < count-1; i++)
+ draw->line(im, xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3], ink);
+ draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink);
+
+ }
+
+ return 0;
+}
+
+int
+ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink,
+ int op)
+{
+ return ImagingFill2(
+ im, ink, bitmap,
+ x0, y0, x0 + bitmap->xsize, y0 + bitmap->ysize
+ );
+}
+
+/* -------------------------------------------------------------------- */
+/* standard shapes */
+
+#define ARC 0
+#define CHORD 1
+#define PIESLICE 2
+
+static int
+ellipse(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink_, int fill,
+ int mode, int op)
+{
+ int i, n;
+ int cx, cy;
+ int w, h;
+ int x = 0, y = 0;
+ int lx = 0, ly = 0;
+ int sx = 0, sy = 0;
+ DRAW* draw;
+ INT32 ink;
+
+ w = x1 - x0;
+ h = y1 - y0;
+ if (w < 0 || h < 0)
+ return 0;
+
+ DRAWINIT();
+
+ cx = (x0 + x1) / 2;
+ cy = (y0 + y1) / 2;
+
+ while (end < start)
+ end += 360;
+
+ if (mode != ARC && fill) {
+
+ /* Build edge list */
+ Edge* e = malloc((end - start + 3) * sizeof(Edge));
+ if (!e) {
+ ImagingError_MemoryError();
+ return -1;
+ }
+
+ n = 0;
+
+ for (i = start; i <= end; i++) {
+ x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5);
+ y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5);
+ if (i != start)
+ add_edge(&e[n++], lx, ly, x, y);
+ else
+ sx = x, sy = y;
+ lx = x, ly = y;
+ }
+
+ if (n > 0) {
+ /* close and draw polygon */
+ if (mode == PIESLICE) {
+ if (x != cx || y != cy) {
+ add_edge(&e[n++], x, y, cx, cy);
+ add_edge(&e[n++], cx, cy, sx, sy);
+ }
+ } else {
+ if (x != sx || y != sy)
+ add_edge(&e[n++], x, y, sx, sy);
+ }
+ draw->polygon(im, n, e, ink, 0);
+ }
+
+ free(e);
+
+ } else {
+
+ for (i = start; i <= end; i++) {
+ x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5);
+ y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5);
+ if (i != start)
+ draw->line(im, lx, ly, x, y, ink);
+ else
+ sx = x, sy = y;
+ lx = x, ly = y;
+ }
+
+ if (i != start) {
+ if (mode == PIESLICE) {
+ if (x != cx || y != cy) {
+ draw->line(im, x, y, cx, cy, ink);
+ draw->line(im, cx, cy, sx, sy, ink);
+ }
+ } else if (mode == CHORD) {
+ if (x != sx || y != sy)
+ draw->line(im, x, y, sx, sy, ink);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int op)
+{
+ return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, ARC, op);
+}
+
+int
+ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int fill, int op)
+{
+ return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, CHORD, op);
+}
+
+int
+ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink, int fill, int op)
+{
+ return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, CHORD, op);
+}
+
+int
+ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int fill, int op)
+{
+ return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, PIESLICE, op);
+}
+
+/* -------------------------------------------------------------------- */
+
+/* experimental level 2 ("arrow") graphics stuff. this implements
+ portions of the arrow api on top of the Edge structure. the
+ semantics are ok, except that "curve" flattens the bezier curves by
+ itself */
+
+#if 1 /* ARROW_GRAPHICS */
+
+struct ImagingOutlineInstance {
+
+ float x0, y0;
+
+ float x, y;
+
+ int count;
+ Edge *edges;
+
+ int size;
+
+};
+
+
+ImagingOutline
+ImagingOutlineNew(void)
+{
+ ImagingOutline outline;
+
+ outline = calloc(1, sizeof(struct ImagingOutlineInstance));
+ if (!outline)
+ return (ImagingOutline) ImagingError_MemoryError();
+
+ outline->edges = NULL;
+ outline->count = outline->size = 0;
+
+ ImagingOutlineMove(outline, 0, 0);
+
+ return outline;
+}
+
+void
+ImagingOutlineDelete(ImagingOutline outline)
+{
+ if (!outline)
+ return;
+
+ if (outline->edges)
+ free(outline->edges);
+
+ free(outline);
+}
+
+
+static Edge*
+allocate(ImagingOutline outline, int extra)
+{
+ Edge* e;
+
+ if (outline->count + extra > outline->size) {
+ /* expand outline buffer */
+ outline->size += extra + 25;
+ if (!outline->edges)
+ e = malloc(outline->size * sizeof(Edge));
+ else
+ e = realloc(outline->edges, outline->size * sizeof(Edge));
+ if (!e)
+ return NULL;
+ outline->edges = e;
+ }
+
+ e = outline->edges + outline->count;
+
+ outline->count += extra;
+
+ return e;
+}
+
+int
+ImagingOutlineMove(ImagingOutline outline, float x0, float y0)
+{
+ outline->x = outline->x0 = x0;
+ outline->y = outline->y0 = y0;
+
+ return 0;
+}
+
+int
+ImagingOutlineLine(ImagingOutline outline, float x1, float y1)
+{
+ Edge* e;
+
+ e = allocate(outline, 1);
+ if (!e)
+ return -1; /* out of memory */
+
+ add_edge(e, (int) outline->x, (int) outline->y, (int) x1, (int) y1);
+
+ outline->x = x1;
+ outline->y = y1;
+
+ return 0;
+}
+
+int
+ImagingOutlineCurve(ImagingOutline outline, float x1, float y1,
+ float x2, float y2, float x3, float y3)
+{
+ Edge* e;
+ int i;
+ float xo, yo;
+
+#define STEPS 32
+
+ e = allocate(outline, STEPS);
+ if (!e)
+ return -1; /* out of memory */
+
+ xo = outline->x;
+ yo = outline->y;
+
+ /* flatten the bezier segment */
+
+ for (i = 1; i <= STEPS; i++) {
+
+ float t = ((float) i) / STEPS;
+ float t2 = t*t;
+ float t3 = t2*t;
+
+ float u = 1.0F - t;
+ float u2 = u*u;
+ float u3 = u2*u;
+
+ float x = outline->x*u3 + 3*(x1*t*u2 + x2*t2*u) + x3*t3 + 0.5;
+ float y = outline->y*u3 + 3*(y1*t*u2 + y2*t2*u) + y3*t3 + 0.5;
+
+ add_edge(e++, xo, yo, (int) x, (int) y);
+
+ xo = x, yo = y;
+
+ }
+
+ outline->x = xo;
+ outline->y = yo;
+
+ return 0;
+}
+
+int
+ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy,
+ float x3, float y3)
+{
+ /* add bezier curve based on three control points (as
+ in the Flash file format) */
+
+ return ImagingOutlineCurve(
+ outline,
+ (outline->x + cx + cx)/3, (outline->y + cy + cy)/3,
+ (cx + cx + x3)/3, (cy + cy + y3)/3,
+ x3, y3);
+}
+
+int
+ImagingOutlineClose(ImagingOutline outline)
+{
+ if (outline->x == outline->x0 && outline->y == outline->y0)
+ return 0;
+ return ImagingOutlineLine(outline, outline->x0, outline->y0);
+}
+
+int
+ImagingOutlineTransform(ImagingOutline outline, double a[6])
+{
+ Edge *eIn;
+ Edge *eOut;
+ 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;
+
+ eOut = allocate(outline, n);
+ if (!eOut) {
+ outline->edges = eIn;
+ outline->count = outline->size = n;
+ 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;
+ else
+ x1 = eIn->xmin;
+ if (eIn->y0 == eIn->ymin)
+ y1 = eIn->ymax;
+ else
+ y1 = eIn->ymin;
+
+ /* full moon tonight! if this doesn't work, you may need to
+ upgrade your compiler (make sure you have the right service
+ pack) */
+
+ X0 = (int) (a0*x0 + a1*y0 + a2);
+ Y0 = (int) (a3*x0 + a4*y0 + a5);
+ X1 = (int) (a0*x1 + a1*y1 + a2);
+ Y1 = (int) (a3*x1 + a4*y1 + a5);
+
+ add_edge(eOut, X0, Y0, X1, Y1);
+
+ eIn++;
+ eOut++;
+
+ }
+
+ free(eIn);
+
+ return 0;
+}
+
+int
+ImagingDrawOutline(Imaging im, ImagingOutline outline, const void* ink_,
+ int fill, int op)
+{
+ DRAW* draw;
+ INT32 ink;
+
+ DRAWINIT();
+
+ draw->polygon(im, outline->count, outline->edges, ink, 0);
+
+ return 0;
+}
+
+#endif
diff --git a/libImaging/Effects.c b/libImaging/Effects.c
new file mode 100644
index 000000000..1b964ac40
--- /dev/null
+++ b/libImaging/Effects.c
@@ -0,0 +1,373 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * various special effects and image generators
+ *
+ * history:
+ * 1997-05-21 fl Just for fun
+ * 1997-06-05 fl Added mandelbrot generator
+ * 2003-05-24 fl Added perlin_turbulence generator (in progress)
+ *
+ * Copyright (c) 1997-2003 by Fredrik Lundh.
+ * Copyright (c) 1997 by Secret Labs AB.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include
+
+Imaging
+ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality)
+{
+ /* Generate a Mandelbrot set covering the given extent */
+
+ Imaging im;
+ int x, y, k;
+ double width, height;
+ double x1, y1, xi2, yi2, cr, ci, radius;
+ double dr, di;
+
+ /* Check arguments */
+ width = extent[2] - extent[0];
+ height = extent[3] - extent[1];
+ if (width < 0.0 || height < 0.0 || quality < 2)
+ return (Imaging) ImagingError_ValueError(NULL);
+
+ im = ImagingNew("L", xsize, ysize);
+ if (!im)
+ return NULL;
+
+ dr = width/(xsize-1);
+ di = height/(ysize-1);
+
+ radius = 100.0;
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* buf = im->image8[y];
+ for (x = 0; x < xsize; x++) {
+ x1 = y1 = xi2 = yi2 = 0.0;
+ cr = x*dr + extent[0];
+ ci = y*di + extent[1];
+ for (k = 1;; k++) {
+ y1 = 2*x1*y1 + ci;
+ x1 = xi2 - yi2 + cr;
+ xi2 = x1*x1;
+ yi2 = y1*y1;
+ if ((xi2 + yi2) > radius) {
+ buf[x] = k*255/quality;
+ break;
+ }
+ if (k > quality) {
+ buf[x] = 0;
+ break;
+ }
+ }
+ }
+ }
+ return im;
+}
+
+Imaging
+ImagingEffectNoise(int xsize, int ysize, float sigma)
+{
+ /* Generate gaussian noise centered around 128 */
+
+ Imaging imOut;
+ int x, y;
+ int nextok;
+ double this, next;
+
+ imOut = ImagingNew("L", xsize, ysize);
+ if (!imOut)
+ return NULL;
+
+ next = 0.0;
+ nextok = 0;
+
+ for (y = 0; y < imOut->ysize; y++) {
+ UINT8* out = imOut->image8[y];
+ for (x = 0; x < imOut->xsize; x++) {
+ if (nextok) {
+ this = next;
+ nextok = 0;
+ } else {
+ /* after numerical recepies */
+ double v1, v2, radius, factor;
+ do {
+ v1 = rand()*(2.0/32767.0) - 1.0;
+ v2 = rand()*(2.0/32767.0) - 1.0;
+ radius= v1*v1 + v2*v2;
+ } while (radius >= 1.0);
+ factor = sqrt(-2.0*log(radius)/radius);
+ this = factor * v1;
+ next = factor * v2;
+ }
+ out[x] = (unsigned char) (128 + sigma * this);
+ }
+ }
+
+ return imOut;
+}
+
+Imaging
+ImagingEffectPerlinTurbulence(int xsize, int ysize)
+{
+ /* Perlin turbulence (In progress) */
+
+ return NULL;
+}
+
+Imaging
+ImagingEffectSpread(Imaging imIn, int distance)
+{
+ /* Randomly spread pixels in an image */
+
+ Imaging imOut;
+ int x, y;
+
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+
+ if (!imOut)
+ return NULL;
+
+#define SPREAD(type, image)\
+ for (y = 0; y < imIn->ysize; y++)\
+ for (x = 0; x < imIn->xsize; x++) {\
+ int xx = x + (rand() % distance) - distance/2;\
+ int yy = y + (rand() % distance) - distance/2;\
+ if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\
+ imOut->image[yy][xx] = imIn->image[y][x];\
+ imOut->image[y][x] = imIn->image[yy][xx];\
+ } else\
+ imOut->image[y][x] = imIn->image[y][x];\
+ }
+
+ if (imIn->image8) {
+ SPREAD(UINT8, image8);
+ } else {
+ SPREAD(INT32, image32);
+ }
+
+ ImagingCopyInfo(imOut, imIn);
+
+ return imOut;
+}
+
+/* -------------------------------------------------------------------- */
+/* Taken from the "C" code in the W3C SVG specification. Translated
+ to C89 by Fredrik Lundh */
+
+#if 0
+
+/* Produces results in the range [1, 2**31 - 2].
+Algorithm is: r = (a * r) mod m
+where a = 16807 and m = 2**31 - 1 = 2147483647
+See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988
+To test: the algorithm should produce the result 1043618065
+as the 10,000th generated number if the original seed is 1.
+*/
+#define RAND_m 2147483647 /* 2**31 - 1 */
+#define RAND_a 16807 /* 7**5; primitive root of m */
+#define RAND_q 127773 /* m / a */
+#define RAND_r 2836 /* m % a */
+
+static long
+perlin_setup_seed(long lSeed)
+{
+ if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1;
+ if (lSeed > RAND_m - 1) lSeed = RAND_m - 1;
+ return lSeed;
+}
+
+static long
+perlin_random(long lSeed)
+{
+ long result;
+ result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q);
+ if (result <= 0) result += RAND_m;
+ return result;
+}
+
+#define BSize 0x100
+#define BM 0xff
+#define PerlinN 0x1000
+#define NP 12 /* 2^PerlinN */
+#define NM 0xfff
+static int perlin_uLatticeSelector[BSize + BSize + 2];
+static double perlin_fGradient[4][BSize + BSize + 2][2];
+typedef struct
+{
+ int nWidth; /* How much to subtract to wrap for stitching. */
+ int nHeight;
+ int nWrapX; /* Minimum value to wrap. */
+ int nWrapY;
+} StitchInfo;
+
+static void
+perlin_init(long lSeed)
+{
+ double s;
+ int i, j, k;
+ lSeed = perlin_setup_seed(lSeed);
+ for(k = 0; k < 4; k++)
+ {
+ for(i = 0; i < BSize; i++)
+ {
+ perlin_uLatticeSelector[i] = i;
+ for (j = 0; j < 2; j++)
+ perlin_fGradient[k][i][j] = (double)(((lSeed = perlin_random(lSeed)) % (BSize + BSize)) - BSize) / BSize;
+ s = (double) (sqrt(perlin_fGradient[k][i][0] * perlin_fGradient[k][i][0] + perlin_fGradient[k][i][1] * perlin_fGradient[k][i][1]));
+ perlin_fGradient[k][i][0] /= s;
+ perlin_fGradient[k][i][1] /= s;
+ }
+ }
+ while(--i)
+ {
+ k = perlin_uLatticeSelector[i];
+ perlin_uLatticeSelector[i] = perlin_uLatticeSelector[j = (lSeed = perlin_random(lSeed)) % BSize];
+ perlin_uLatticeSelector[j] = k;
+ }
+ for(i = 0; i < BSize + 2; i++)
+ {
+ perlin_uLatticeSelector[BSize + i] = perlin_uLatticeSelector[i];
+ for(k = 0; k < 4; k++)
+ for(j = 0; j < 2; j++)
+ perlin_fGradient[k][BSize + i][j] = perlin_fGradient[k][i][j];
+ }
+}
+
+#define s_curve(t) ( t * t * (3. - 2. * t) )
+#define lerp(t, a, b) ( a + t * (b - a) )
+static double
+perlin_noise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo)
+{
+ int bx0, bx1, by0, by1, b00, b10, b01, b11;
+ double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v;
+ register int i, j;
+
+ t = vec[0] + (double) PerlinN;
+ bx0 = (int)t;
+ bx1 = bx0+1;
+ rx0 = t - (int)t;
+ rx1 = rx0 - 1.0f;
+ t = vec[1] + (double) PerlinN;
+ by0 = (int)t;
+ by1 = by0+1;
+ ry0 = t - (int)t;
+ ry1 = ry0 - 1.0f;
+
+ /* If stitching, adjust lattice points accordingly. */
+ if(pStitchInfo != NULL)
+ {
+ if(bx0 >= pStitchInfo->nWrapX)
+ bx0 -= pStitchInfo->nWidth;
+ if(bx1 >= pStitchInfo->nWrapX)
+ bx1 -= pStitchInfo->nWidth;
+ if(by0 >= pStitchInfo->nWrapY)
+ by0 -= pStitchInfo->nHeight;
+ if(by1 >= pStitchInfo->nWrapY)
+ by1 -= pStitchInfo->nHeight;
+ }
+
+ bx0 &= BM;
+ bx1 &= BM;
+ by0 &= BM;
+ by1 &= BM;
+
+ i = perlin_uLatticeSelector[bx0];
+ j = perlin_uLatticeSelector[bx1];
+ b00 = perlin_uLatticeSelector[i + by0];
+ b10 = perlin_uLatticeSelector[j + by0];
+ b01 = perlin_uLatticeSelector[i + by1];
+ b11 = perlin_uLatticeSelector[j + by1];
+ sx = (double) (s_curve(rx0));
+ sy = (double) (s_curve(ry0));
+ q = perlin_fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1];
+ q = perlin_fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1];
+ a = lerp(sx, u, v);
+ q = perlin_fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1];
+ q = perlin_fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1];
+ b = lerp(sx, u, v);
+ return lerp(sy, a, b);
+}
+
+double
+perlin_turbulence(
+ int nColorChannel, double *point, double fBaseFreqX, double fBaseFreqY,
+ int nNumOctaves, int bFractalSum, int bDoStitching,
+ double fTileX, double fTileY, double fTileWidth, double fTileHeight)
+{
+ StitchInfo stitch;
+ StitchInfo *pStitchInfo = NULL; /* Not stitching when NULL. */
+
+ double fSum = 0.0f;
+ double vec[2];
+ double ratio = 1;
+
+ int nOctave;
+
+ vec[0] = point[0] * fBaseFreqX;
+ vec[1] = point[1] * fBaseFreqY;
+
+ /* Adjust the base frequencies if necessary for stitching. */
+ if(bDoStitching)
+ {
+ /* When stitching tiled turbulence, the frequencies must be adjusted */
+ /* so that the tile borders will be continuous. */
+ if(fBaseFreqX != 0.0)
+ {
+ double fLoFreq = (double) (floor(fTileWidth * fBaseFreqX)) / fTileWidth;
+ double fHiFreq = (double) (ceil(fTileWidth * fBaseFreqX)) / fTileWidth;
+ if(fBaseFreqX / fLoFreq < fHiFreq / fBaseFreqX)
+ fBaseFreqX = fLoFreq;
+ else
+ fBaseFreqX = fHiFreq;
+ }
+
+ if(fBaseFreqY != 0.0)
+ {
+ double fLoFreq = (double) (floor(fTileHeight * fBaseFreqY)) / fTileHeight;
+ double fHiFreq = (double) (ceil(fTileHeight * fBaseFreqY)) / fTileHeight;
+ if(fBaseFreqY / fLoFreq < fHiFreq / fBaseFreqY)
+ fBaseFreqY = fLoFreq;
+ else
+ fBaseFreqY = fHiFreq;
+ }
+
+ /* Set up initial stitch values. */
+ pStitchInfo = &stitch;
+ stitch.nWidth = (int) (fTileWidth * fBaseFreqX + 0.5f);
+ stitch.nWrapX = (int) (fTileX * fBaseFreqX + PerlinN + stitch.nWidth);
+ stitch.nHeight = (int) (fTileHeight * fBaseFreqY + 0.5f);
+ stitch.nWrapY = (int) (fTileY * fBaseFreqY + PerlinN + stitch.nHeight);
+ }
+
+ for(nOctave = 0; nOctave < nNumOctaves; nOctave++)
+ {
+ if(bFractalSum)
+ fSum += (double) (perlin_noise2(nColorChannel, vec, pStitchInfo) / ratio);
+ else
+ fSum += (double) (fabs(perlin_noise2(nColorChannel, vec, pStitchInfo)) / ratio);
+
+ vec[0] *= 2;
+ vec[1] *= 2;
+ ratio *= 2;
+
+ if(pStitchInfo != NULL)
+ {
+ /* Update stitch values. Subtracting PerlinN before the multiplication and */
+ /* adding it afterward simplifies to subtracting it once. */
+ stitch.nWidth *= 2;
+ stitch.nWrapX = 2 * stitch.nWrapX - PerlinN;
+ stitch.nHeight *= 2;
+ stitch.nWrapY = 2 * stitch.nWrapY - PerlinN;
+ }
+ }
+ return fSum;
+}
+
+#endif
diff --git a/libImaging/EpsEncode.c b/libImaging/EpsEncode.c
new file mode 100644
index 000000000..704d5a656
--- /dev/null
+++ b/libImaging/EpsEncode.c
@@ -0,0 +1,80 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * encoder for EPS hex data
+ *
+ * history:
+ * 96-04-19 fl created
+ * 96-06-27 fl don't drop last block of encoded data
+ *
+ * notes:
+ * FIXME: rename to HexEncode.c ??
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ enum { HEXBYTE=1, NEWLINE };
+ const char *hex = "0123456789abcdef";
+
+ UINT8* ptr = buf;
+ UINT8* in, i;
+
+ if (!state->state) {
+ state->state = HEXBYTE;
+ state->xsize *= im->pixelsize; /* Hack! */
+ }
+
+ in = (UINT8*) im->image[state->y];
+
+ for (;;) {
+
+ if (state->state == NEWLINE) {
+ if (bytes < 1)
+ break;
+ *ptr++ = '\n';
+ bytes--;
+ state->state = HEXBYTE;
+ }
+
+ if (bytes < 2)
+ break;
+
+ i = in[state->x++];
+ *ptr++ = hex[(i>>4)&15];
+ *ptr++ = hex[i&15];
+ bytes -= 2;
+
+ /* Skip junk bytes */
+ if (im->bands == 3 && (state->x & 3) == 3)
+ state->x++;
+
+ if (++state->count >= 79/2) {
+ state->state = NEWLINE;
+ state->count = 0;
+ }
+
+ if (state->x >= state->xsize) {
+ state->x = 0;
+ if (++state->y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_END;
+ break;
+ }
+ in = (UINT8*) im->image[state->y];
+ }
+
+ }
+
+ return ptr - buf;
+
+}
diff --git a/libImaging/Except.c b/libImaging/Except.c
new file mode 100644
index 000000000..635435d57
--- /dev/null
+++ b/libImaging/Except.c
@@ -0,0 +1,83 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * default exception handling
+ *
+ * This module is usually overridden by application code (e.g.
+ * _imaging.c for PIL's standard Python bindings). If you get
+ * linking errors, remove this file from your project/library.
+ *
+ * history:
+ * 1995-06-15 fl Created
+ * 1998-12-29 fl Minor tweaks
+ * 2003-09-13 fl Added ImagingEnter/LeaveSection()
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB.
+ * Copyright (c) 1995-2003 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+/* exception state */
+
+void *
+ImagingError_IOError(void)
+{
+ fprintf(stderr, "*** exception: file access error\n");
+ return NULL;
+}
+
+void *
+ImagingError_MemoryError(void)
+{
+ fprintf(stderr, "*** exception: out of memory\n");
+ return NULL;
+}
+
+void *
+ImagingError_ModeError(void)
+{
+ return ImagingError_ValueError("bad image mode");
+ return NULL;
+}
+
+void *
+ImagingError_Mismatch(void)
+{
+ return ImagingError_ValueError("images don't match");
+ return NULL;
+}
+
+void *
+ImagingError_ValueError(const char *message)
+{
+ if (!message)
+ message = "exception: bad argument to function";
+ fprintf(stderr, "*** %s\n", message);
+ return NULL;
+}
+
+void
+ImagingError_Clear(void)
+{
+ /* nop */;
+}
+
+/* thread state */
+
+void
+ImagingSectionEnter(ImagingSectionCookie* cookie)
+{
+ /* pass */
+}
+
+void
+ImagingSectionLeave(ImagingSectionCookie* cookie)
+{
+ /* pass */
+}
diff --git a/libImaging/File.c b/libImaging/File.c
new file mode 100644
index 000000000..6f454d1ca
--- /dev/null
+++ b/libImaging/File.c
@@ -0,0 +1,193 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * built-in image file handling
+ *
+ * history:
+ * 1995-11-26 fl Created, supports PGM/PPM
+ * 1996-08-07 fl Write "1" images as PGM
+ * 1999-02-21 fl Don't write non-standard modes
+ *
+ * Copyright (c) 1997-99 by Secret Labs AB.
+ * Copyright (c) 1995-96 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include
+
+Imaging
+ImagingOpenPPM(const char* infile)
+{
+ FILE* fp;
+ int i, c, v;
+ char* mode;
+ int x, y, max;
+ Imaging im;
+
+ if (!infile)
+ return ImagingError_ValueError(NULL);
+
+ fp = fopen(infile, "rb");
+ if (!fp)
+ return ImagingError_IOError();
+
+ /* PPM magic */
+ if (fgetc(fp) != 'P')
+ goto error;
+ switch (fgetc(fp)) {
+ case '4': /* FIXME: 1-bit images are not yet supported */
+ goto error;
+ case '5':
+ mode = "L";
+ break;
+ case '6':
+ mode = "RGB";
+ break;
+ default:
+ goto error;
+ }
+
+ i = 0;
+ c = fgetc(fp);
+
+ x = y = max = 0;
+
+ while (i < 3) {
+
+ /* Ignore optional comment fields */
+ while (c == '\n') {
+ c = fgetc(fp);
+ if (c == '#') {
+ do {
+ c = fgetc(fp);
+ if (c == EOF)
+ goto error;
+ } while (c != '\n');
+ c = fgetc(fp);
+ }
+ }
+
+ /* Skip forward to next value */
+ while (isspace(c))
+ c = fgetc(fp);
+
+ /* And parse it */
+ v = 0;
+ while (isdigit(c)) {
+ v = v * 10 + (c - '0');
+ c = fgetc(fp);
+ }
+
+ if (c == EOF)
+ goto error;
+
+ switch (i++) {
+ case 0:
+ x = v;
+ break;
+ case 1:
+ y = v;
+ break;
+ case 2:
+ max = v;
+ break;
+ }
+ }
+
+ im = ImagingNew(mode, x, y);
+ if (!im)
+ return NULL;
+
+ /* if (max != 255) ... FIXME: does anyone ever use this feature? */
+
+ if (strcmp(im->mode, "L") == 0) {
+
+ /* PPM "L" */
+ for (y = 0; y < im->ysize; y++)
+ if (fread(im->image[y], im->xsize, 1, fp) != 1)
+ goto error;
+
+ } else {
+
+ /* PPM "RGB" or PyPPM mode */
+ for (y = 0; y < im->ysize; y++)
+ for (x = i = 0; x < im->xsize; x++, i += im->pixelsize)
+ if (fread(im->image[y]+i, im->bands, 1, fp) != 1)
+ goto error;
+ }
+
+ fclose(fp);
+
+ return im;
+
+error:
+ fclose(fp);
+ return ImagingError_IOError();
+}
+
+
+int
+ImagingSaveRaw(Imaging im, FILE* fp)
+{
+ int x, y, i;
+
+ if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+
+ /* @PIL227: FIXME: for mode "1", map != 0 to 255 */
+
+ /* PGM "L" */
+ for (y = 0; y < im->ysize; y++)
+ fwrite(im->image[y], 1, im->xsize, fp);
+
+ } else {
+
+ /* PPM "RGB" or other internal format */
+ for (y = 0; y < im->ysize; y++)
+ for (x = i = 0; x < im->xsize; x++, i += im->pixelsize)
+ fwrite(im->image[y]+i, 1, im->bands, fp);
+
+ }
+
+ return 1;
+}
+
+
+int
+ImagingSavePPM(Imaging im, const char* outfile)
+{
+ FILE* fp;
+
+ if (!im) {
+ (void) ImagingError_ValueError(NULL);
+ return 0;
+ }
+
+ fp = fopen(outfile, "wb");
+ if (!fp) {
+ (void) ImagingError_IOError();
+ return 0;
+ }
+
+ if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) {
+ /* Write "PGM" */
+ fprintf(fp, "P5\n%d %d\n255\n", im->xsize, im->ysize);
+ } else if (strcmp(im->mode, "RGB") == 0) {
+ /* Write "PPM" */
+ fprintf(fp, "P6\n%d %d\n255\n", im->xsize, im->ysize);
+ } else {
+ (void) ImagingError_ModeError();
+ return 0;
+ }
+
+ ImagingSaveRaw(im, fp);
+
+ fclose(fp);
+
+ return 1;
+}
+
diff --git a/libImaging/Fill.c b/libImaging/Fill.c
new file mode 100644
index 000000000..ce00e6c15
--- /dev/null
+++ b/libImaging/Fill.c
@@ -0,0 +1,101 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * fill image with constant pixel value
+ *
+ * history:
+ * 95-11-26 fl moved from Imaging.c
+ * 96-05-17 fl added radial fill, renamed wedge to linear
+ * 98-06-23 fl changed ImageFill signature
+ *
+ * Copyright (c) Secret Labs AB 1997-98. All rights reserved.
+ * Copyright (c) Fredrik Lundh 1995-96.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include "math.h"
+
+Imaging
+ImagingFill(Imaging im, const void* colour)
+{
+ int x, y;
+
+ if (im->type == IMAGING_TYPE_SPECIAL) {
+ /* use generic API */
+ ImagingAccess access = ImagingAccessNew(im);
+ if (access) {
+ for (y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++)
+ access->put_pixel(im, x, y, colour);
+ ImagingAccessDelete(im, access);
+ } else {
+ /* wipe the image */
+ for (y = 0; y < im->ysize; y++)
+ memset(im->image[y], 0, im->linesize);
+ }
+ } else {
+ INT32 c = 0L;
+ memcpy(&c, colour, im->pixelsize);
+ if (im->image32 && c != 0L) {
+ for (y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++)
+ im->image32[y][x] = c;
+ } else {
+ unsigned char cc = (unsigned char) *(UINT8*) colour;
+ for (y = 0; y < im->ysize; y++)
+ memset(im->image[y], cc, im->linesize);
+ }
+ }
+
+ return im;
+}
+
+Imaging
+ImagingFillLinearGradient(const char *mode)
+{
+ Imaging im;
+ int y;
+
+ if (strlen(mode) != 1)
+ return (Imaging) ImagingError_ModeError();
+
+ im = ImagingNew(mode, 256, 256);
+ if (!im)
+ return NULL;
+
+ for (y = 0; y < 256; y++)
+ memset(im->image8[y], (unsigned char) y, 256);
+
+ return im;
+}
+
+Imaging
+ImagingFillRadialGradient(const char *mode)
+{
+ Imaging im;
+ int x, y;
+ int d;
+
+ if (strlen(mode) != 1)
+ return (Imaging) ImagingError_ModeError();
+
+ im = ImagingNew(mode, 256, 256);
+ if (!im)
+ return NULL;
+
+ for (y = 0; y < 256; y++)
+ for (x = 0; x < 256; x++) {
+ d = (int) sqrt((double) ((x-128)*(x-128) + (y-128)*(y-128)) * 2.0);
+ if (d >= 255)
+ im->image8[y][x] = 255;
+ else
+ im->image8[y][x] = d;
+ }
+
+ return im;
+}
diff --git a/libImaging/Filter.c b/libImaging/Filter.c
new file mode 100644
index 000000000..45c9bf060
--- /dev/null
+++ b/libImaging/Filter.c
@@ -0,0 +1,179 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * apply convolution kernel to image
+ *
+ * history:
+ * 1995-11-26 fl Created, supports 3x3 kernels
+ * 1995-11-27 fl Added 5x5 kernels, copy border
+ * 1999-07-26 fl Eliminated a few compiler warnings
+ * 2002-06-09 fl Moved kernel definitions to Python
+ * 2002-06-11 fl Support floating point kernels
+ * 2003-09-15 fl Added ImagingExpand helper
+ *
+ * Copyright (c) Secret Labs AB 1997-2002. All rights reserved.
+ * Copyright (c) Fredrik Lundh 1995.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+/*
+ * FIXME: Support RGB and RGBA/CMYK modes as well
+ * FIXME: Expand image border (current version leaves border as is)
+ * FIXME: Implement image processing gradient filters
+ */
+
+#include "Imaging.h"
+
+Imaging
+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");
+
+ imOut = ImagingNew(
+ imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin
+ );
+ if (!imOut)
+ return NULL;
+
+#define EXPAND_LINE(type, image, yin, yout) {\
+ for (x = 0; x < xmargin; x++)\
+ imOut->image[yout][x] = imIn->image[yin][0];\
+ for (x = 0; x < imIn->xsize; x++)\
+ imOut->image[yout][x+xmargin] = imIn->image[yin][x];\
+ for (x = 0; x < xmargin; x++)\
+ imOut->image[yout][xmargin+imIn->xsize+x] =\
+ imIn->image[yin][imIn->xsize-1];\
+ }
+
+#define EXPAND(type, image) {\
+ for (y = 0; y < ymargin; y++)\
+ EXPAND_LINE(type, image, 0, y);\
+ for (y = 0; y < imIn->ysize; y++)\
+ EXPAND_LINE(type, image, y, y+ymargin);\
+ for (y = 0; y < ymargin; y++)\
+ EXPAND_LINE(type, image, imIn->ysize-1, ymargin+imIn->ysize+y);\
+ }
+
+ if (imIn->image8) {
+ EXPAND(UINT8, image8);
+ } else {
+ EXPAND(INT32, image32);
+ }
+
+ ImagingCopyInfo(imOut, imIn);
+
+ return imOut;
+}
+
+Imaging
+ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel,
+ FLOAT32 offset, FLOAT32 divisor)
+{
+ Imaging imOut;
+ int x, y;
+ FLOAT32 sum;
+
+ if (!im || strcmp(im->mode, "L") != 0)
+ return (Imaging) ImagingError_ModeError();
+
+ if (im->xsize < xsize || im->ysize < ysize)
+ return ImagingCopy(im);
+
+ if ((xsize != 3 && xsize != 5) || xsize != ysize)
+ return (Imaging) ImagingError_ValueError("bad kernel size");
+
+ imOut = ImagingNew(im->mode, im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ /* brute force kernel implementations */
+#define KERNEL3x3(image, kernel, d) ( \
+ (int) image[y+1][x-d] * kernel[0] + \
+ (int) image[y+1][x] * kernel[1] + \
+ (int) image[y+1][x+d] * kernel[2] + \
+ (int) image[y][x-d] * kernel[3] + \
+ (int) image[y][x] * kernel[4] + \
+ (int) image[y][x+d] * kernel[5] + \
+ (int) image[y-1][x-d] * kernel[6] + \
+ (int) image[y-1][x] * kernel[7] + \
+ (int) image[y-1][x+d] * kernel[8])
+
+#define KERNEL5x5(image, kernel, d) ( \
+ (int) image[y+2][x-d-d] * kernel[0] + \
+ (int) image[y+2][x-d] * kernel[1] + \
+ (int) image[y+2][x] * kernel[2] + \
+ (int) image[y+2][x+d] * kernel[3] + \
+ (int) image[y+2][x+d+d] * kernel[4] + \
+ (int) image[y+1][x-d-d] * kernel[5] + \
+ (int) image[y+1][x-d] * kernel[6] + \
+ (int) image[y+1][x] * kernel[7] + \
+ (int) image[y+1][x+d] * kernel[8] + \
+ (int) image[y+1][x+d+d] * kernel[9] + \
+ (int) image[y][x-d-d] * kernel[10] + \
+ (int) image[y][x-d] * kernel[11] + \
+ (int) image[y][x] * kernel[12] + \
+ (int) image[y][x+d] * kernel[13] + \
+ (int) image[y][x+d+d] * kernel[14] + \
+ (int) image[y-1][x-d-d] * kernel[15] + \
+ (int) image[y-1][x-d] * kernel[16] + \
+ (int) image[y-1][x] * kernel[17] + \
+ (int) image[y-1][x+d] * kernel[18] + \
+ (int) image[y-1][x+d+d] * kernel[19] + \
+ (int) image[y-2][x-d-d] * kernel[20] + \
+ (int) image[y-2][x-d] * kernel[21] + \
+ (int) image[y-2][x] * kernel[22] + \
+ (int) image[y-2][x+d] * kernel[23] + \
+ (int) image[y-2][x+d+d] * kernel[24])
+
+ if (xsize == 3) {
+ /* 3x3 kernel. */
+ for (x = 0; x < im->xsize; x++)
+ imOut->image[0][x] = im->image8[0][x];
+ for (y = 1; y < im->ysize-1; y++) {
+ imOut->image[y][0] = im->image8[y][0];
+ for (x = 1; x < im->xsize-1; x++) {
+ sum = KERNEL3x3(im->image8, kernel, 1) / divisor + offset;
+ if (sum <= 0)
+ imOut->image8[y][x] = 0;
+ else if (sum >= 255)
+ imOut->image8[y][x] = 255;
+ else
+ imOut->image8[y][x] = (UINT8) sum;
+ }
+ imOut->image8[y][x] = im->image8[y][x];
+ }
+ for (x = 0; x < im->xsize; x++)
+ imOut->image8[y][x] = im->image8[y][x];
+ } else {
+ /* 5x5 kernel. */
+ for (y = 0; y < 2; y++)
+ for (x = 0; x < im->xsize; x++)
+ imOut->image8[y][x] = im->image8[y][x];
+ for (; y < im->ysize-2; y++) {
+ for (x = 0; x < 2; x++)
+ imOut->image8[y][x] = im->image8[y][x];
+ for (; x < im->xsize-2; x++) {
+ sum = KERNEL5x5(im->image8, kernel, 1) / divisor + offset;
+ if (sum <= 0)
+ imOut->image8[y][x] = 0;
+ else if (sum >= 255)
+ imOut->image8[y][x] = 255;
+ else
+ imOut->image8[y][x] = (UINT8) sum;
+ }
+ for (; x < im->xsize; x++)
+ imOut->image8[y][x] = im->image8[y][x];
+ }
+ for (; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++)
+ imOut->image8[y][x] = im->image8[y][x];
+ }
+ return imOut;
+}
+
diff --git a/libImaging/FliDecode.c b/libImaging/FliDecode.c
new file mode 100644
index 000000000..75eebe86c
--- /dev/null
+++ b/libImaging/FliDecode.c
@@ -0,0 +1,205 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for Autodesk Animator FLI/FLC animations
+ *
+ * history:
+ * 97-01-03 fl Created
+ * 97-01-17 fl Added SS2 support (FLC)
+ *
+ * Copyright (c) Fredrik Lundh 1997.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+#define I16(ptr)\
+ ((ptr)[0] + ((ptr)[1] << 8))
+
+#define I32(ptr)\
+ ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
+
+
+int
+ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+ int framesize;
+ int c, chunks;
+ int l, lines;
+ int i, j, x = 0, y, ymax;
+
+ /* If not even the chunk size is present, we'd better leave */
+
+ if (bytes < 4)
+ return 0;
+
+ /* We don't decode anything unless we have a full chunk in the
+ input buffer (on the other hand, the Python part of the driver
+ makes sure this is always the case) */
+
+ ptr = buf;
+
+ framesize = I32(ptr);
+ if (framesize < I32(ptr))
+ return 0;
+
+ /* Make sure this is a frame chunk. The Python driver takes
+ case of other chunk types. */
+
+ if (I16(ptr+4) != 0xF1FA) {
+ state->errcode = IMAGING_CODEC_UNKNOWN;
+ return -1;
+ }
+
+ chunks = I16(ptr+6);
+ ptr += 16;
+
+ /* Process subchunks */
+ for (c = 0; c < chunks; c++) {
+ UINT8 *data = ptr + 6;
+ switch (I16(ptr+4)) {
+ case 4: case 11:
+ /* FLI COLOR chunk */
+ break; /* ignored; handled by Python code */
+ case 7:
+ /* FLI SS2 chunk (word delta) */
+ lines = I16(data); data += 2;
+ for (l = y = 0; l < lines && y < state->ysize; l++, y++) {
+ UINT8* buf = (UINT8*) im->image[y];
+ int p, packets;
+ packets = I16(data); data += 2;
+ while (packets & 0x8000) {
+ /* flag word */
+ if (packets & 0x4000) {
+ y += 65536 - packets; /* skip lines */
+ if (y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+ buf = (UINT8*) im->image[y];
+ } else {
+ /* store last byte (used if line width is odd) */
+ buf[state->xsize-1] = (UINT8) packets;
+ }
+ packets = I16(data); data += 2;
+ }
+ for (p = x = 0; p < packets; p++) {
+ x += data[0]; /* pixel skip */
+ if (data[1] >= 128) {
+ i = 256-data[1]; /* run */
+ if (x + i + i > state->xsize)
+ break;
+ for (j = 0; j < i; j++) {
+ buf[x++] = data[2];
+ buf[x++] = data[3];
+ }
+ data += 2 + 2;
+ } else {
+ i = 2 * (int) data[1]; /* chunk */
+ if (x + i > state->xsize)
+ break;
+ memcpy(buf + x, data + 2, i);
+ data += 2 + i;
+ x += i;
+ }
+ }
+ if (p < packets)
+ break; /* didn't process all packets */
+ }
+ if (l < lines) {
+ /* didn't process all lines */
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+ break;
+ case 12:
+ /* FLI LC chunk (byte delta) */
+ y = I16(data); ymax = y + I16(data+2); data += 4;
+ for (; y < ymax && y < state->ysize; y++) {
+ UINT8* out = (UINT8*) im->image[y];
+ int p, packets = *data++;
+ for (p = x = 0; p < packets; p++, x += i) {
+ x += data[0]; /* skip pixels */
+ if (data[1] & 0x80) {
+ i = 256-data[1]; /* run */
+ if (x + i > state->xsize)
+ break;
+ memset(out + x, data[2], i);
+ data += 3;
+ } else {
+ i = data[1]; /* chunk */
+ if (x + i > state->xsize)
+ break;
+ memcpy(out + x, data + 2, i);
+ data += i + 2;
+ }
+ }
+ if (p < packets)
+ break; /* didn't process all packets */
+ }
+ if (y < ymax) {
+ /* didn't process all lines */
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+ break;
+ case 13:
+ /* FLI BLACK chunk */
+ for (y = 0; y < state->ysize; y++)
+ memset(im->image[y], 0, state->xsize);
+ break;
+ case 15:
+ /* FLI BRUN chunk */
+ for (y = 0; y < state->ysize; y++) {
+ UINT8* out = (UINT8*) im->image[y];
+ data += 1; /* ignore packetcount byte */
+ for (x = 0; x < state->xsize; x += i) {
+ if (data[0] & 0x80) {
+ i = 256 - data[0];
+ if (x + i > state->xsize)
+ break; /* safety first */
+ memcpy(out + x, data + 1, i);
+ data += i + 1;
+ } else {
+ i = data[0];
+ if (x + i > state->xsize)
+ break; /* safety first */
+ memset(out + x, data[1], i);
+ data += 2;
+ }
+ }
+ if (x != state->xsize) {
+ /* didn't unpack whole line */
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+ }
+ break;
+ case 16:
+ /* COPY chunk */
+ for (y = 0; y < state->ysize; y++) {
+ UINT8* buf = (UINT8*) im->image[y];
+ memcpy(buf+x, data, state->xsize);
+ data += state->xsize;
+ }
+ break;
+ case 18:
+ /* PSTAMP chunk */
+ break; /* ignored */
+ default:
+ /* unknown chunk */
+ /* printf("unknown FLI/FLC chunk: %d\n", I16(ptr+4)); */
+ state->errcode = IMAGING_CODEC_UNKNOWN;
+ return -1;
+ }
+ ptr += I32(ptr);
+ }
+
+ return -1; /* end of frame */
+}
diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c
new file mode 100644
index 000000000..41f535b56
--- /dev/null
+++ b/libImaging/Geometry.c
@@ -0,0 +1,959 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * the imaging geometry methods
+ *
+ * history:
+ * 1995-06-15 fl Created
+ * 1996-04-15 fl Changed origin
+ * 1996-05-18 fl Fixed rotate90/270 for rectangular images
+ * 1996-05-27 fl Added general purpose transform
+ * 1996-11-22 fl Don't crash when resizing from outside source image
+ * 1997-08-09 fl Fixed rounding error in resize
+ * 1998-09-21 fl Incorporated transformation patches (from Zircon #2)
+ * 1998-09-22 fl Added bounding box to transform engines
+ * 1999-02-03 fl Fixed bicubic filtering for RGB images
+ * 1999-02-16 fl Added fixed-point version of affine transform
+ * 2001-03-28 fl Fixed transform(EXTENT) for xoffset < 0
+ * 2003-03-10 fl Compiler tweaks
+ * 2004-09-19 fl Fixed bilinear/bicubic filtering of LA images
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB
+ * Copyright (c) 1995-1997 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+/* Undef if you don't need resampling filters */
+#define WITH_FILTERS
+
+#define COORD(v) ((v) < 0.0 ? -1 : ((int)(v)))
+#define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v)))
+
+/* -------------------------------------------------------------------- */
+/* Transpose operations */
+
+Imaging
+ImagingFlipLeftRight(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int x, y, xr;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+ if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize)
+ return (Imaging) ImagingError_Mismatch();
+
+ ImagingCopyInfo(imOut, imIn);
+
+#define FLIP_HORIZ(image)\
+ for (y = 0; y < imIn->ysize; y++) {\
+ xr = imIn->xsize-1;\
+ for (x = 0; x < imIn->xsize; x++, xr--)\
+ imOut->image[y][x] = imIn->image[y][xr];\
+ }
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8)
+ FLIP_HORIZ(image8)
+ else
+ FLIP_HORIZ(image32)
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+
+Imaging
+ImagingFlipTopBottom(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int y, yr;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+ if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize)
+ return (Imaging) ImagingError_Mismatch();
+
+ ImagingCopyInfo(imOut, imIn);
+
+ ImagingSectionEnter(&cookie);
+
+ yr = imIn->ysize-1;
+ for (y = 0; y < imIn->ysize; y++, yr--)
+ memcpy(imOut->image[yr], imIn->image[y], imIn->linesize);
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+
+Imaging
+ImagingRotate90(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int x, y, xr;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+ if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize)
+ return (Imaging) ImagingError_Mismatch();
+
+ ImagingCopyInfo(imOut, imIn);
+
+#define ROTATE_90(image)\
+ for (y = 0; y < imIn->ysize; y++) {\
+ xr = imIn->xsize-1;\
+ for (x = 0; x < imIn->xsize; x++, xr--)\
+ imOut->image[xr][y] = imIn->image[y][x];\
+ }
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8)
+ ROTATE_90(image8)
+ else
+ ROTATE_90(image32)
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+
+Imaging
+ImagingRotate180(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int x, y, xr, yr;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+ if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize)
+ return (Imaging) ImagingError_Mismatch();
+
+ ImagingCopyInfo(imOut, imIn);
+
+ yr = imIn->ysize-1;
+
+#define ROTATE_180(image)\
+ for (y = 0; y < imIn->ysize; y++, yr--) {\
+ xr = imIn->xsize-1;\
+ for (x = 0; x < imIn->xsize; x++, xr--)\
+ imOut->image[y][x] = imIn->image[yr][xr];\
+ }
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8)
+ ROTATE_180(image8)
+ else
+ ROTATE_180(image32)
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+
+Imaging
+ImagingRotate270(Imaging imOut, Imaging imIn)
+{
+ ImagingSectionCookie cookie;
+ int x, y, yr;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+ if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize)
+ return (Imaging) ImagingError_Mismatch();
+
+ ImagingCopyInfo(imOut, imIn);
+
+ yr = imIn->ysize - 1;
+
+#define ROTATE_270(image)\
+ for (y = 0; y < imIn->ysize; y++, yr--)\
+ for (x = 0; x < imIn->xsize; x++)\
+ imOut->image[x][y] = imIn->image[yr][x];
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8)
+ ROTATE_270(image8)
+ else
+ ROTATE_270(image32)
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Transforms */
+
+/* transform primitives (ImagingTransformMap) */
+
+static int
+affine_transform(double* xin, double* yin, int x, int y, void* data)
+{
+ /* full moon tonight. your compiler will generate bogus code
+ for simple expressions, unless you reorganize the code, or
+ install Service Pack 3 */
+
+ double* a = (double*) data;
+ double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
+ double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
+
+ xin[0] = a0 + a1*x + a2*y;
+ yin[0] = a3 + a4*x + a5*y;
+
+ return 1;
+}
+
+static int
+perspective_transform(double* xin, double* yin, int x, int y, void* data)
+{
+ double* a = (double*) data;
+ double a0 = a[0]; double a1 = a[1]; double a2 = a[2];
+ double a3 = a[3]; double a4 = a[4]; double a5 = a[5];
+ double a6 = a[6]; double a7 = a[7];
+
+ xin[0] = (a0 + a1*x + a2*y) / (a6*x + a7*y + 1);
+ yin[0] = (a3 + a4*x + a5*y) / (a6*x + a7*y + 1);
+
+ return 1;
+}
+
+#if 0
+static int
+quadratic_transform(double* xin, double* yin, int x, int y, void* data)
+{
+ double* a = (double*) data;
+
+ double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3];
+ double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7];
+ double a8 = a[8]; double a9 = a[9]; double a10 = a[10]; double a11 = a[11];
+
+ xin[0] = a0 + a1*x + a2*y + a3*x*x + a4*x*y + a5*y*y;
+ yin[0] = a6 + a7*x + a8*y + a9*x*x + a10*x*y + a11*y*y;
+
+ return 1;
+}
+#endif
+
+static int
+quad_transform(double* xin, double* yin, int x, int y, void* data)
+{
+ /* quad warp: map quadrilateral to rectangle */
+
+ double* a = (double*) data;
+ double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3];
+ double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7];
+
+ xin[0] = a0 + a1*x + a2*y + a3*x*y;
+ yin[0] = a4 + a5*x + a6*y + a7*x*y;
+
+ return 1;
+}
+
+/* transform filters (ImagingTransformFilter) */
+
+#ifdef WITH_FILTERS
+
+static int
+nearest_filter8(void* out, Imaging im, double xin, double yin, void* data)
+{
+ int x = COORD(xin);
+ int y = COORD(yin);
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return 0;
+ ((UINT8*)out)[0] = im->image8[y][x];
+ return 1;
+}
+
+static int
+nearest_filter16(void* out, Imaging im, double xin, double yin, void* data)
+{
+ int x = COORD(xin);
+ int y = COORD(yin);
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return 0;
+ ((INT16*)out)[0] = ((INT16*)(im->image8[y]))[x];
+ return 1;
+}
+
+static int
+nearest_filter32(void* out, Imaging im, double xin, double yin, void* data)
+{
+ int x = COORD(xin);
+ int y = COORD(yin);
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return 0;
+ ((INT32*)out)[0] = im->image32[y][x];
+ return 1;
+}
+
+#define XCLIP(im, x) ( ((x) < 0) ? 0 : ((x) < im->xsize) ? (x) : im->xsize-1 )
+#define YCLIP(im, y) ( ((y) < 0) ? 0 : ((y) < im->ysize) ? (y) : im->ysize-1 )
+
+#define BILINEAR(v, a, b, d)\
+ (v = (a) + ( (b) - (a) ) * (d))
+
+#define BILINEAR_HEAD(type)\
+ int x, y;\
+ int x0, x1;\
+ double v1, v2;\
+ double dx, dy;\
+ type* in;\
+ if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\
+ return 0;\
+ xin -= 0.5;\
+ yin -= 0.5;\
+ x = FLOOR(xin);\
+ y = FLOOR(yin);\
+ dx = xin - x;\
+ dy = yin - y;
+
+#define BILINEAR_BODY(type, image, step, offset) {\
+ in = (type*) ((image)[YCLIP(im, y)] + offset);\
+ x0 = XCLIP(im, x+0)*step;\
+ x1 = XCLIP(im, x+1)*step;\
+ BILINEAR(v1, in[x0], in[x1], dx);\
+ if (y+1 >= 0 && y+1 < im->ysize) {\
+ in = (type*) ((image)[y+1] + offset);\
+ BILINEAR(v2, in[x0], in[x1], dx);\
+ } else\
+ v2 = v1;\
+ BILINEAR(v1, v1, v2, dy);\
+}
+
+static int
+bilinear_filter8(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BILINEAR_HEAD(UINT8);
+ BILINEAR_BODY(UINT8, im->image8, 1, 0);
+ ((UINT8*)out)[0] = (UINT8) v1;
+ return 1;
+}
+
+static int
+bilinear_filter32I(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BILINEAR_HEAD(INT32);
+ BILINEAR_BODY(INT32, im->image32, 1, 0);
+ ((INT32*)out)[0] = (INT32) v1;
+ return 1;
+}
+
+static int
+bilinear_filter32F(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BILINEAR_HEAD(FLOAT32);
+ BILINEAR_BODY(FLOAT32, im->image32, 1, 0);
+ ((FLOAT32*)out)[0] = (FLOAT32) v1;
+ return 1;
+}
+
+static int
+bilinear_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BILINEAR_HEAD(UINT8);
+ BILINEAR_BODY(UINT8, im->image, 4, 0);
+ ((UINT8*)out)[0] = (UINT8) v1;
+ ((UINT8*)out)[1] = (UINT8) v1;
+ ((UINT8*)out)[2] = (UINT8) v1;
+ BILINEAR_BODY(UINT8, im->image, 4, 3);
+ ((UINT8*)out)[3] = (UINT8) v1;
+ return 1;
+}
+
+static int
+bilinear_filter32RGB(void* out, Imaging im, double xin, double yin, void* data)
+{
+ int b;
+ BILINEAR_HEAD(UINT8);
+ for (b = 0; b < im->bands; b++) {
+ BILINEAR_BODY(UINT8, im->image, 4, b);
+ ((UINT8*)out)[b] = (UINT8) v1;
+ }
+ return 1;
+}
+
+#define BICUBIC(v, v1, v2, v3, v4, d) {\
+ double p1 = v2;\
+ double p2 = -v1 + v3;\
+ double p3 = 2*(v1 - v2) + v3 - v4;\
+ double p4 = -v1 + v2 - v3 + v4;\
+ v = p1 + (d)*(p2 + (d)*(p3 + (d)*p4));\
+}
+
+#define BICUBIC_HEAD(type)\
+ int x = FLOOR(xin);\
+ int y = FLOOR(yin);\
+ int x0, x1, x2, x3;\
+ double v1, v2, v3, v4;\
+ double dx, dy;\
+ type* in;\
+ if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\
+ return 0;\
+ xin -= 0.5;\
+ yin -= 0.5;\
+ x = FLOOR(xin);\
+ y = FLOOR(yin);\
+ dx = xin - x;\
+ dy = yin - y;\
+ x--; y--;
+
+#define BICUBIC_BODY(type, image, step, offset) {\
+ in = (type*) ((image)[YCLIP(im, y)] + offset);\
+ x0 = XCLIP(im, x+0)*step;\
+ x1 = XCLIP(im, x+1)*step;\
+ x2 = XCLIP(im, x+2)*step;\
+ x3 = XCLIP(im, x+3)*step;\
+ BICUBIC(v1, in[x0], in[x1], in[x2], in[x3], dx);\
+ if (y+1 >= 0 && y+1 < im->ysize) {\
+ in = (type*) ((image)[y+1] + offset);\
+ BICUBIC(v2, in[x0], in[x1], in[x2], in[x3], dx);\
+ } else\
+ v2 = v1;\
+ if (y+2 >= 0 && y+2 < im->ysize) {\
+ in = (type*) ((image)[y+2] + offset);\
+ BICUBIC(v3, in[x0], in[x1], in[x2], in[x3], dx);\
+ } else\
+ v3 = v2;\
+ if (y+3 >= 0 && y+3 < im->ysize) {\
+ in = (type*) ((image)[y+3] + offset);\
+ BICUBIC(v4, in[x0], in[x1], in[x2], in[x3], dx);\
+ } else\
+ v4 = v3;\
+ BICUBIC(v1, v1, v2, v3, v4, dy);\
+}
+
+
+static int
+bicubic_filter8(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BICUBIC_HEAD(UINT8);
+ BICUBIC_BODY(UINT8, im->image8, 1, 0);
+ if (v1 <= 0.0)
+ ((UINT8*)out)[0] = 0;
+ else if (v1 >= 255.0)
+ ((UINT8*)out)[0] = 255;
+ else
+ ((UINT8*)out)[0] = (UINT8) v1;
+ return 1;
+}
+
+static int
+bicubic_filter32I(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BICUBIC_HEAD(INT32);
+ BICUBIC_BODY(INT32, im->image32, 1, 0);
+ ((INT32*)out)[0] = (INT32) v1;
+ return 1;
+}
+
+static int
+bicubic_filter32F(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BICUBIC_HEAD(FLOAT32);
+ BICUBIC_BODY(FLOAT32, im->image32, 1, 0);
+ ((FLOAT32*)out)[0] = (FLOAT32) v1;
+ return 1;
+}
+
+static int
+bicubic_filter32LA(void* out, Imaging im, double xin, double yin, void* data)
+{
+ BICUBIC_HEAD(UINT8);
+ BICUBIC_BODY(UINT8, im->image, 4, 0);
+ if (v1 <= 0.0) {
+ ((UINT8*)out)[0] = 0;
+ ((UINT8*)out)[1] = 0;
+ ((UINT8*)out)[2] = 0;
+ } else if (v1 >= 255.0) {
+ ((UINT8*)out)[0] = 255;
+ ((UINT8*)out)[1] = 255;
+ ((UINT8*)out)[2] = 255;
+ } else {
+ ((UINT8*)out)[0] = (UINT8) v1;
+ ((UINT8*)out)[1] = (UINT8) v1;
+ ((UINT8*)out)[2] = (UINT8) v1;
+ }
+ BICUBIC_BODY(UINT8, im->image, 4, 3);
+ if (v1 <= 0.0)
+ ((UINT8*)out)[3] = 0;
+ else if (v1 >= 255.0)
+ ((UINT8*)out)[3] = 255;
+ else
+ ((UINT8*)out)[3] = (UINT8) v1;
+ return 1;
+}
+
+static int
+bicubic_filter32RGB(void* out, Imaging im, double xin, double yin, void* data)
+{
+ int b;
+ BICUBIC_HEAD(UINT8);
+ for (b = 0; b < im->bands; b++) {
+ BICUBIC_BODY(UINT8, im->image, 4, b);
+ if (v1 <= 0.0)
+ ((UINT8*)out)[b] = 0;
+ else if (v1 >= 255.0)
+ ((UINT8*)out)[b] = 255;
+ else
+ ((UINT8*)out)[b] = (UINT8) v1;
+ }
+ return 1;
+}
+
+static ImagingTransformFilter
+getfilter(Imaging im, int filterid)
+{
+ switch (filterid) {
+ case IMAGING_TRANSFORM_NEAREST:
+ if (im->image8)
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ return (ImagingTransformFilter) nearest_filter8;
+ case IMAGING_TYPE_SPECIAL:
+ switch (im->pixelsize) {
+ case 1:
+ return (ImagingTransformFilter) nearest_filter8;
+ case 2:
+ return (ImagingTransformFilter) nearest_filter16;
+ case 4:
+ return (ImagingTransformFilter) nearest_filter32;
+ }
+ }
+ else
+ return (ImagingTransformFilter) nearest_filter32;
+ break;
+ case IMAGING_TRANSFORM_BILINEAR:
+ if (im->image8)
+ return (ImagingTransformFilter) bilinear_filter8;
+ else if (im->image32) {
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ if (im->bands == 2)
+ return (ImagingTransformFilter) bilinear_filter32LA;
+ else
+ return (ImagingTransformFilter) bilinear_filter32RGB;
+ case IMAGING_TYPE_INT32:
+ return (ImagingTransformFilter) bilinear_filter32I;
+ case IMAGING_TYPE_FLOAT32:
+ return (ImagingTransformFilter) bilinear_filter32F;
+ }
+ }
+ break;
+ case IMAGING_TRANSFORM_BICUBIC:
+ if (im->image8)
+ return (ImagingTransformFilter) bicubic_filter8;
+ else if (im->image32) {
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ if (im->bands == 2)
+ return (ImagingTransformFilter) bicubic_filter32LA;
+ else
+ return (ImagingTransformFilter) bicubic_filter32RGB;
+ case IMAGING_TYPE_INT32:
+ return (ImagingTransformFilter) bicubic_filter32I;
+ case IMAGING_TYPE_FLOAT32:
+ return (ImagingTransformFilter) bicubic_filter32F;
+ }
+ }
+ break;
+ }
+ /* no such filter */
+ return NULL;
+}
+
+#else
+#define getfilter(im, id) NULL
+#endif
+
+/* transformation engines */
+
+Imaging
+ImagingTransform(
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ ImagingTransformMap transform, void* transform_data,
+ ImagingTransformFilter filter, void* filter_data,
+ int fill)
+{
+ /* slow generic transformation. use ImagingTransformAffine or
+ ImagingScaleAffine where possible. */
+
+ ImagingSectionCookie cookie;
+ int x, y;
+ char *out;
+ double xx, yy;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+
+ ImagingCopyInfo(imOut, imIn);
+
+ ImagingSectionEnter(&cookie);
+
+ if (x0 < 0)
+ x0 = 0;
+ if (y0 < 0)
+ y0 = 0;
+ if (x1 > imOut->xsize)
+ x1 = imOut->xsize;
+ if (y1 > imOut->ysize)
+ y1 = imOut->ysize;
+
+ for (y = y0; y < y1; y++) {
+ out = imOut->image[y] + x0*imOut->pixelsize;
+ for (x = x0; x < x1; x++) {
+ if (!transform(&xx, &yy, x-x0, y-y0, transform_data) ||
+ !filter(out, imIn, xx, yy, filter_data)) {
+ if (fill)
+ memset(out, 0, imOut->pixelsize);
+ }
+ out += imOut->pixelsize;
+ }
+ }
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+static Imaging
+ImagingScaleAffine(Imaging imOut, Imaging imIn,
+ int x0, int y0, int x1, int y1,
+ double a[6], int fill)
+{
+ /* scale, nearest neighbour resampling */
+
+ ImagingSectionCookie cookie;
+ int x, y;
+ int xin;
+ double xo, yo;
+ int xmin, xmax;
+ int *xintab;
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+
+ ImagingCopyInfo(imOut, imIn);
+
+ if (x0 < 0)
+ x0 = 0;
+ if (y0 < 0)
+ y0 = 0;
+ if (x1 > imOut->xsize)
+ x1 = imOut->xsize;
+ if (y1 > imOut->ysize)
+ y1 = imOut->ysize;
+
+ xintab = (int*) malloc(imOut->xsize * sizeof(int));
+ if (!xintab) {
+ ImagingDelete(imOut);
+ return (Imaging) ImagingError_MemoryError();
+ }
+
+ xo = a[0];
+ yo = a[3];
+
+ xmin = x1;
+ xmax = x0;
+
+ /* Pretabulate horizontal pixel positions */
+ for (x = x0; x < x1; x++) {
+ xin = COORD(xo);
+ if (xin >= 0 && xin < (int) imIn->xsize) {
+ xmax = x+1;
+ if (x < xmin)
+ xmin = x;
+ xintab[x] = xin;
+ }
+ xo += a[1];
+ }
+
+#define AFFINE_SCALE(pixel, image)\
+ for (y = y0; y < y1; y++) {\
+ int yi = COORD(yo);\
+ pixel *in, *out;\
+ out = imOut->image[y];\
+ if (fill && x1 > x0)\
+ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\
+ if (yi >= 0 && yi < imIn->ysize) {\
+ in = imIn->image[yi];\
+ for (x = xmin; x < xmax; x++)\
+ out[x] = in[xintab[x]];\
+ }\
+ yo += a[5];\
+ }
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8) {
+ AFFINE_SCALE(UINT8, image8);
+ } else {
+ AFFINE_SCALE(INT32, image32);
+ }
+
+ ImagingSectionLeave(&cookie);
+
+ free(xintab);
+
+ return imOut;
+}
+
+static inline int
+check_fixed(double a[6], int x, int y)
+{
+ return (fabs(a[0] + x*a[1] + y*a[2]) < 32768.0 &&
+ fabs(a[3] + x*a[4] + y*a[5]) < 32768.0);
+}
+
+static inline Imaging
+affine_fixed(Imaging imOut, Imaging imIn,
+ int x0, int y0, int x1, int y1,
+ double a[6], int filterid, int fill)
+{
+ /* affine transform, nearest neighbour resampling, fixed point
+ arithmetics */
+
+ int x, y;
+ int xin, yin;
+ int xsize, ysize;
+ int xx, yy;
+ int a0, a1, a2, a3, a4, a5;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ xsize = (int) imIn->xsize;
+ ysize = (int) imIn->ysize;
+
+/* use 16.16 fixed point arithmetics */
+#define FIX(v) FLOOR((v)*65536.0 + 0.5)
+
+ a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]);
+ a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]);
+
+#define AFFINE_TRANSFORM_FIXED(pixel, image)\
+ for (y = y0; y < y1; y++) {\
+ pixel *out;\
+ xx = a0;\
+ yy = a3;\
+ out = imOut->image[y];\
+ if (fill && x1 > x0)\
+ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\
+ for (x = x0; x < x1; x++, out++) {\
+ xin = xx >> 16;\
+ if (xin >= 0 && xin < xsize) {\
+ yin = yy >> 16;\
+ if (yin >= 0 && yin < ysize)\
+ *out = imIn->image[yin][xin];\
+ }\
+ xx += a1;\
+ yy += a4;\
+ }\
+ a0 += a2;\
+ a3 += a5;\
+ }
+
+ if (imIn->image8)
+ AFFINE_TRANSFORM_FIXED(UINT8, image8)
+ else
+ AFFINE_TRANSFORM_FIXED(INT32, image32)
+
+ return imOut;
+}
+
+Imaging
+ImagingTransformAffine(Imaging imOut, Imaging imIn,
+ int x0, int y0, int x1, int y1,
+ double a[6], int filterid, int fill)
+{
+ /* affine transform, nearest neighbour resampling, floating point
+ arithmetics*/
+
+ ImagingSectionCookie cookie;
+ int x, y;
+ int xin, yin;
+ int xsize, ysize;
+ double xx, yy;
+ double xo, yo;
+
+ if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) {
+ /* Filtered transform */
+ ImagingTransformFilter filter = getfilter(imIn, filterid);
+ if (!filter)
+ return (Imaging) ImagingError_ValueError("unknown filter");
+ return ImagingTransform(
+ imOut, imIn,
+ x0, y0, x1, y1,
+ affine_transform, a,
+ filter, NULL, fill);
+ }
+
+ if (a[2] == 0 && a[4] == 0)
+ /* Scaling */
+ return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);
+
+ if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0)
+ return (Imaging) ImagingError_ModeError();
+
+ if (x0 < 0)
+ x0 = 0;
+ if (y0 < 0)
+ y0 = 0;
+ if (x1 > imOut->xsize)
+ x1 = imOut->xsize;
+ if (y1 > imOut->ysize)
+ y1 = imOut->ysize;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ /* translate all four corners to check if they are within the
+ range that can be represented by the fixed point arithmetics */
+
+ if (check_fixed(a, 0, 0) && check_fixed(a, x1-x0, y1-y0) &&
+ check_fixed(a, 0, y1-y0) && check_fixed(a, x1-x0, 0))
+ return affine_fixed(imOut, imIn, x0, y0, x1, y1, a, filterid, fill);
+
+ /* FIXME: cannot really think of any reasonable case when the
+ following code is used. maybe we should fall back on the slow
+ generic transform engine in this case? */
+
+ xsize = (int) imIn->xsize;
+ ysize = (int) imIn->ysize;
+
+ xo = a[0];
+ yo = a[3];
+
+#define AFFINE_TRANSFORM(pixel, image)\
+ for (y = y0; y < y1; y++) {\
+ pixel *out;\
+ xx = xo;\
+ yy = yo;\
+ out = imOut->image[y];\
+ if (fill && x1 > x0)\
+ memset(out+x0, 0, (x1-x0)*sizeof(pixel));\
+ for (x = x0; x < x1; x++, out++) {\
+ xin = COORD(xx);\
+ if (xin >= 0 && xin < xsize) {\
+ yin = COORD(yy);\
+ if (yin >= 0 && yin < ysize)\
+ *out = imIn->image[yin][xin];\
+ }\
+ xx += a[1];\
+ yy += a[4];\
+ }\
+ xo += a[2];\
+ yo += a[5];\
+ }
+
+ ImagingSectionEnter(&cookie);
+
+ if (imIn->image8)
+ AFFINE_TRANSFORM(UINT8, image8)
+ else
+ AFFINE_TRANSFORM(INT32, image32)
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+Imaging
+ImagingTransformPerspective(Imaging imOut, Imaging imIn,
+ int x0, int y0, int x1, int y1,
+ double a[8], int filterid, int fill)
+{
+ ImagingTransformFilter filter = getfilter(imIn, filterid);
+ if (!filter)
+ return (Imaging) ImagingError_ValueError("bad filter number");
+
+ return ImagingTransform(
+ imOut, imIn,
+ x0, y0, x1, y1,
+ perspective_transform, a,
+ filter, NULL,
+ fill);
+}
+
+Imaging
+ImagingTransformQuad(Imaging imOut, Imaging imIn,
+ int x0, int y0, int x1, int y1,
+ double a[8], int filterid, int fill)
+{
+ ImagingTransformFilter filter = getfilter(imIn, filterid);
+ if (!filter)
+ return (Imaging) ImagingError_ValueError("bad filter number");
+
+ return ImagingTransform(
+ imOut, imIn,
+ x0, y0, x1, y1,
+ quad_transform, a,
+ filter, NULL,
+ fill);
+}
+
+/* -------------------------------------------------------------------- */
+/* Convenience functions */
+
+Imaging
+ImagingResize(Imaging imOut, Imaging imIn, int filterid)
+{
+ double a[6];
+
+ if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize)
+ return ImagingCopy2(imOut, imIn);
+
+ memset(a, 0, sizeof a);
+ a[1] = (double) imIn->xsize / imOut->xsize;
+ a[5] = (double) imIn->ysize / imOut->ysize;
+
+ if (!filterid && imIn->type != IMAGING_TYPE_SPECIAL)
+ return ImagingScaleAffine(
+ imOut, imIn,
+ 0, 0, imOut->xsize, imOut->ysize,
+ a, 1);
+
+ return ImagingTransformAffine(
+ imOut, imIn,
+ 0, 0, imOut->xsize, imOut->ysize,
+ a, filterid, 1);
+}
+
+Imaging
+ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid)
+{
+ int xsize, ysize;
+ double sintheta, costheta;
+ double a[6];
+
+ /* Setup an affine transform to rotate around the image center */
+ theta = -theta * M_PI / 180.0;
+ sintheta = sin(theta);
+ costheta = cos(theta);
+
+ xsize = imOut->xsize;
+ ysize = imOut->ysize;
+
+ a[0] = -costheta * xsize/2 - sintheta * ysize/2 + xsize/2;
+ a[1] = costheta;
+ a[2] = sintheta;
+ a[3] = sintheta * xsize/2 - costheta * ysize/2 + ysize/2;
+ a[4] = -sintheta;
+ a[5] = costheta;
+
+ return ImagingTransformAffine(
+ imOut, imIn,
+ 0, 0, imOut->xsize, imOut->ysize,
+ a, filterid, 1);
+}
diff --git a/libImaging/GetBBox.c b/libImaging/GetBBox.c
new file mode 100644
index 000000000..d1722fe14
--- /dev/null
+++ b/libImaging/GetBBox.c
@@ -0,0 +1,316 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * helpers to bounding boxes, min/max values, number of colors, etc.
+ *
+ * history:
+ * 1996-07-22 fl Created
+ * 1996-12-30 fl Added projection stuff
+ * 1998-07-12 fl Added extrema stuff
+ * 2004-09-17 fl Added colors stuff
+ *
+ * Copyright (c) 1997-2004 by Secret Labs AB.
+ * Copyright (c) 1996-2004 by Fredrik Lundh.
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingGetBBox(Imaging im, int bbox[4])
+{
+ /* Get the bounding box for any non-zero data in the image.*/
+
+ int x, y;
+ int has_data;
+
+ /* Initialize bounding box to max values */
+ bbox[0] = im->xsize;
+ bbox[1] = -1;
+ bbox[2] = bbox[3] = 0;
+
+#define GETBBOX(image, mask)\
+ for (y = 0; y < im->ysize; y++) {\
+ has_data = 0;\
+ for (x = 0; x < im->xsize; x++)\
+ if (im->image[y][x] & mask) {\
+ has_data = 1;\
+ if (x < bbox[0])\
+ bbox[0] = x;\
+ if (x >= bbox[2])\
+ bbox[2] = x+1;\
+ }\
+ if (has_data) {\
+ if (bbox[1] < 0)\
+ bbox[1] = y;\
+ bbox[3] = y+1;\
+ }\
+ }
+
+ if (im->image8) {
+ GETBBOX(image8, 0xff);
+ } else {
+ INT32 mask = 0xffffffff;
+ if (im->bands == 3)
+ ((UINT8*) &mask)[3] = 0;
+ GETBBOX(image32, mask);
+ }
+
+ /* Check that we got a box */
+ if (bbox[1] < 0)
+ return 0; /* no data */
+
+ return 1; /* ok */
+}
+
+
+int
+ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj)
+{
+ /* Get projection arrays for non-zero data in the image.*/
+
+ int x, y;
+ int has_data;
+
+ /* Initialize projection arrays */
+ memset(xproj, 0, im->xsize);
+ memset(yproj, 0, im->ysize);
+
+#define GETPROJ(image, mask)\
+ for (y = 0; y < im->ysize; y++) {\
+ has_data = 0;\
+ for (x = 0; x < im->xsize; x++)\
+ if (im->image[y][x] & mask) {\
+ has_data = 1;\
+ xproj[x] = 1;\
+ }\
+ if (has_data)\
+ yproj[y] = 1;\
+ }
+
+ if (im->image8) {
+ GETPROJ(image8, 0xff);
+ } else {
+ INT32 mask = 0xffffffff;
+ if (im->bands == 3)
+ ((UINT8*) &mask)[3] = 0;
+ GETPROJ(image32, mask);
+ }
+
+ return 1; /* ok */
+}
+
+
+int
+ImagingGetExtrema(Imaging im, void *extrema)
+{
+ int x, y;
+ INT32 imin, imax;
+ FLOAT32 fmin, fmax;
+
+ if (im->bands != 1) {
+ (void) ImagingError_ModeError();
+ return -1; /* mismatch */
+ }
+
+ if (!im->xsize || !im->ysize)
+ return 0; /* zero size */
+
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ imin = imax = im->image8[0][0];
+ for (y = 0; y < im->ysize; y++) {
+ UINT8* in = im->image8[y];
+ for (x = 0; x < im->xsize; x++) {
+ if (imin > in[x])
+ imin = in[x];
+ else if (imax < in[x])
+ imax = in[x];
+ }
+ }
+ ((UINT8*) extrema)[0] = (UINT8) imin;
+ ((UINT8*) extrema)[1] = (UINT8) imax;
+ break;
+ case IMAGING_TYPE_INT32:
+ imin = imax = im->image32[0][0];
+ for (y = 0; y < im->ysize; y++) {
+ INT32* in = im->image32[y];
+ for (x = 0; x < im->xsize; x++) {
+ if (imin > in[x])
+ imin = in[x];
+ else if (imax < in[x])
+ imax = in[x];
+ }
+ }
+ ((INT32*) extrema)[0] = imin;
+ ((INT32*) extrema)[1] = imax;
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ fmin = fmax = ((FLOAT32*) im->image32[0])[0];
+ for (y = 0; y < im->ysize; y++) {
+ FLOAT32* in = (FLOAT32*) im->image32[y];
+ for (x = 0; x < im->xsize; x++) {
+ if (fmin > in[x])
+ fmin = in[x];
+ else if (fmax < in[x])
+ fmax = in[x];
+ }
+ }
+ ((FLOAT32*) extrema)[0] = fmin;
+ ((FLOAT32*) extrema)[1] = fmax;
+ break;
+ case IMAGING_TYPE_SPECIAL:
+ if (strcmp(im->mode, "I;16") == 0) {
+ imin = imax = ((UINT16*) im->image8[0])[0];
+ for (y = 0; y < im->ysize; y++) {
+ UINT16* in = (UINT16 *) im->image[y];
+ for (x = 0; x < im->xsize; x++) {
+ if (imin > in[x])
+ imin = in[x];
+ else if (imax < in[x])
+ imax = in[x];
+ }
+ }
+ ((UINT16*) extrema)[0] = (UINT16) imin;
+ ((UINT16*) extrema)[1] = (UINT16) imax;
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ (void) ImagingError_ModeError();
+ return -1;
+ }
+ return 1; /* ok */
+}
+
+
+/* static ImagingColorItem* getcolors8(Imaging im, int maxcolors, int* size);*/
+static ImagingColorItem* getcolors32(Imaging im, int maxcolors, int* size);
+
+ImagingColorItem*
+ImagingGetColors(Imaging im, int maxcolors, int* size)
+{
+ /* FIXME: add support for 8-bit images */
+ return getcolors32(im, maxcolors, size);
+}
+
+static ImagingColorItem*
+getcolors32(Imaging im, int maxcolors, int* size)
+{
+ unsigned int h;
+ unsigned int i, incr;
+ int colors;
+ INT32 pixel_mask;
+ int x, y;
+ ImagingColorItem* table;
+ ImagingColorItem* v;
+
+ unsigned int code_size;
+ unsigned int code_poly;
+ unsigned int code_mask;
+
+ /* note: the hash algorithm used here is based on the dictionary
+ code in Python 2.1.3; the exact implementation is borrowed from
+ Python's Unicode property database (written by yours truly) /F */
+
+ static int SIZES[] = {
+ 4,3, 8,3, 16,3, 32,5, 64,3, 128,3, 256,29, 512,17, 1024,9, 2048,5,
+ 4096,83, 8192,27, 16384,43, 32768,3, 65536,45, 131072,9, 262144,39,
+ 524288,39, 1048576,9, 2097152,5, 4194304,3, 8388608,33, 16777216,27,
+ 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) {
+ if (SIZES[i] > maxcolors) {
+ code_size = SIZES[i];
+ code_poly = SIZES[i+1];
+ code_mask = code_size - 1;
+ break;
+ }
+ }
+
+ /* printf("code_size=%d\n", code_size); */
+ /* printf("code_poly=%d\n", code_poly); */
+
+ if (!code_size)
+ return ImagingError_MemoryError(); /* just give up */
+
+ if (!im->image32)
+ return ImagingError_ModeError();
+
+ table = calloc(code_size + 1, sizeof(ImagingColorItem));
+ if (!table)
+ return ImagingError_MemoryError();
+
+ pixel_mask = 0xffffffff;
+ if (im->bands == 3)
+ ((UINT8*) &pixel_mask)[3] = 0;
+
+ colors = 0;
+
+ for (y = 0; y < im->ysize; y++) {
+ INT32* p = im->image32[y];
+ for (x = 0; x < im->xsize; x++) {
+ INT32 pixel = p[x] & pixel_mask;
+ h = (pixel); /* null hashing */
+ i = (~h) & code_mask;
+ v = &table[i];
+ if (!v->count) {
+ /* add to table */
+ if (colors++ == maxcolors)
+ goto overflow;
+ v->x = x; v->y = y;
+ v->pixel = pixel;
+ v->count = 1;
+ continue;
+ } else if (v->pixel == pixel) {
+ v->count++;
+ continue;
+ }
+ incr = (h ^ (h >> 3)) & code_mask;
+ if (!incr)
+ incr = code_mask;
+ for (;;) {
+ i = (i + incr) & code_mask;
+ v = &table[i];
+ if (!v->count) {
+ /* add to table */
+ if (colors++ == maxcolors)
+ goto overflow;
+ v->x = x; v->y = y;
+ v->pixel = pixel;
+ v->count = 1;
+ break;
+ } else if (v->pixel == pixel) {
+ v->count++;
+ break;
+ }
+ incr = incr << 1;
+ if (incr > code_mask)
+ incr = incr ^ code_poly;
+ }
+ }
+ }
+
+overflow:
+
+ /* pack the table */
+ for (x = y = 0; x < (int) code_size; x++)
+ if (table[x].count) {
+ if (x != y)
+ table[y] = table[x];
+ y++;
+ }
+ table[y].count = 0; /* mark end of table */
+
+ *size = colors;
+
+ return table;
+}
diff --git a/libImaging/Gif.h b/libImaging/Gif.h
new file mode 100644
index 000000000..85d428b0d
--- /dev/null
+++ b/libImaging/Gif.h
@@ -0,0 +1,109 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * Declarations for a fast, suspendable GIF decoder.
+ *
+ * Copyright (c) Fredrik Lundh 1995-96.
+ */
+
+
+/* Max size for a LZW code word. */
+
+#define GIFBITS 12
+
+#define GIFTABLE (1<
+#include /* memcpy() */
+
+#include "Gif.h"
+
+
+#define NEWLINE(state, context) {\
+ state->x = 0;\
+ state->y += context->step;\
+ while (state->y >= state->ysize)\
+ switch (context->interlace) {\
+ case 1:\
+ context->repeat = state->y = 4;\
+ context->interlace = 2;\
+ break;\
+ case 2:\
+ context->step = 4;\
+ context->repeat = state->y = 2;\
+ context->interlace = 3;\
+ break;\
+ case 3:\
+ context->step = 2;\
+ context->repeat = state->y = 1;\
+ context->interlace = 0;\
+ break;\
+ default:\
+ return -1;\
+ }\
+ if (state->y < state->ysize)\
+ out = im->image8[state->y + state->yoff] + state->xoff;\
+}
+
+
+int
+ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes)
+{
+ UINT8* p;
+ UINT8* out;
+ int c, i;
+ int thiscode;
+ GIFDECODERSTATE *context = (GIFDECODERSTATE*) state->context;
+
+ UINT8 *ptr = buffer;
+
+ if (!state->state) {
+
+ /* Initialise state */
+ if (context->bits < 0 || context->bits > 8) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ /* Clear code */
+ context->clear = 1 << context->bits;
+
+ /* End code */
+ context->end = context->clear + 1;
+
+ /* Interlace */
+ if (context->interlace) {
+ context->interlace = 1;
+ context->step = context->repeat = 8;
+ } else
+ context->step = 1;
+
+ state->state = 1;
+ }
+
+ out = im->image8[state->y + state->yoff] + state->xoff + state->x;
+
+ for (;;) {
+
+ if (state->state == 1) {
+
+ /* First free entry in table */
+ context->next = context->clear + 2;
+
+ /* Initial code size */
+ context->codesize = context->bits + 1;
+ context->codemask = (1 << context->codesize) - 1;
+
+ /* Buffer pointer. We fill the buffer from right, which
+ allows us to return all of it in one operation. */
+ context->bufferindex = GIFBUFFER;
+
+ state->state = 2;
+ }
+
+ if (context->bufferindex < GIFBUFFER) {
+
+ /* Return whole buffer in one chunk */
+ i = GIFBUFFER - context->bufferindex;
+ p = &context->buffer[context->bufferindex];
+
+ context->bufferindex = GIFBUFFER;
+
+ } else {
+
+ /* Get current symbol */
+
+ while (context->bitcount < context->codesize) {
+
+ if (context->blocksize > 0) {
+
+ /* Read next byte */
+ c = *ptr++; bytes--;
+
+ context->blocksize--;
+
+ /* New bits are shifted in from from the left. */
+ context->bitbuffer |= (INT32) c << context->bitcount;
+ context->bitcount += 8;
+
+ } else {
+
+ /* New GIF block */
+
+ /* We don't start decoding unless we have a full block */
+ if (bytes < 1)
+ return ptr - buffer;
+ c = *ptr;
+ if (bytes < c+1)
+ return ptr - buffer;
+
+ context->blocksize = c;
+
+ ptr++; bytes--;
+
+ }
+ }
+
+ /* Extract current symbol from bit buffer. */
+ c = (int) context->bitbuffer & context->codemask;
+
+ /* Adjust buffer */
+ context->bitbuffer >>= context->codesize;
+ context->bitcount -= context->codesize;
+
+ /* If c is less than "clear", it's a data byte. Otherwise,
+ it's either clear/end or a code symbol which should be
+ expanded. */
+
+ if (c == context->clear) {
+ if (state->state != 2)
+ state->state = 1;
+ continue;
+ }
+
+ if (c == context->end)
+ break;
+
+ i = 1;
+ p = &context->lastdata;
+
+ if (state->state == 2) {
+
+ /* First valid symbol after clear; use as is */
+ if (c > context->clear) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->lastdata = context->lastcode = c;
+ state->state = 3;
+
+ } else {
+
+ thiscode = c;
+
+ if (c > context->next) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ if (c == context->next) {
+
+ /* c == next is allowed. not sure why. */
+
+ if (context->bufferindex <= 0) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->buffer[--context->bufferindex] =
+ context->lastdata;
+
+ c = context->lastcode;
+
+ }
+
+ while (c >= context->clear) {
+
+ /* Copy data string to buffer (beginning from right) */
+
+ if (context->bufferindex <= 0 || c >= GIFTABLE) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->buffer[--context->bufferindex] =
+ context->data[c];
+
+ c = context->link[c];
+ }
+
+ context->lastdata = c;
+
+ if (context->next < GIFTABLE) {
+
+ /* We'll only add this symbol if we have room
+ for it (take advise, Netscape!) */
+ context->data[context->next] = c;
+ context->link[context->next] = context->lastcode;
+
+ if (context->next == context->codemask &&
+ context->codesize < GIFBITS) {
+
+ /* Expand code size */
+ context->codesize++;
+ context->codemask = (1 << context->codesize) - 1;
+ }
+
+ context->next++;
+
+ }
+
+ context->lastcode = thiscode;
+
+ }
+ }
+
+ /* Copy the bytes into the image */
+ if (state->y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ /* To squeeze some extra pixels out of this loop, we test for
+ some common cases and handle them separately. */
+
+ /* FIXME: should we handle the transparency index in here??? */
+
+ if (i == 1) {
+ if (state->x < state->xsize-1) {
+ /* Single pixel, not at the end of the line. */
+ *out++ = p[0];
+ state->x++;
+ continue;
+ }
+ } else if (state->x + i <= state->xsize) {
+ /* This string fits into current line. */
+ memcpy(out, p, i);
+ out += i;
+ state->x += i;
+ if (state->x == state->xsize) {
+ NEWLINE(state, context);
+ }
+ continue;
+ }
+
+ /* No shortcut, copy pixel by pixel */
+ for (c = 0; c < i; c++) {
+ *out++ = p[c];
+ if (++state->x >= state->xsize) {
+ NEWLINE(state, context);
+ }
+ }
+ }
+
+ return ptr - buffer;
+}
diff --git a/libImaging/GifEncode.c b/libImaging/GifEncode.c
new file mode 100644
index 000000000..f4d07598f
--- /dev/null
+++ b/libImaging/GifEncode.c
@@ -0,0 +1,319 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * encoder for uncompressed GIF data
+ *
+ * history:
+ * 97-01-05 fl created (writes uncompressed data)
+ * 97-08-27 fl fixed off-by-one error in buffer size test
+ * 98-07-09 fl added interlace write support
+ * 99-02-07 fl rewritten, now uses a run-length encoding strategy
+ * 99-02-08 fl improved run-length encoding for long runs
+ *
+ * Copyright (c) Secret Labs AB 1997-99.
+ * Copyright (c) Fredrik Lundh 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+#include "Gif.h"
+
+/* codes from 0 to 255 are literals */
+#define CLEAR_CODE 256
+#define EOF_CODE 257
+#define FIRST_CODE 258
+#define LAST_CODE 511
+
+enum { INIT, ENCODE, ENCODE_EOF, FLUSH, EXIT };
+
+/* to make things a little less complicated, we use a simple output
+ queue to hold completed blocks. the following inlined function
+ adds a byte to the current block. it allocates a new block if
+ necessary. */
+
+static inline int
+emit(GIFENCODERSTATE *context, int byte)
+{
+ /* write a byte to the output buffer */
+
+ if (!context->block || context->block->size == 255) {
+ GIFENCODERBLOCK* block;
+
+ /* 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;
+ while (block && block->next)
+ block = block->next;
+ if (block)
+ block->next = context->block;
+ else
+ context->flush = context->block;
+ }
+
+ /* get a new block */
+ if (context->free) {
+ block = context->free;
+ context->free = NULL;
+ } else {
+ block = malloc(sizeof(GIFENCODERBLOCK));
+ if (!block)
+ return 0;
+ }
+
+ block->size = 0;
+ block->next = NULL;
+
+ context->block = block;
+
+ }
+
+ /* write new byte to block */
+ context->block->data[context->block->size++] = byte;
+
+ return 1;
+}
+
+/* write a code word to the current block. this is a macro to make
+ sure it's inlined on all platforms */
+
+#define EMIT(code) {\
+ context->bitbuffer |= ((INT32) (code)) << context->bitcount;\
+ context->bitcount += 9;\
+ while (context->bitcount >= 8) {\
+ if (!emit(context, (UINT8) context->bitbuffer)) {\
+ state->errcode = IMAGING_CODEC_MEMORY;\
+ return 0;\
+ }\
+ context->bitbuffer >>= 8;\
+ context->bitcount -= 8;\
+ }\
+}
+
+/* write a run. we use a combination of literals and combinations of
+ literals. this can give quite decent compression for images with
+ long stretches of identical pixels. but remember: if you want
+ really good compression, use another file format. */
+
+#define EMIT_RUN(label) {\
+label:\
+ while (context->count > 0) {\
+ int run = 2;\
+ EMIT(context->last);\
+ context->count--;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ goto label;\
+ }\
+ while (context->count >= run) {\
+ EMIT(state->count - 1);\
+ context->count -= run;\
+ run++;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ goto label;\
+ }\
+ }\
+ if (context->count > 1) {\
+ EMIT(state->count - 1 - (run - context->count));\
+ context->count = 0;\
+ if (state->count++ == LAST_CODE) {\
+ EMIT(CLEAR_CODE);\
+ state->count = FIRST_CODE;\
+ }\
+ break;\
+ }\
+ }\
+}
+
+int
+ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+ int this;
+
+ GIFENCODERBLOCK* block;
+ GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context;
+
+ if (!state->state) {
+
+ /* place a clear code in the output buffer */
+ context->bitbuffer = CLEAR_CODE;
+ context->bitcount = 9;
+
+ state->count = FIRST_CODE;
+
+ if (context->interlace) {
+ context->interlace = 1;
+ context->step = 8;
+ } else
+ context->step = 1;
+
+ context->last = -1;
+
+ /* sanity check */
+ if (state->xsize <= 0 || state->ysize <= 0)
+ state->state = ENCODE_EOF;
+
+ }
+
+ ptr = buf;
+
+ for (;;)
+
+ switch (state->state) {
+
+ case INIT:
+ case ENCODE:
+
+ /* identify and store a run of pixels */
+
+ if (state->x == 0 || state->x >= state->xsize) {
+
+ if (!context->interlace && state->y >= state->ysize) {
+ state->state = ENCODE_EOF;
+ break;
+ }
+
+ if (context->flush) {
+ state->state = FLUSH;
+ break;
+ }
+
+ /* get another line of data */
+ state->shuffle(
+ state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize
+ );
+
+ state->x = 0;
+
+ if (state->state == INIT) {
+ /* preload the run-length buffer and get going */
+ context->last = state->buffer[0];
+ context->count = state->x = 1;
+ state->state = ENCODE;
+ }
+
+ /* step forward, according to the interlace settings */
+ state->y += context->step;
+ while (context->interlace && state->y >= state->ysize)
+ switch (context->interlace) {
+ case 1:
+ state->y = 4;
+ context->interlace = 2;
+ break;
+ case 2:
+ context->step = 4;
+ state->y = 2;
+ context->interlace = 3;
+ break;
+ case 3:
+ context->step = 2;
+ state->y = 1;
+ context->interlace = 0;
+ break;
+ default:
+ /* just make sure we don't loop forever */
+ context->interlace = 0;
+ }
+
+ }
+
+ this = state->buffer[state->x++];
+
+ if (this == context->last)
+ context->count++;
+ else {
+ EMIT_RUN(label1);
+ context->last = this;
+ context->count = 1;
+ }
+ break;
+
+
+ case ENCODE_EOF:
+
+ /* write the final run */
+ EMIT_RUN(label2);
+
+ /* write an end of image marker */
+ EMIT(EOF_CODE);
+
+ /* empty the bit buffer */
+ while (context->bitcount > 0) {
+ if (!emit(context, (UINT8) context->bitbuffer)) {
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return 0;
+ }
+ context->bitbuffer >>= 8;
+ context->bitcount -= 8;
+ }
+
+ /* flush the last block, and exit */
+ if (context->block) {
+ GIFENCODERBLOCK* block;
+ block = context->flush;
+ while (block && block->next)
+ block = block->next;
+ if (block)
+ block->next = context->block;
+ else
+ context->flush = context->block;
+ context->block = NULL;
+ }
+
+ state->state = EXIT;
+
+ /* fall through... */
+
+ case EXIT:
+ case FLUSH:
+
+ while (context->flush) {
+
+ /* get a block from the flush queue */
+ block = context->flush;
+
+ if (block->size > 0) {
+
+ /* make sure it fits into the output buffer */
+ if (bytes < block->size+1)
+ return ptr - buf;
+
+ ptr[0] = block->size;
+ memcpy(ptr+1, block->data, block->size);
+
+ ptr += block->size+1;
+ bytes -= block->size+1;
+
+ }
+
+ context->flush = block->next;
+
+ if (context->free)
+ free(context->free);
+ context->free = block;
+
+ }
+
+ if (state->state == EXIT) {
+ /* this was the last block! */
+ if (context->free)
+ free(context->free);
+ state->errcode = IMAGING_CODEC_END;
+ return ptr - buf;
+ }
+
+ state->state = ENCODE;
+ break;
+ }
+}
diff --git a/libImaging/HexDecode.c b/libImaging/HexDecode.c
new file mode 100644
index 000000000..14f5241dc
--- /dev/null
+++ b/libImaging/HexDecode.c
@@ -0,0 +1,67 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for hex encoded image data
+ *
+ * history:
+ * 96-05-16 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\
+ (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\
+ (v >= 'A' && v <= 'F') ? v - 'A' + 10 : -1)
+
+int
+ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+ int a, b;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (bytes < 2)
+ return ptr - buf;
+
+ a = HEX(ptr[0]);
+ b = HEX(ptr[1]);
+
+ if (a < 0 || b < 0) {
+
+ ptr++;
+ bytes--;
+
+ } else {
+
+ ptr += 2;
+ bytes -= 2;
+
+ state->buffer[state->x] = (a<<4) + b;
+
+ if (++state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y], state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ }
+ }
+}
diff --git a/libImaging/Histo.c b/libImaging/Histo.c
new file mode 100644
index 000000000..513c84475
--- /dev/null
+++ b/libImaging/Histo.c
@@ -0,0 +1,169 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * histogram support
+ *
+ * history:
+ * 1995-06-15 fl Created.
+ * 1996-04-05 fl Fixed histogram for multiband images.
+ * 1997-02-23 fl Added mask support
+ * 1998-07-01 fl Added basic 32-bit float/integer support
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB.
+ * Copyright (c) 1995-2003 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+/* HISTOGRAM */
+/* --------------------------------------------------------------------
+ * Take a histogram of an image. Returns a histogram object containing
+ * 256 slots per band in the input image.
+ */
+
+void
+ImagingHistogramDelete(ImagingHistogram h)
+{
+ if (h->histogram)
+ free(h->histogram);
+ free(h);
+}
+
+ImagingHistogram
+ImagingHistogramNew(Imaging im)
+{
+ ImagingHistogram h;
+
+ /* Create histogram descriptor */
+ h = calloc(1, sizeof(struct ImagingHistogramInstance));
+ strcpy(h->mode, im->mode);
+ h->bands = im->bands;
+ h->histogram = calloc(im->pixelsize, 256 * sizeof(long));
+
+ return h;
+}
+
+ImagingHistogram
+ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax)
+{
+ ImagingSectionCookie cookie;
+ int x, y, i;
+ ImagingHistogram h;
+ INT32 imin, imax;
+ FLOAT32 fmin, fmax, scale;
+
+ if (!im)
+ return ImagingError_ModeError();
+
+ if (imMask) {
+ /* Validate mask */
+ if (im->xsize != imMask->xsize || im->ysize != imMask->ysize)
+ return ImagingError_Mismatch();
+ if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0)
+ return ImagingError_ValueError("bad transparency mask");
+ }
+
+ h = ImagingHistogramNew(im);
+
+ if (imMask) {
+ /* mask */
+ if (im->image8) {
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++)
+ if (imMask->image8[y][x] != 0)
+ h->histogram[im->image8[y][x]]++;
+ ImagingSectionLeave(&cookie);
+ } else { /* yes, we need the braces. C isn't Python! */
+ if (im->type != IMAGING_TYPE_UINT8)
+ return ImagingError_ModeError();
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < im->ysize; y++) {
+ UINT8* in = (UINT8*) im->image32[y];
+ for (x = 0; x < im->xsize; x++)
+ if (imMask->image8[y][x] != 0) {
+ h->histogram[(*in++)]++;
+ h->histogram[(*in++)+256]++;
+ h->histogram[(*in++)+512]++;
+ h->histogram[(*in++)+768]++;
+ } else
+ in += 4;
+ }
+ ImagingSectionLeave(&cookie);
+ }
+ } else {
+ /* mask not given; process pixels in image */
+ if (im->image8) {
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++)
+ h->histogram[im->image8[y][x]]++;
+ ImagingSectionLeave(&cookie);
+ } else {
+ switch (im->type) {
+ case IMAGING_TYPE_UINT8:
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < im->ysize; y++) {
+ UINT8* in = (UINT8*) im->image[y];
+ for (x = 0; x < im->xsize; x++) {
+ h->histogram[(*in++)]++;
+ h->histogram[(*in++)+256]++;
+ h->histogram[(*in++)+512]++;
+ h->histogram[(*in++)+768]++;
+ }
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ case IMAGING_TYPE_INT32:
+ if (!minmax)
+ return ImagingError_ValueError("min/max not given");
+ if (!im->xsize || !im->ysize)
+ break;
+ imin = ((INT32*) minmax)[0];
+ imax = ((INT32*) minmax)[1];
+ if (imin >= imax)
+ break;
+ ImagingSectionEnter(&cookie);
+ scale = 255.0F / (imax - imin);
+ for (y = 0; y < im->ysize; y++) {
+ INT32* in = im->image32[y];
+ for (x = 0; x < im->xsize; x++) {
+ i = (int) (((*in++)-imin)*scale);
+ if (i >= 0 && i < 256)
+ h->histogram[i]++;
+ }
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ if (!minmax)
+ return ImagingError_ValueError("min/max not given");
+ if (!im->xsize || !im->ysize)
+ break;
+ fmin = ((FLOAT32*) minmax)[0];
+ fmax = ((FLOAT32*) minmax)[1];
+ if (fmin >= fmax)
+ break;
+ ImagingSectionEnter(&cookie);
+ scale = 255.0F / (fmax - fmin);
+ for (y = 0; y < im->ysize; y++) {
+ FLOAT32* in = (FLOAT32*) im->image32[y];
+ for (x = 0; x < im->xsize; x++) {
+ i = (int) (((*in++)-fmin)*scale);
+ if (i >= 0 && i < 256)
+ h->histogram[i]++;
+ }
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ }
+ }
+ }
+
+ return h;
+}
diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h
new file mode 100644
index 000000000..2effd3870
--- /dev/null
+++ b/libImaging/ImDib.h
@@ -0,0 +1,64 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * Windows DIB specifics
+ *
+ * Copyright (c) Secret Labs AB 1997-98.
+ * Copyright (c) Fredrik Lundh 1996.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#ifdef WIN32
+
+#if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__)
+/* already defined in basetsd.h */
+#undef INT32
+#undef INT64
+#undef UINT32
+#endif
+
+#include
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct ImagingDIBInstance {
+ /* Windows interface */
+ HDC dc;
+ HBITMAP bitmap;
+ HGDIOBJ old_bitmap;
+ BITMAPINFO *info;
+ UINT8 *bits;
+ HPALETTE palette;
+ /* Used by cut and paste */
+ char mode[4];
+ int xsize, ysize;
+ int pixelsize;
+ int linesize;
+ ImagingShuffler pack;
+ ImagingShuffler unpack;
+};
+
+typedef struct ImagingDIBInstance* ImagingDIB;
+
+extern char* ImagingGetModeDIB(int size_out[2]);
+
+extern ImagingDIB ImagingNewDIB(const char *mode, int xsize, int ysize);
+
+extern void ImagingDeleteDIB(ImagingDIB im);
+
+extern void ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]);
+extern void ImagingExposeDIB(ImagingDIB dib, int dc);
+
+extern int ImagingQueryPaletteDIB(ImagingDIB dib, int dc);
+
+extern void ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/libImaging/ImPlatform.h b/libImaging/ImPlatform.h
new file mode 100644
index 000000000..4ffd6fdef
--- /dev/null
+++ b/libImaging/ImPlatform.h
@@ -0,0 +1,72 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * platform declarations for the imaging core library
+ *
+ * Copyright (c) Fredrik Lundh 1995-2003.
+ */
+
+#include "Python.h"
+
+/* Check that we have an ANSI compliant compiler */
+#ifndef HAVE_PROTOTYPES
+#error Sorry, this library requires support for ANSI prototypes.
+#endif
+#ifndef STDC_HEADERS
+#error Sorry, this library requires ANSI header files.
+#endif
+
+#if defined(_MSC_VER)
+#ifndef WIN32
+#define WIN32
+#endif
+/* VC++ 4.0 is a bit annoying when it comes to precision issues (like
+ claiming that "float a = 0.0;" would lead to loss of precision). I
+ don't like to see warnings from my code, but since I still want to
+ keep it readable, I simply switch off a few warnings instead of adding
+ the tons of casts that VC++ seem to require. This code is compiled
+ with numerous other compilers as well, so any real errors are likely
+ to be catched anyway. */
+#pragma warning(disable: 4244) /* conversion from 'float' to 'int' */
+#endif
+
+#if defined(_MSC_VER)
+#define inline __inline
+#elif !defined(USE_INLINE)
+#define inline
+#endif
+
+#if SIZEOF_SHORT == 2
+#define INT16 short
+#elif SIZEOF_INT == 2
+#define INT16 int
+#else
+#define INT16 short /* most things works just fine anyway... */
+#endif
+
+#if SIZEOF_SHORT == 4
+#define INT32 short
+#elif SIZEOF_INT == 4
+#define INT32 int
+#elif SIZEOF_LONG == 4
+#define INT32 long
+#else
+#error Cannot find required 32-bit integer type
+#endif
+
+#if SIZEOF_LONG == 8
+#define INT64 long
+#elif SIZEOF_LONG_LONG == 8
+#define INT64 long
+#endif
+
+/* assume IEEE; tweak if necessary (patches are welcome) */
+#define FLOAT32 float
+#define FLOAT64 double
+
+#define INT8 signed char
+#define UINT8 unsigned char
+
+#define UINT16 unsigned INT16
+#define UINT32 unsigned INT32
diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h
new file mode 100644
index 000000000..4609376ad
--- /dev/null
+++ b/libImaging/Imaging.h
@@ -0,0 +1,497 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * declarations for the imaging core library
+ *
+ * Copyright (c) 1997-2005 by Secret Labs AB
+ * Copyright (c) 1995-2005 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "ImPlatform.h"
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#ifndef M_PI
+#define M_PI 3.14159265359
+#endif
+
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * Image data organization:
+ *
+ * mode bytes byte order
+ * -------------------------------
+ * 1 1 1
+ * L 1 L
+ * P 1 P
+ * I 4 I (32-bit integer, native byte order)
+ * F 4 F (32-bit IEEE float, native byte order)
+ * RGB 4 R, G, B, -
+ * RGBA 4 R, G, B, A
+ * CMYK 4 C, M, Y, K
+ * YCbCr 4 Y, Cb, Cr, -
+ *
+ * experimental modes (incomplete):
+ * LA 4 L, -, -, A
+ * PA 4 P, -, -, A
+ * I;16 2 I (16-bit integer, native byte order)
+ *
+ * "P" is an 8-bit palette mode, which should be mapped through the
+ * palette member to get an output image. Check palette->mode to
+ * find the corresponding "real" mode.
+ *
+ * For information on how to access Imaging objects from your own C
+ * extensions, see http://www.effbot.org/zone/pil-extending.htm
+ */
+
+/* Handles */
+
+typedef struct ImagingMemoryInstance* Imaging;
+
+typedef struct ImagingAccessInstance* ImagingAccess;
+typedef struct ImagingHistogramInstance* ImagingHistogram;
+typedef struct ImagingOutlineInstance* ImagingOutline;
+typedef struct ImagingPaletteInstance* ImagingPalette;
+
+/* handle magics (used with PyCObject). */
+#define IMAGING_MAGIC "PIL Imaging"
+
+/* pixel types */
+#define IMAGING_TYPE_UINT8 0
+#define IMAGING_TYPE_INT32 1
+#define IMAGING_TYPE_FLOAT32 2
+#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */
+
+struct ImagingMemoryInstance {
+
+ /* Format */
+ char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */
+ int type; /* Data type (IMAGING_TYPE_*) */
+ int depth; /* Depth (ignored in this version) */
+ int bands; /* Number of bands (1, 2, 3, or 4) */
+ int xsize; /* Image dimension. */
+ int ysize;
+
+ /* Colour palette (for "P" images only) */
+ ImagingPalette palette;
+
+ /* Data pointers */
+ UINT8 **image8; /* Set for 8-bit images (pixelsize=1). */
+ INT32 **image32; /* Set for 32-bit images (pixelsize=4). */
+
+ /* Internals */
+ char **image; /* Actual raster data. */
+ char *block; /* Set if data is allocated in a single block. */
+
+ int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */
+ int linesize; /* Size of a line, in bytes (xsize * pixelsize) */
+
+ /* Virtual methods */
+ void (*destroy)(Imaging im);
+};
+
+
+#define IMAGING_PIXEL_1(im,x,y) ((im)->image8[(y)][(x)])
+#define IMAGING_PIXEL_L(im,x,y) ((im)->image8[(y)][(x)])
+#define IMAGING_PIXEL_LA(im,x,y) ((im)->image[(y)][(x)*4])
+#define IMAGING_PIXEL_P(im,x,y) ((im)->image8[(y)][(x)])
+#define IMAGING_PIXEL_PA(im,x,y) ((im)->image[(y)][(x)*4])
+#define IMAGING_PIXEL_I(im,x,y) ((im)->image32[(y)][(x)])
+#define IMAGING_PIXEL_F(im,x,y) (((FLOAT32*)(im)->image32[y])[x])
+#define IMAGING_PIXEL_RGB(im,x,y) ((im)->image[(y)][(x)*4])
+#define IMAGING_PIXEL_RGBA(im,x,y) ((im)->image[(y)][(x)*4])
+#define IMAGING_PIXEL_CMYK(im,x,y) ((im)->image[(y)][(x)*4])
+#define IMAGING_PIXEL_YCbCr(im,x,y) ((im)->image[(y)][(x)*4])
+
+#define IMAGING_PIXEL_UINT8(im,x,y) ((im)->image8[(y)][(x)])
+#define IMAGING_PIXEL_INT32(im,x,y) ((im)->image32[(y)][(x)])
+#define IMAGING_PIXEL_FLOAT32(im,x,y) (((FLOAT32*)(im)->image32[y])[x])
+
+struct ImagingAccessInstance {
+ const char* mode;
+ void* (*line)(Imaging im, int x, int y);
+ void (*get_pixel)(Imaging im, int x, int y, void* pixel);
+ void (*put_pixel)(Imaging im, int x, int y, const void* pixel);
+};
+
+struct ImagingHistogramInstance {
+
+ /* Format */
+ char mode[4+1]; /* Band names (of corresponding source image) */
+ int bands; /* Number of bands (1, 3, or 4) */
+
+ /* Data */
+ long *histogram; /* Histogram (bands*256 longs) */
+
+};
+
+
+struct ImagingPaletteInstance {
+
+ /* Format */
+ char mode[4+1]; /* Band names */
+
+ /* Data */
+ UINT8 palette[1024];/* Palette data (same format as image data) */
+
+ INT16* cache; /* Palette cache (used for predefined palettes) */
+ int keep_cache; /* This palette will be reused; keep cache */
+
+};
+
+
+/* Objects */
+/* ------- */
+
+extern int ImagingNewCount;
+
+extern Imaging ImagingNew(const char* mode, int xsize, int ysize);
+extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn);
+extern void ImagingDelete(Imaging im);
+
+extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize);
+extern Imaging ImagingNewArray(const char* mode, int xsize, int ysize);
+extern Imaging ImagingNewMap(const char* filename, int readonly,
+ const char* mode, int xsize, int ysize);
+
+extern Imaging ImagingNewPrologue(const char *mode,
+ unsigned xsize, unsigned ysize);
+extern Imaging ImagingNewPrologueSubtype(const char *mode,
+ unsigned xsize, unsigned ysize,
+ int structure_size);
+extern Imaging ImagingNewEpilogue(Imaging im);
+
+extern void ImagingCopyInfo(Imaging destination, Imaging source);
+
+extern void ImagingHistogramDelete(ImagingHistogram histogram);
+
+extern void ImagingAccessInit(void);
+extern ImagingAccess ImagingAccessNew(Imaging im);
+extern void _ImagingAccessDelete(Imaging im, ImagingAccess access);
+#define ImagingAccessDelete(im, access) /* nop, for now */
+/*#define ImagingAccessDelete(im, access) \
+ ((access)->dynamic ? _ImagingAccessDelete((im), (access)), 0 : 0)) */
+
+extern ImagingPalette ImagingPaletteNew(const char *mode);
+extern ImagingPalette ImagingPaletteNewBrowser(void);
+extern ImagingPalette ImagingPaletteDuplicate(ImagingPalette palette);
+extern void ImagingPaletteDelete(ImagingPalette palette);
+
+extern int ImagingPaletteCachePrepare(ImagingPalette palette);
+extern void ImagingPaletteCacheUpdate(ImagingPalette palette,
+ int r, int g, int b);
+extern void ImagingPaletteCacheDelete(ImagingPalette palette);
+
+#define ImagingPaletteCache(p, r, g, b)\
+ p->cache[(r>>2) + (g>>2)*64 + (b>>2)*64*64]
+
+extern Imaging ImagingQuantize(Imaging im, int colours, int mode, int kmeans);
+
+/* Threading */
+/* --------- */
+
+typedef void* ImagingSectionCookie;
+
+extern void ImagingSectionEnter(ImagingSectionCookie* cookie);
+extern void ImagingSectionLeave(ImagingSectionCookie* cookie);
+
+/* Exceptions */
+/* ---------- */
+
+extern void* ImagingError_IOError(void);
+extern void* ImagingError_MemoryError(void);
+extern void* ImagingError_ModeError(void); /* maps to ValueError by default */
+extern void* ImagingError_Mismatch(void); /* maps to ValueError by default */
+extern void* ImagingError_ValueError(const char* message);
+extern void ImagingError_Clear(void);
+
+/* Transform callbacks */
+/* ------------------- */
+
+/* standard transforms */
+#define IMAGING_TRANSFORM_AFFINE 0
+#define IMAGING_TRANSFORM_PERSPECTIVE 2
+#define IMAGING_TRANSFORM_QUAD 3
+
+
+/* standard filters */
+#define IMAGING_TRANSFORM_NEAREST 0
+#define IMAGING_TRANSFORM_ANTIALIAS 1
+#define IMAGING_TRANSFORM_BILINEAR 2
+#define IMAGING_TRANSFORM_BICUBIC 3
+
+typedef int (*ImagingTransformMap)(double* X, double* Y,
+ int x, int y, void* data);
+typedef int (*ImagingTransformFilter)(void* out, Imaging im,
+ double x, double y,
+ void* data);
+
+/* Image Manipulation Methods */
+/* -------------------------- */
+
+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 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);
+extern int ImagingFill2(
+ Imaging into, const void* ink, Imaging mask,
+ int x0, int y0, int x1, int y1);
+extern Imaging ImagingFillBand(Imaging im, int band, int color);
+extern Imaging ImagingFillLinearGradient(const char* mode);
+extern Imaging ImagingFillRadialGradient(const char* mode);
+extern Imaging ImagingFilter(
+ Imaging im, int xsize, int ysize, const FLOAT32* kernel,
+ FLOAT32 offset, FLOAT32 divisor);
+extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn);
+extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn);
+extern Imaging ImagingGaussianBlur(Imaging im, Imaging imOut, float radius);
+extern Imaging ImagingGetBand(Imaging im, int band);
+extern int ImagingGetBBox(Imaging im, int bbox[4]);
+typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem;
+extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors,
+ int *colors);
+extern int ImagingGetExtrema(Imaging im, void *extrema);
+extern int ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj);
+extern ImagingHistogram ImagingGetHistogram(
+ Imaging im, Imaging mask, void *extrema);
+extern Imaging ImagingModeFilter(Imaging im, int size);
+extern Imaging ImagingNegative(Imaging im);
+extern Imaging ImagingOffset(Imaging im, int xoffset, int yoffset);
+extern int ImagingPaste(
+ Imaging into, Imaging im, Imaging mask,
+ int x0, int y0, int x1, int y1);
+extern Imaging ImagingPoint(
+ Imaging im, const char* tablemode, const void* table);
+extern Imaging ImagingPointTransform(
+ Imaging imIn, double scale, double offset);
+extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band);
+extern Imaging ImagingRankFilter(Imaging im, int size, int rank);
+extern Imaging ImagingResize(Imaging imOut, Imaging imIn, int filter);
+extern Imaging ImagingRotate(
+ Imaging imOut, Imaging imIn, double theta, int filter);
+extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn);
+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,
+ double a[8], int filter, int fill);
+extern Imaging ImagingTransformAffine(
+ 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,
+ double a[8], int filter, int fill);
+extern Imaging ImagingTransform(
+ Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1,
+ ImagingTransformMap transform, void* transform_data,
+ ImagingTransformFilter filter, void* filter_data,
+ int fill);
+extern Imaging ImagingUnsharpMask(
+ Imaging im, Imaging imOut, float radius, int percent, int threshold);
+
+extern Imaging ImagingCopy2(Imaging imOut, Imaging imIn);
+extern Imaging ImagingConvert2(Imaging imOut, Imaging imIn);
+
+/* Channel operations */
+/* any mode, except "F" */
+extern Imaging ImagingChopLighter(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopDarker(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopDifference(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopMultiply(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopScreen(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopAdd(
+ Imaging imIn1, Imaging imIn2, float scale, int offset);
+extern Imaging ImagingChopSubtract(
+ Imaging imIn1, Imaging imIn2, float scale, int offset);
+extern Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2);
+
+/* "1" images only */
+extern Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopOr(Imaging imIn1, Imaging imIn2);
+extern Imaging ImagingChopXor(Imaging imIn1, Imaging imIn2);
+
+/* Image measurement */
+extern void ImagingCrack(Imaging im, int x0, int y0);
+
+/* Graphics */
+struct ImagingAffineMatrixInstance {
+ float a[9];
+};
+
+typedef struct ImagingAffineMatrixInstance *ImagingAffineMatrix;
+
+extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int op);
+extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap,
+ const void* ink, int op);
+extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int fill,
+ int op);
+extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink, int fill, int op);
+extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink, int op);
+extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink, int width, int op);
+extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1,
+ int start, int end, const void* ink, int fill,
+ int op);
+extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op);
+extern int ImagingDrawPolygon(Imaging im, int points, int *xy,
+ const void* ink, int fill, int op);
+extern int ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1,
+ const void* ink, int fill, int op);
+
+/* Level 2 graphics (WORK IN PROGRESS) */
+extern ImagingOutline ImagingOutlineNew(void);
+extern void ImagingOutlineDelete(ImagingOutline outline);
+
+extern int ImagingDrawOutline(Imaging im, ImagingOutline outline,
+ const void* ink, int fill, int op);
+
+extern int ImagingOutlineMove(ImagingOutline outline, float x, float y);
+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 */
+extern Imaging ImagingEffectSpread(Imaging imIn, int distance);
+extern Imaging ImagingEffectNoise(int xsize, int ysize, float sigma);
+extern Imaging ImagingEffectMandelbrot(int xsize, int ysize,
+ double extent[4], int quality);
+
+/* Obsolete */
+extern int ImagingToString(Imaging im, int orientation, char *buffer);
+extern int ImagingFromString(Imaging im, int orientation, char *buffer);
+
+
+/* File I/O */
+/* -------- */
+
+/* Built-in drivers */
+extern Imaging ImagingOpenPPM(const char* filename);
+extern int ImagingSavePPM(Imaging im, const char* filename);
+
+/* Utility functions */
+extern UINT32 ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes);
+
+/* Codecs */
+typedef struct ImagingCodecStateInstance *ImagingCodecState;
+typedef int (*ImagingCodec)(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+
+extern int ImagingBitDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingEpsEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingFliDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingGifDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingGifEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingHexDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+#ifdef HAVE_LIBJPEG
+extern int ImagingJpegDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+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_LIBMPEG
+extern int ImagingMpegDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+#endif
+extern int ImagingMspDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingPackbitsDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingPcdDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingPcxDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingPcxEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingRawDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingRawEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingSunRleDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingTgaRleDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingXbmDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingXbmEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+#ifdef HAVE_LIBZ
+extern int ImagingZipDecode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+extern int ImagingZipEncode(Imaging im, ImagingCodecState state,
+ UINT8* buffer, int bytes);
+#endif
+
+typedef void (*ImagingShuffler)(UINT8* out, const UINT8* in, int pixels);
+
+/* Public shufflers */
+extern void ImagingPackRGB(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingPackBGR(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingUnpackYCbCr(UINT8* out, const UINT8* in, int pixels);
+
+extern void ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels);
+extern void ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels);
+
+extern ImagingShuffler ImagingFindUnpacker(const char* mode,
+ const char* rawmode, int* bits_out);
+extern ImagingShuffler ImagingFindPacker(const char* mode,
+ const char* rawmode, int* bits_out);
+
+struct ImagingCodecStateInstance {
+ int count;
+ int state;
+ int errcode;
+ int x, y;
+ int ystep;
+ int xsize, ysize, xoff, yoff;
+ ImagingShuffler shuffle;
+ int bits, bytes;
+ UINT8 *buffer;
+ void *context;
+};
+
+/* Errcodes */
+#define IMAGING_CODEC_END 1
+#define IMAGING_CODEC_OVERRUN -1
+#define IMAGING_CODEC_BROKEN -2
+#define IMAGING_CODEC_UNKNOWN -3
+#define IMAGING_CODEC_CONFIG -8
+#define IMAGING_CODEC_MEMORY -9
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h
new file mode 100644
index 000000000..d39165f3c
--- /dev/null
+++ b/libImaging/Jpeg.h
@@ -0,0 +1,104 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * declarations for the IJG JPEG codec interface.
+ *
+ * Copyright (c) 1995-2001 by Secret Labs AB
+ * Copyright (c) 1995-1996 by Fredrik Lundh
+ */
+
+#include "jpeglib.h"
+
+#include
+
+
+typedef struct {
+ struct jpeg_error_mgr pub; /* "public" fields */
+ jmp_buf setjmp_buffer; /* for return to caller */
+} JPEGERROR;
+
+
+/* -------------------------------------------------------------------- */
+/* Decoder */
+
+typedef struct {
+ struct jpeg_source_mgr pub;
+ int skip;
+} JPEGSOURCE;
+
+typedef struct {
+
+ /* CONFIGURATION */
+
+ /* Jpeg file mode (empty if not known) */
+ char jpegmode[8+1];
+
+ /* Converter output mode (input to the shuffler). If empty,
+ convert conversions are disabled */
+ char rawmode[8+1];
+
+ /* If set, trade quality for speed */
+ int draft;
+
+ /* Scale factor (1, 2, 4, 8) */
+ int scale;
+
+ /* PRIVATE CONTEXT (set by decoder) */
+
+ struct jpeg_decompress_struct cinfo;
+
+ JPEGERROR error;
+
+ JPEGSOURCE source;
+
+} JPEGSTATE;
+
+
+/* -------------------------------------------------------------------- */
+/* Encoder */
+
+typedef struct {
+ struct jpeg_destination_mgr pub;
+ /* might add something some other day */
+} JPEGDESTINATION;
+
+typedef struct {
+
+ /* CONFIGURATION */
+
+ /* Quality (1-100, 0 means default) */
+ int quality;
+
+ /* Progressive mode */
+ int progressive;
+
+ /* Smoothing factor (1-100, 0 means none) */
+ int smooth;
+
+ /* Optimize Huffman tables (slow) */
+ int optimize;
+
+ /* Stream type (0=full, 1=tables only, 2=image only) */
+ int streamtype;
+
+ /* DPI setting (0=square pixels, otherwide DPI) */
+ int xdpi, ydpi;
+
+ /* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */
+ int subsampling;
+
+ /* Extra data (to be injected after header) */
+ char* extra; int extra_size;
+
+ /* PRIVATE CONTEXT (set by encoder) */
+
+ struct jpeg_compress_struct cinfo;
+
+ JPEGERROR error;
+
+ JPEGDESTINATION destination;
+
+ int extra_offset;
+
+} JPEGENCODERSTATE;
diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c
new file mode 100644
index 000000000..11b2767ba
--- /dev/null
+++ b/libImaging/JpegDecode.c
@@ -0,0 +1,267 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for JPEG image data.
+ *
+ * history:
+ * 1996-05-02 fl Created
+ * 1996-05-05 fl Handle small JPEG files correctly
+ * 1996-05-28 fl Added "draft mode" support
+ * 1997-01-25 fl Added colour conversion override
+ * 1998-01-31 fl Adapted to libjpeg 6a
+ * 1998-07-12 fl Extended YCbCr support
+ * 1998-12-29 fl Added new state to handle suspension in multipass modes
+ * 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
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#ifdef HAVE_LIBJPEG
+
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef HAVE_STDDEF_H
+#undef UINT8
+#undef UINT16
+#undef UINT32
+#undef INT16
+#undef INT32
+
+#include "Jpeg.h"
+
+
+/* -------------------------------------------------------------------- */
+/* Suspending input handler */
+/* -------------------------------------------------------------------- */
+
+METHODDEF(void)
+stub(j_decompress_ptr cinfo)
+{
+ /* empty */
+}
+
+METHODDEF(boolean)
+fill_input_buffer(j_decompress_ptr cinfo)
+{
+ /* Suspension */
+ return FALSE;
+}
+
+METHODDEF(void)
+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;
+ } else {
+ /* Skip portion of the buffer */
+ source->pub.bytes_in_buffer -= num_bytes;
+ source->pub.next_input_byte += num_bytes;
+ source->skip = 0;
+ }
+}
+
+
+GLOBAL(void)
+jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source)
+{
+ cinfo->src = (void*) source;
+
+ /* Prepare for suspending reader */
+ source->pub.init_source = stub;
+ source->pub.fill_input_buffer = fill_input_buffer;
+ source->pub.skip_input_data = skip_input_data;
+ source->pub.resync_to_restart = jpeg_resync_to_restart;
+ source->pub.term_source = stub;
+ source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+
+ source->skip = 0;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Error handler */
+/* -------------------------------------------------------------------- */
+
+METHODDEF(void)
+error(j_common_ptr cinfo)
+{
+ JPEGERROR* error;
+ error = (JPEGERROR*) cinfo->err;
+ longjmp(error->setjmp_buffer, 1);
+}
+
+METHODDEF(void)
+output(j_common_ptr cinfo)
+{
+ /* nothing */
+}
+
+/* -------------------------------------------------------------------- */
+/* Decoder */
+/* -------------------------------------------------------------------- */
+
+int
+ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ JPEGSTATE* context = (JPEGSTATE*) state->context;
+ int ok;
+
+ if (setjmp(context->error.setjmp_buffer)) {
+ /* 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);
+
+ /* Ready to decode */
+ state->state = 1;
+
+ }
+
+ /* Load the source buffer */
+ context->source.pub.next_input_byte = buf;
+ 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;
+ }
+
+ 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);
+
+ } while (ok == JPEG_HEADER_TABLES_ONLY);
+
+ if (ok == JPEG_SUSPENDED)
+ break;
+
+ /* Decoder settings */
+
+ /* 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;
+ }
+
+ 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 */
+
+ 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))
+ break;
+
+ 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 */
+
+ case 4:
+
+ /* 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;
+
+ }
+
+ /* Return number of bytes consumed */
+ return context->source.pub.next_input_byte - buf;
+
+}
+
+#endif
+
diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c
new file mode 100644
index 000000000..1e2191a69
--- /dev/null
+++ b/libImaging/JpegEncode.c
@@ -0,0 +1,284 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * coder for JPEG data
+ *
+ * history:
+ * 1996-05-06 fl created
+ * 1996-07-16 fl don't drop last block of encoded data
+ * 1996-12-30 fl added quality and progressive settings
+ * 1997-01-08 fl added streamtype settings
+ * 1998-01-31 fl adapted to libjpeg 6a
+ * 1998-07-12 fl added YCbCr support
+ * 2001-04-16 fl added DPI write support
+ *
+ * Copyright (c) 1997-2001 by Secret Labs AB
+ * Copyright (c) 1995-1997 by Fredrik Lundh
+ *
+ * See the README file for details on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#ifdef HAVE_LIBJPEG
+
+#undef HAVE_PROTOTYPES
+#undef HAVE_STDLIB_H
+#undef HAVE_STDDEF_H
+#undef UINT8
+#undef UINT16
+#undef UINT32
+#undef INT16
+#undef INT32
+
+#include "Jpeg.h"
+
+/* -------------------------------------------------------------------- */
+/* Suspending output handler */
+/* -------------------------------------------------------------------- */
+
+METHODDEF(void)
+stub(j_compress_ptr cinfo)
+{
+ /* empty */
+}
+
+METHODDEF(boolean)
+empty_output_buffer (j_compress_ptr cinfo)
+{
+ /* Suspension */
+ return FALSE;
+}
+
+GLOBAL(void)
+jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination)
+{
+ cinfo->dest = (void*) destination;
+
+ destination->pub.init_destination = stub;
+ destination->pub.empty_output_buffer = empty_output_buffer;
+ destination->pub.term_destination = stub;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* 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);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Encoder */
+/* -------------------------------------------------------------------- */
+
+int
+ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ JPEGENCODERSTATE* context = (JPEGENCODERSTATE*) state->context;
+ int ok;
+
+ if (setjmp(context->error.setjmp_buffer)) {
+ /* JPEG error handler */
+ jpeg_destroy_compress(&context->cinfo);
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ if (!state->state) {
+
+ /* Setup compression context (very similar to the decoder) */
+ context->cinfo.err = jpeg_std_error(&context->error.pub);
+ context->error.pub.error_exit = error;
+ jpeg_create_compress(&context->cinfo);
+ jpeg_buffer_dest(&context->cinfo, &context->destination);
+
+ context->extra_offset = 0;
+
+ /* Ready to encode */
+ state->state = 1;
+
+ }
+
+ /* Load the destination buffer */
+ context->destination.pub.next_output_byte = buf;
+ context->destination.pub.free_in_buffer = bytes;
+
+ switch (state->state) {
+
+ case 1:
+
+ context->cinfo.image_width = state->xsize;
+ context->cinfo.image_height = state->ysize;
+
+ switch (state->bits) {
+ case 8:
+ context->cinfo.input_components = 1;
+ context->cinfo.in_color_space = JCS_GRAYSCALE;
+ break;
+ case 24:
+ context->cinfo.input_components = 3;
+ if (strcmp(im->mode, "YCbCr") == 0)
+ context->cinfo.in_color_space = JCS_YCbCr;
+ else
+ context->cinfo.in_color_space = JCS_RGB;
+ break;
+ case 32:
+ context->cinfo.input_components = 4;
+ context->cinfo.in_color_space = JCS_CMYK;
+ break;
+ default:
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ /* Compressor configuration */
+ jpeg_set_defaults(&context->cinfo);
+ 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 */
+ {
+ 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 */
+ {
+ 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 */
+ {
+ 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:
+ {
+ /* Use the lib's default */
+ break;
+ }
+ }
+ if (context->progressive)
+ jpeg_simple_progression(&context->cinfo);
+ context->cinfo.smoothing_factor = context->smooth;
+ context->cinfo.optimize_coding = (boolean) context->optimize;
+ if (context->xdpi > 0 && context->ydpi > 0) {
+ context->cinfo.density_unit = 1; /* dots per inch */
+ context->cinfo.X_density = context->xdpi;
+ context->cinfo.Y_density = context->ydpi;
+ }
+ switch (context->streamtype) {
+ case 1:
+ /* tables only -- not yet implemented */
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ case 2:
+ /* image only */
+ jpeg_suppress_tables(&context->cinfo, TRUE);
+ jpeg_start_compress(&context->cinfo, FALSE);
+ /* suppress extra section */
+ context->extra_offset = context->extra_size;
+ break;
+ default:
+ /* interchange stream */
+ jpeg_start_compress(&context->cinfo, TRUE);
+ break;
+ }
+ state->state++;
+ /* fall through */
+
+ case 2:
+
+ if (context->extra) {
+ /* copy extra buffer to output buffer */
+ unsigned int n = context->extra_size - context->extra_offset;
+ if (n > context->destination.pub.free_in_buffer)
+ n = context->destination.pub.free_in_buffer;
+ memcpy(context->destination.pub.next_output_byte,
+ context->extra + context->extra_offset, n);
+ context->destination.pub.next_output_byte += n;
+ context->destination.pub.free_in_buffer -= n;
+ context->extra_offset += n;
+ if (context->extra_offset >= context->extra_size)
+ state->state++;
+ else
+ break;
+ } else
+ state->state++;
+
+ case 3:
+
+ ok = 1;
+ while (state->y < state->ysize) {
+ state->shuffle(state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize);
+ ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1);
+ if (ok != 1)
+ break;
+ state->y++;
+ }
+
+ if (ok != 1)
+ break;
+ state->state++;
+ /* fall through */
+
+ case 4:
+
+ /* Finish compression */
+ if (context->destination.pub.free_in_buffer < 100)
+ break;
+ jpeg_finish_compress(&context->cinfo);
+
+ /* Clean up */
+ if (context->extra)
+ free(context->extra);
+ jpeg_destroy_compress(&context->cinfo);
+ /* if (jerr.pub.num_warnings) return BROKEN; */
+ state->errcode = IMAGING_CODEC_END;
+ break;
+
+ }
+
+ /* Return number of bytes in output buffer */
+ return context->destination.pub.next_output_byte - buf;
+
+}
+
+const char*
+ImagingJpegVersion(void)
+{
+ static char version[20];
+ sprintf(version, "%d.%d", JPEG_LIB_VERSION / 10, JPEG_LIB_VERSION % 10);
+ return version;
+}
+
+#endif
diff --git a/libImaging/Lzw.h b/libImaging/Lzw.h
new file mode 100644
index 000000000..7d087ac47
--- /dev/null
+++ b/libImaging/Lzw.h
@@ -0,0 +1,52 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * declarations for the TIFF LZW decoder.
+ *
+ * Copyright (c) Fredrik Lundh 1995-96.
+ */
+
+
+/* Max size for LZW code words */
+
+#define LZWBITS 12
+
+#define LZWTABLE (1<
+#include /* memcpy() */
+
+#include "Lzw.h"
+
+
+int
+ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* p;
+ int c, i;
+ int thiscode;
+ LZWSTATE* context = (LZWSTATE*) state->context;
+
+ unsigned char *ptr = buf;
+
+ if (!state->state) {
+
+ /* Clear code */
+ context->clear = 1 << 8;
+
+ /* End code */
+ context->end = context->clear + 1;
+
+ state->state = 1;
+ }
+
+ for (;;) {
+
+ if (state->state == 1) {
+
+ /* First free entry in table */
+ context->next = context->clear + 2;
+
+ /* Initial code size */
+ context->codesize = 8 + 1;
+ context->codemask = (1 << context->codesize) - 1;
+
+ /* Buffer pointer. We fill the buffer from right, which
+ allows us to return all of it in one operation. */
+ context->bufferindex = LZWBUFFER;
+
+ state->state = 2;
+ }
+
+ if (context->bufferindex < LZWBUFFER) {
+
+ /* Return whole buffer in one chunk */
+ i = LZWBUFFER - context->bufferindex;
+ p = &context->buffer[context->bufferindex];
+
+ context->bufferindex = LZWBUFFER;
+
+ } else {
+
+ /* Get current symbol */
+ while (context->bitcount < context->codesize) {
+
+ if (bytes < 1)
+ return ptr - buf;;
+
+ /* Read next byte */
+ c = *ptr++; bytes--;
+
+ /* New bits are shifted in from from the right. */
+ context->bitbuffer = (context->bitbuffer << 8) | c;
+ context->bitcount += 8;
+
+ }
+
+ /* Extract current symbol from bit buffer. */
+ c = (context->bitbuffer >> (context->bitcount -
+ context->codesize))
+ & context->codemask;
+
+ /* Adjust buffer */
+ context->bitcount -= context->codesize;
+
+ /* If c is less than clear, it's a data byte. Otherwise,
+ it's either clear/end or a code symbol which should be
+ expanded. */
+
+ if (c == context->clear) {
+ if (state->state != 2)
+ state->state = 1;
+ continue;
+ }
+
+ if (c == context->end)
+ break;
+
+ i = 1;
+ p = &context->lastdata;
+
+ if (state->state == 2) {
+
+ /* First valid symbol after clear; use as is */
+ if (c > context->clear) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->lastdata = context->lastcode = c;
+ state->state = 3;
+
+ } else {
+
+ thiscode = c;
+
+ if (c > context->next) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ if (c == context->next) {
+
+ /* c == next is allowed, by some strange reason */
+ if (context->bufferindex <= 0) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->buffer[--context->bufferindex] = context->lastdata;
+ c = context->lastcode;
+ }
+
+ while (c >= context->clear) {
+
+ /* Copy data string to buffer (beginning from right) */
+
+ if (context->bufferindex <= 0 || c >= LZWTABLE) {
+ state->errcode = IMAGING_CODEC_BROKEN;
+ return -1;
+ }
+
+ context->buffer[--context->bufferindex] =
+ context->data[c];
+ c = context->link[c];
+ }
+
+ context->lastdata = c;
+
+ if (context->next < LZWTABLE) {
+
+ /* While we still have room for it, add this
+ symbol to the table. */
+ context->data[context->next] = c;
+ context->link[context->next] = context->lastcode;
+
+ context->next++;
+
+ if (context->next == context->codemask &&
+ context->codesize < LZWBITS) {
+
+ /* Expand code size */
+ context->codesize++;
+ context->codemask = (1 << context->codesize) - 1;
+
+ }
+ }
+ context->lastcode = thiscode;
+ }
+ }
+
+ /* Update the output image */
+ for (c = 0; c < i; c++) {
+
+ state->buffer[state->x] = p[c];
+
+ if (++state->x >= state->bytes) {
+
+ int x, bpp;
+
+ /* Apply filter */
+ switch (context->filter) {
+ case 2:
+ /* Horizontal differing ("prior") */
+ bpp = (state->bits + 7) / 8;
+ for (x = bpp; x < state->bytes; x++)
+ state->buffer[x] += state->buffer[x-bpp];
+ }
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize)
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/Matrix.c b/libImaging/Matrix.c
new file mode 100644
index 000000000..5d2f031cf
--- /dev/null
+++ b/libImaging/Matrix.c
@@ -0,0 +1,74 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * colour and luminance matrix transforms
+ *
+ * history:
+ * 1996-05-18 fl: created (brute force implementation)
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8) v)
+
+
+Imaging
+ImagingConvertMatrix(Imaging im, const char *mode, float m[])
+{
+ Imaging imOut;
+ int x, y;
+
+ /* Assume there's enough data in the buffer */
+ if (!im)
+ return (Imaging) ImagingError_ModeError();
+
+ if (strcmp(mode, "L") == 0 && im->bands == 3) {
+
+ imOut = ImagingNew("L", im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ for (y = 0; y < im->ysize; y++) {
+ UINT8* in = (UINT8*) im->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+
+ for (x = 0; x < im->xsize; x++) {
+ float v = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5;
+ out[x] = CLIPF(v);
+ in += 4;
+ }
+ }
+
+ } else if (strlen(mode) == 3 && im->bands == 3) {
+
+ imOut = ImagingNew(mode, im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ for (y = 0; y < im->ysize; y++) {
+ UINT8* in = (UINT8*) im->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+
+ for (x = 0; x < im->xsize; x++) {
+ float v0 = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5;
+ float v1 = m[4]*in[0] + m[5]*in[1] + m[6]*in[2] + m[7] + 0.5;
+ float v2 = m[8]*in[0] + m[9]*in[1] + m[10]*in[2] + m[11] + 0.5;
+ out[0] = CLIPF(v0);
+ out[1] = CLIPF(v1);
+ out[2] = CLIPF(v2);
+ in += 4; out += 4;
+ }
+ }
+ } else
+ return (Imaging) ImagingError_ModeError();
+
+ return imOut;
+}
diff --git a/libImaging/ModeFilter.c b/libImaging/ModeFilter.c
new file mode 100644
index 000000000..ad11d87d4
--- /dev/null
+++ b/libImaging/ModeFilter.c
@@ -0,0 +1,78 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * mode filter
+ *
+ * history:
+ * 2002-06-08 fl Created (based on code from IFUNC95)
+ * 2004-10-05 fl Rewritten; use a simpler brute-force algorithm
+ *
+ * Copyright (c) Secret Labs AB 2002-2004. All rights reserved.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+Imaging
+ImagingModeFilter(Imaging im, int size)
+{
+ Imaging imOut;
+ int x, y, i;
+ int xx, yy;
+ int maxcount;
+ UINT8 maxpixel;
+ int histogram[256];
+
+ if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8)
+ return (Imaging) ImagingError_ModeError();
+
+ imOut = ImagingNew(im->mode, im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ size = size / 2;
+
+ for (y = 0; y < imOut->ysize; y++) {
+ UINT8* out = &IMAGING_PIXEL_L(imOut, 0, y);
+ for (x = 0; x < imOut->xsize; x++) {
+
+ /* calculate histogram over current area */
+
+ /* FIXME: brute force! to improve, update the histogram
+ incrementally. may also add a "frequent list", like in
+ the old implementation, but I'm not sure that's worth
+ the added complexity... */
+
+ memset(histogram, 0, sizeof(histogram));
+ for (yy = y - size; yy <= y + size; yy++)
+ if (yy >= 0 && yy < imOut->ysize) {
+ UINT8* in = &IMAGING_PIXEL_L(im, 0, yy);
+ for (xx = x - size; xx <= x + size; xx++)
+ if (xx >= 0 && xx < imOut->xsize)
+ histogram[in[xx]]++;
+ }
+
+ /* find most frequent pixel value in this region */
+ maxpixel = 0;
+ maxcount = histogram[maxpixel];
+ for (i = 1; i < 256; i++)
+ if (histogram[i] > maxcount) {
+ maxcount = histogram[i];
+ maxpixel = (UINT8) i;
+ }
+
+ if (maxcount > 2)
+ out[x] = maxpixel;
+ else
+ out[x] = IMAGING_PIXEL_L(im, x, y);
+
+ }
+
+ }
+
+ ImagingCopyInfo(imOut, im);
+
+ return imOut;
+}
diff --git a/libImaging/MspDecode.c b/libImaging/MspDecode.c
new file mode 100644
index 000000000..b611098d8
--- /dev/null
+++ b/libImaging/MspDecode.c
@@ -0,0 +1,91 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for MSP version 2 data.
+ *
+ * history:
+ * 97-01-03 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1997.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingMspDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ int n;
+ UINT8* ptr;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (bytes < 1)
+ return ptr - buf;
+
+ if (ptr[0] == 0) {
+
+ /* Run (3 bytes block) */
+ if (bytes < 3)
+ break;
+
+ n = ptr[1];
+
+ if (state->x + n > state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ memset(state->buffer + state->x, ptr[2], n);
+
+ ptr += 3;
+ bytes -= 3;
+
+ } else {
+
+ /* Literal (1+n bytes block) */
+ n = ptr[0];
+
+ if (bytes < 1 + n)
+ break;
+
+ if (state->x + n > state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ memcpy(state->buffer + state->x, ptr + 1, n);
+
+ ptr += 1 + n;
+ bytes -= 1 + n;
+
+ }
+
+ state->x += n;
+
+ if (state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/Negative.c b/libImaging/Negative.c
new file mode 100644
index 000000000..eaa790768
--- /dev/null
+++ b/libImaging/Negative.c
@@ -0,0 +1,42 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * negate image
+ *
+ * to do:
+ * FIXME: Maybe this should be implemented using ImagingPoint()
+ *
+ * history:
+ * 95-11-27 fl: Created
+ *
+ * Copyright (c) Fredrik Lundh 1995.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+Imaging
+ImagingNegative(Imaging im)
+{
+ Imaging imOut;
+ int x, y;
+
+ if (!im)
+ return (Imaging) ImagingError_ModeError();
+
+ imOut = ImagingNew(im->mode, im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ for (y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->linesize; x++)
+ imOut->image[y][x] = ~im->image[y][x];
+
+ return imOut;
+}
+
diff --git a/libImaging/Offset.c b/libImaging/Offset.c
new file mode 100644
index 000000000..f6d6e510e
--- /dev/null
+++ b/libImaging/Offset.c
@@ -0,0 +1,61 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * offset an image in x and y directions
+ *
+ * history:
+ * 96-07-22 fl: Created
+ * 98-11-01 cgw@pgt.com: Fixed negative-array index bug
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+Imaging
+ImagingOffset(Imaging im, int xoffset, int yoffset)
+{
+ int x, y;
+ Imaging imOut;
+
+ if (!im)
+ return (Imaging) ImagingError_ModeError();
+
+ imOut = ImagingNew(im->mode, im->xsize, im->ysize);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, im);
+
+ /* make offsets positive to avoid negative coordinates */
+ xoffset %= im->xsize;
+ xoffset = im->xsize - xoffset;
+ if (xoffset < 0)
+ xoffset += im->xsize;
+
+ yoffset %= im->ysize;
+ yoffset = im->ysize - yoffset;
+ if (yoffset < 0)
+ yoffset += im->ysize;
+
+#define OFFSET(image)\
+ for (y = 0; y < im->ysize; y++)\
+ for (x = 0; x < im->xsize; x++) {\
+ int yi = (y + yoffset) % im->ysize;\
+ int xi = (x + xoffset) % im->xsize;\
+ imOut->image[y][x] = im->image[yi][xi];\
+ }
+
+ if (im->image8)
+ OFFSET(image8)
+ else
+ OFFSET(image32)
+
+ return imOut;
+}
diff --git a/libImaging/Pack.c b/libImaging/Pack.c
new file mode 100644
index 000000000..478de7499
--- /dev/null
+++ b/libImaging/Pack.c
@@ -0,0 +1,566 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * code to pack raw data
+ *
+ * history:
+ * 1996-04-30 fl Created
+ * 1996-05-12 fl Published a few RGB packers
+ * 1996-11-01 fl More RGB packers (Tk booster stuff)
+ * 1996-12-30 fl Added P;1, P;2 and P;4 packers
+ * 1997-06-02 fl Added F (F;32NF) packer
+ * 1997-08-28 fl Added 1 as L packer
+ * 1998-02-08 fl Added I packer
+ * 1998-03-09 fl Added mode field, RGBA/RGBX as RGB packers
+ * 1998-07-01 fl Added YCbCr support
+ * 1998-07-12 fl Added I 16 packer
+ * 1999-02-03 fl Added BGR packers
+ * 2003-09-26 fl Added LA/PA packers
+ * 2006-06-22 fl Added CMYK;I packer
+ *
+ * Copyright (c) 1997-2006 by Secret Labs AB.
+ * Copyright (c) 1996-1997 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define R 0
+#define G 1
+#define B 2
+#define X 3
+#define A 3
+
+#define C 0
+#define M 1
+#define Y 2
+#define K 3
+
+/* byte swapping macros */
+
+#define C16N\
+ (out[0]=tmp[0], out[1]=tmp[1]);
+#define C16S\
+ (out[1]=tmp[0], out[0]=tmp[1]);
+#define C32N\
+ (out[0]=tmp[0], out[1]=tmp[1], out[2]=tmp[2], out[3]=tmp[3]);
+#define C32S\
+ (out[3]=tmp[0], out[2]=tmp[1], out[1]=tmp[2], out[0]=tmp[3]);
+#define C64N\
+ (out[0]=tmp[0], out[1]=tmp[1], out[2]=tmp[2], out[3]=tmp[3],\
+ out[4]=tmp[4], out[5]=tmp[5], out[6]=tmp[6], out[7]=tmp[7]);
+#define C64S\
+ (out[7]=tmp[0], out[6]=tmp[1], out[5]=tmp[2], out[4]=tmp[3],\
+ out[3]=tmp[4], out[2]=tmp[5], out[1]=tmp[6], out[0]=tmp[7]);
+
+#ifdef WORDS_BIGENDIAN
+#define C16B C16N
+#define C16L C16S
+#define C32B C32N
+#define C32L C32S
+#define C64B C64N
+#define C64L C64S
+#else
+#define C16B C16S
+#define C16L C16N
+#define C32B C32S
+#define C32L C32N
+#define C64B C64S
+#define C64L C64N
+#endif
+
+static void
+pack1(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, m, b;
+ /* bilevel (black is 0) */
+ b = 0; m = 128;
+ for (i = 0; i < pixels; i++) {
+ if (in[i] != 0)
+ b |= m;
+ m >>= 1;
+ if (m == 0) {
+ *out++ = b;
+ b = 0; m = 128;
+ }
+ }
+ if (m != 128)
+ *out++ = b;
+}
+
+static void
+pack1I(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, m, b;
+ /* bilevel (black is 1) */
+ b = 0; m = 128;
+ for (i = 0; i < pixels; i++) {
+ if (in[i] == 0)
+ b |= m;
+ m >>= 1;
+ if (m == 0) {
+ *out++ = b;
+ b = 0; m = 128;
+ }
+ }
+ if (m != 128)
+ *out++ = b;
+}
+
+static void
+pack1R(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, m, b;
+ /* bilevel, lsb first (black is 0) */
+ b = 0; m = 1;
+ for (i = 0; i < pixels; i++) {
+ if (in[i] != 0)
+ b |= m;
+ m <<= 1;
+ if (m == 256){
+ *out++ = b;
+ b = 0; m = 1;
+ }
+ }
+ if (m != 1)
+ *out++ = b;
+}
+
+static void
+pack1IR(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, m, b;
+ /* bilevel, lsb first (black is 1) */
+ b = 0; m = 1;
+ for (i = 0; i < pixels; i++) {
+ if (in[i] == 0)
+ b |= m;
+ m <<= 1;
+ if (m == 256){
+ *out++ = b;
+ b = 0; m = 1;
+ }
+ }
+ if (m != 1)
+ *out++ = b;
+}
+
+static void
+pack1L(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* bilevel, stored as bytes */
+ for (i = 0; i < pixels; i++)
+ out[i] = (in[i] != 0) ? 255 : 0;
+}
+
+static void
+packP4(UINT8* out, const UINT8* in, int pixels)
+{
+ while (pixels >= 2) {
+ *out++ = (in[0] << 4) |
+ (in[1] & 15);
+ in += 2; pixels -= 2;
+ }
+
+ if (pixels)
+ out[0] = (in[0] << 4);
+}
+
+static void
+packP2(UINT8* out, const UINT8* in, int pixels)
+{
+ while (pixels >= 4) {
+ *out++ = (in[0] << 6) |
+ ((in[1] & 3) << 4) |
+ ((in[2] & 3) << 2) |
+ (in[3] & 3);
+ in += 4; pixels -= 4;
+ }
+
+ switch (pixels) {
+ case 3:
+ out[0] = (in[0] << 6) |
+ ((in[1] & 3) << 4) |
+ ((in[2] & 3) << 2);
+ break;
+ case 2:
+ out[0] = (in[0] << 6) |
+ ((in[1] & 3) << 4);
+ case 1:
+ out[0] = (in[0] << 6);
+ }
+}
+
+static void
+packLA(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* LA, pixel interleaved */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[R];
+ out[1] = in[A];
+ out += 2; in += 4;
+ }
+}
+
+static void
+packLAL(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* LA, line interleaved */
+ for (i = 0; i < pixels; i++) {
+ out[i] = in[R];
+ out[i+pixels] = in[A];
+ in += 4;
+ }
+}
+
+void
+ImagingPackRGB(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* RGB triplets */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[R];
+ out[1] = in[G];
+ out[2] = in[B];
+ out += 3; in += 4;
+ }
+}
+
+void
+ImagingPackXRGB(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* XRGB, triplets with left padding */
+ for (i = 0; i < pixels; i++) {
+ out[0] = 0;
+ out[1] = in[R];
+ out[2] = in[G];
+ out[3] = in[B];
+ out += 4; in += 4;
+ }
+}
+
+void
+ImagingPackBGR(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* RGB, reversed bytes */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[B];
+ out[1] = in[G];
+ out[2] = in[R];
+ out += 3; in += 4;
+ }
+}
+
+void
+ImagingPackBGRX(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* BGRX, reversed bytes with right padding */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[B];
+ out[1] = in[G];
+ out[2] = in[R];
+ out[3] = 0;
+ out += 4; in += 4;
+ }
+}
+
+void
+ImagingPackXBGR(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* XBGR, reversed bytes with left padding */
+ for (i = 0; i < pixels; i++) {
+ out[0] = 0;
+ out[1] = in[B];
+ out[2] = in[G];
+ out[3] = in[R];
+ out += 4; in += 4;
+ }
+}
+
+void
+ImagingPackBGRA(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* BGRX, reversed bytes with right padding */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[B];
+ out[1] = in[G];
+ out[2] = in[R];
+ out[3] = in[A];
+ out += 4; in += 4;
+ }
+}
+
+void
+ImagingPackABGR(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* XBGR, reversed bytes with left padding */
+ for (i = 0; i < pixels; i++) {
+ out[0] = in[A];
+ out[1] = in[B];
+ out[2] = in[G];
+ out[3] = in[R];
+ out += 4; in += 4;
+ }
+}
+
+static void
+packRGBL(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* RGB, line interleaved */
+ for (i = 0; i < pixels; i++) {
+ out[i] = in[R];
+ out[i+pixels] = in[G];
+ out[i+pixels+pixels] = in[B];
+ in += 4;
+ }
+}
+
+static void
+packRGBXL(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* RGBX, line interleaved */
+ for (i = 0; i < pixels; i++) {
+ out[i] = in[R];
+ out[i+pixels] = in[G];
+ out[i+pixels+pixels] = in[B];
+ out[i+pixels+pixels+pixels] = in[X];
+ in += 4;
+ }
+}
+
+static void
+packI16B(UINT8* out, const UINT8* in_, int pixels)
+{
+ int i;
+ INT32* in = (INT32*) in_;
+ UINT16 tmp_;
+ UINT8* tmp = (UINT8*) &tmp_;
+ for (i = 0; i < pixels; i++) {
+ if (in[0] <= 0)
+ tmp_ = 0;
+ else if (in[0] > 65535)
+ tmp_ = 65535;
+ else
+ tmp_ = in[0];
+ C16B;
+ out += 2; in++;
+ }
+}
+
+static void
+packI32S(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ UINT8* tmp = (UINT8*) in;
+ for (i = 0; i < pixels; i++) {
+ C32L;
+ out += 4; tmp += 4;
+ }
+}
+
+static void
+copy1(UINT8* out, const UINT8* in, int pixels)
+{
+ /* L, P */
+ memcpy(out, in, pixels);
+}
+
+static void
+copy2(UINT8* out, const UINT8* in, int pixels)
+{
+ /* I;16, etc */
+ memcpy(out, in, pixels*2);
+}
+
+static void
+copy3(UINT8* out, const UINT8* in, int pixels)
+{
+ /* BGR;24, etc */
+ memcpy(out, in, pixels*3);
+}
+
+static void
+copy4(UINT8* out, const UINT8* in, int pixels)
+{
+ /* RGBA, CMYK quadruples */
+ memcpy(out, in, 4*pixels);
+}
+
+static void
+copy4I(UINT8* out, const UINT8* in, int pixels)
+{
+ /* RGBA, CMYK quadruples, inverted */
+ int i;
+ for (i = 0; i < pixels*4; i++)
+ out[i] = ~in[i];
+}
+
+static void
+band0(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ for (i = 0; i < pixels; i++, in += 4)
+ out[i] = in[0];
+}
+
+static void
+band1(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ for (i = 0; i < pixels; i++, in += 4)
+ out[i] = in[1];
+}
+
+static void
+band2(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ for (i = 0; i < pixels; i++, in += 4)
+ out[i] = in[2];
+}
+
+static void
+band3(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ for (i = 0; i < pixels; i++, in += 4)
+ out[i] = in[3];
+}
+
+static struct {
+ const char* mode;
+ const char* rawmode;
+ int bits;
+ ImagingShuffler pack;
+} packers[] = {
+
+ /* bilevel */
+ {"1", "1", 1, pack1},
+ {"1", "1;I", 1, pack1I},
+ {"1", "1;R", 1, pack1R},
+ {"1", "1;IR", 1, pack1IR},
+ {"1", "L", 8, pack1L},
+
+ /* greyscale */
+ {"L", "L", 8, copy1},
+
+ /* greyscale w. alpha */
+ {"LA", "LA", 16, packLA},
+ {"LA", "LA;L", 16, packLAL},
+
+ /* palette */
+ {"P", "P;1", 1, pack1},
+ {"P", "P;2", 2, packP2},
+ {"P", "P;4", 4, packP4},
+ {"P", "P", 8, copy1},
+
+ /* palette w. alpha */
+ {"PA", "PA", 16, packLA},
+ {"PA", "PA;L", 16, packLAL},
+
+ /* true colour */
+ {"RGB", "RGB", 24, ImagingPackRGB},
+ {"RGB", "RGBX", 32, copy4},
+ {"RGB", "XRGB", 32, ImagingPackXRGB},
+ {"RGB", "BGR", 24, ImagingPackBGR},
+ {"RGB", "BGRX", 32, ImagingPackBGRX},
+ {"RGB", "XBGR", 32, ImagingPackXBGR},
+ {"RGB", "RGB;L", 24, packRGBL},
+ {"RGB", "R", 8, band0},
+ {"RGB", "G", 8, band1},
+ {"RGB", "B", 8, band2},
+
+ /* true colour w. alpha */
+ {"RGBA", "RGBA", 32, copy4},
+ {"RGBA", "RGBA;L", 32, packRGBXL},
+ {"RGBA", "RGB", 24, ImagingPackRGB},
+ {"RGBA", "BGR", 24, ImagingPackBGR},
+ {"RGBA", "BGRA", 32, ImagingPackBGRA},
+ {"RGBA", "ABGR", 32, ImagingPackABGR},
+ {"RGBA", "R", 8, band0},
+ {"RGBA", "G", 8, band1},
+ {"RGBA", "B", 8, band2},
+ {"RGBA", "A", 8, band3},
+
+ /* true colour w. padding */
+ {"RGBX", "RGBX", 32, copy4},
+ {"RGBX", "RGBX;L", 32, packRGBXL},
+ {"RGBX", "RGB", 32, ImagingPackRGB},
+ {"RGBX", "BGR", 32, ImagingPackBGR},
+ {"RGBX", "BGRX", 32, ImagingPackBGRX},
+ {"RGBX", "XBGR", 32, ImagingPackXBGR},
+ {"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, copy4I},
+ {"CMYK", "CMYK;L", 32, packRGBXL},
+ {"CMYK", "C", 8, band0},
+ {"CMYK", "M", 8, band1},
+ {"CMYK", "Y", 8, band2},
+ {"CMYK", "K", 8, band3},
+
+ /* video (YCbCr) */
+ {"YCbCr", "YCbCr", 24, ImagingPackRGB},
+ {"YCbCr", "YCbCr;L", 24, packRGBL},
+ {"YCbCr", "YCbCrX", 32, copy4},
+ {"YCbCr", "YCbCrK", 32, copy4},
+ {"YCbCr", "Y", 8, band0},
+ {"YCbCr", "Cb", 8, band1},
+ {"YCbCr", "Cr", 8, band2},
+
+ /* integer */
+ {"I", "I", 32, copy4},
+ {"I", "I;16B", 16, packI16B},
+ {"I", "I;32S", 32, packI32S},
+ {"I", "I;32NS", 32, copy4},
+
+ /* floating point */
+ {"F", "F", 32, copy4},
+ {"F", "F;32F", 32, packI32S},
+ {"F", "F;32NF", 32, copy4},
+
+ /* storage modes */
+ {"I;16", "I;16", 16, copy2},
+ {"I;16B", "I;16B", 16, copy2},
+ {"I;16L", "I;16L", 16, copy2},
+ {"BGR;15", "BGR;15", 16, copy2},
+ {"BGR;16", "BGR;16", 16, copy2},
+ {"BGR;24", "BGR;24", 24, copy3},
+
+ {NULL} /* sentinel */
+};
+
+
+ImagingShuffler
+ImagingFindPacker(const char* mode, const char* rawmode, int* bits_out)
+{
+ int i;
+
+ /* find a suitable pixel packer */
+ for (i = 0; packers[i].rawmode; i++)
+ if (strcmp(packers[i].mode, mode) == 0 &&
+ strcmp(packers[i].rawmode, rawmode) == 0) {
+ if (bits_out)
+ *bits_out = packers[i].bits;
+ return packers[i].pack;
+ }
+ return NULL;
+}
diff --git a/libImaging/PackDecode.c b/libImaging/PackDecode.c
new file mode 100644
index 000000000..aea8f04e3
--- /dev/null
+++ b/libImaging/PackDecode.c
@@ -0,0 +1,92 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for PackBits image data.
+ *
+ * history:
+ * 96-04-19 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+int
+ImagingPackbitsDecode(Imaging im, ImagingCodecState state,
+ UINT8* buf, int bytes)
+{
+ UINT8 n;
+ UINT8* ptr;
+ int i;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (bytes < 1)
+ return ptr - buf;
+
+ if (ptr[0] & 0x80) {
+
+ if (ptr[0] == 0x80) {
+ /* Nop */
+ ptr++; bytes--;
+ continue;
+ }
+
+ /* Run */
+ if (bytes < 2)
+ return ptr - buf;
+
+ for (n = 257 - ptr[0]; n > 0; n--) {
+ if (state->x >= state->bytes) {
+ /* state->errcode = IMAGING_CODEC_OVERRUN; */
+ break;
+ }
+ state->buffer[state->x++] = ptr[1];
+ }
+
+ ptr += 2; bytes -= 2;
+
+ } else {
+
+ /* Literal */
+ n = ptr[0]+2;
+
+ if (bytes < n)
+ return ptr - buf;
+
+ for (i = 1; i < n; i++) {
+ if (state->x >= state->bytes) {
+ /* state->errcode = IMAGING_CODEC_OVERRUN; */
+ break;
+ }
+ state->buffer[state->x++] = ptr[i];
+ }
+
+ ptr += n; bytes -= n;
+
+ }
+
+ if (state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ }
+}
diff --git a/libImaging/Palette.c b/libImaging/Palette.c
new file mode 100644
index 000000000..bd4f4f1d6
--- /dev/null
+++ b/libImaging/Palette.c
@@ -0,0 +1,316 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * imaging palette object
+ *
+ * history:
+ * 1996-05-05 fl Added to library
+ * 1996-05-27 fl Added colour mapping stuff
+ * 1997-05-12 fl Support RGBA palettes
+ * 2005-02-09 fl Removed grayscale entries from web palette
+ *
+ * Copyright (c) Secret Labs AB 1997-2005. All rights reserved.
+ * Copyright (c) Fredrik Lundh 1995-1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include
+
+
+ImagingPalette
+ImagingPaletteNew(const char* mode)
+{
+ /* Create a palette object */
+
+ int i;
+ ImagingPalette palette;
+
+ if (strcmp(mode, "RGB") && strcmp(mode, "RGBA"))
+ return (ImagingPalette) ImagingError_ModeError();
+
+ palette = calloc(1, sizeof(struct ImagingPaletteInstance));
+ if (!palette)
+ return (ImagingPalette) ImagingError_MemoryError();
+
+ strcpy(palette->mode, mode);
+
+ /* Initialize to ramp */
+ for (i = 0; i < 256; i++) {
+ palette->palette[i*4+0] =
+ palette->palette[i*4+1] =
+ palette->palette[i*4+2] = (UINT8) i;
+ palette->palette[i*4+3] = 255; /* opaque */
+ }
+
+ return palette;
+}
+
+ImagingPalette
+ImagingPaletteNewBrowser(void)
+{
+ /* Create a standard "browser" palette object */
+
+ int i, r, g, b;
+ ImagingPalette palette;
+
+ palette = ImagingPaletteNew("RGB");
+ if (!palette)
+ return NULL;
+
+ /* Blank out unused entries */
+ /* FIXME: Add 10-level windows palette here? */
+
+ for (i = 0; i < 10; i++) {
+ palette->palette[i*4+0] =
+ palette->palette[i*4+1] =
+ palette->palette[i*4+2] = 0;
+ }
+
+ /* Simple 6x6x6 colour cube */
+
+ for (b = 0; b < 256; b += 51)
+ for (g = 0; g < 256; g += 51)
+ for (r = 0; r < 256; r += 51) {
+ palette->palette[i*4+0] = r;
+ palette->palette[i*4+1] = g;
+ palette->palette[i*4+2] = b;
+ i++;
+ }
+
+ /* Blank out unused entries */
+ /* FIXME: add 30-level greyscale wedge here? */
+
+ for (; i < 256; i++) {
+ palette->palette[i*4+0] =
+ palette->palette[i*4+1] =
+ palette->palette[i*4+2] = 0;
+ }
+
+ return palette;
+}
+
+ImagingPalette
+ImagingPaletteDuplicate(ImagingPalette palette)
+{
+ /* Duplicate palette descriptor */
+
+ ImagingPalette new_palette;
+
+ if (!palette)
+ return NULL;
+
+ new_palette = malloc(sizeof(struct ImagingPaletteInstance));
+ if (!new_palette)
+ return (ImagingPalette) ImagingError_MemoryError();
+
+ memcpy(new_palette, palette, sizeof(struct ImagingPaletteInstance));
+
+ /* Don't share the cache */
+ new_palette->cache = NULL;
+
+ return new_palette;
+}
+
+void
+ImagingPaletteDelete(ImagingPalette palette)
+{
+ /* Destroy palette object */
+
+ if (palette) {
+ if (palette->cache)
+ free(palette->cache);
+ free(palette);
+ }
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Colour mapping */
+/* -------------------------------------------------------------------- */
+
+/* This code is used to map RGB triplets to palette indices, using
+ a palette index cache. */
+
+/*
+ * This implementation is loosely based on the corresponding code in
+ * the IJG JPEG library by Thomas G. Lane. Original algorithms by
+ * Paul Heckbert and Spencer W. Thomas.
+ *
+ * The IJG JPEG library is copyright (C) 1991-1995, Thomas G. Lane. */
+
+#define DIST(a, b, s) (a - b) * (a - b) * s
+
+/* Colour weights (no scaling, for now) */
+#define RSCALE 1
+#define GSCALE 1
+#define BSCALE 1
+
+/* Calculated scaled distances */
+#define RDIST(a, b) DIST(a, b, RSCALE*RSCALE)
+#define GDIST(a, b) DIST(a, b, GSCALE*GSCALE)
+#define BDIST(a, b) DIST(a, b, BSCALE*BSCALE)
+
+/* Incremental steps */
+#define RSTEP (4 * RSCALE)
+#define GSTEP (4 * GSCALE)
+#define BSTEP (4 * BSCALE)
+
+#define BOX 8
+
+#define BOXVOLUME BOX*BOX*BOX
+
+void
+ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b)
+{
+ int i, j;
+ unsigned int dmin[256], dmax;
+ int r0, g0, b0;
+ int r1, g1, b1;
+ int rc, gc, bc;
+ unsigned int d[BOXVOLUME];
+ UINT8 c[BOXVOLUME];
+
+ /* Get box boundaries for the given (r,g,b)-triplet. Each box
+ covers eight cache slots (32 colour values, that is). */
+
+ r0 = r & 0xe0; r1 = r0 + 0x1f; rc = (r0 + r1) / 2;
+ g0 = g & 0xe0; g1 = g0 + 0x1f; gc = (g0 + g1) / 2;
+ b0 = b & 0xe0; b1 = b0 + 0x1f; bc = (b0 + b1) / 2;
+
+ /* Step 1 -- Select relevant palette entries (after Heckbert) */
+
+ /* For each palette entry, calculate the min and max distances to
+ * any position in the box given by the colour we're looking for. */
+
+ dmax = (unsigned int) ~0;
+
+ for (i = 0; i < 256; i++) {
+
+ int r, g, b;
+ unsigned int tmin, tmax;
+
+ /* Find min and max distances to any point in the box */
+ r = palette->palette[i*4+0];
+ tmin = (r < r0) ? RDIST(r, r1) : (r > r1) ? RDIST(r, r0) : 0;
+ tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0);
+
+ g = palette->palette[i*4+1];
+ tmin += (g < g0) ? GDIST(g, g1) : (g > g1) ? GDIST(g, g0) : 0;
+ tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0);
+
+ b = palette->palette[i*4+2];
+ tmin += (b < b0) ? BDIST(b, b1) : (b > b1) ? BDIST(b, b0) : 0;
+ tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0);
+
+ dmin[i] = tmin;
+ if (tmax < dmax)
+ dmax = tmax; /* keep the smallest max distance only */
+
+ }
+
+ /* Step 2 -- Incrementally update cache slot (after Thomas) */
+
+ /* Find the box containing the nearest palette entry, and update
+ * all slots in that box. We only check boxes for which the min
+ * distance is less than or equal the smallest max distance */
+
+ for (i = 0; i < BOXVOLUME; i++)
+ d[i] = (unsigned int) ~0;
+
+ for (i = 0; i < 256; i++)
+
+ if (dmin[i] <= dmax) {
+
+ int rd, gd, bd;
+ int ri, gi, bi;
+ int rx, gx, bx;
+
+ ri = (r0 - palette->palette[i*4+0]) * RSCALE;
+ gi = (g0 - palette->palette[i*4+1]) * GSCALE;
+ bi = (b0 - palette->palette[i*4+2]) * BSCALE;
+
+ rd = ri*ri + gi*gi + bi*bi;
+
+ ri = ri * (2 * RSTEP) + RSTEP * RSTEP;
+ gi = gi * (2 * GSTEP) + GSTEP * GSTEP;
+ bi = bi * (2 * BSTEP) + BSTEP * BSTEP;
+
+ rx = ri;
+ for (r = j = 0; r < BOX; r++) {
+ gd = rd; gx = gi;
+ for (g = 0; g < BOX; g++) {
+ bd = gd; bx = bi;
+ for (b = 0; b < BOX; b++) {
+ if ((unsigned int) bd < d[j]) {
+ d[j] = bd;
+ c[j] = (UINT8) i;
+ }
+ bd += bx;
+ bx += 2 * BSTEP * BSTEP;
+ j++;
+ }
+ gd += gx;
+ gx += 2 * GSTEP * GSTEP;
+ }
+ rd += rx;
+ rx += 2 * RSTEP * RSTEP;
+ }
+ }
+
+ /* Step 3 -- Update cache */
+
+ /* The c array now contains the closest match for each
+ * cache slot in the box. Update the cache. */
+
+ j = 0;
+ for (r = r0; r < r1; r+=4)
+ for (g = g0; g < g1; g+=4)
+ for (b = b0; b < b1; b+=4)
+ ImagingPaletteCache(palette, r, g, b) = c[j++];
+}
+
+
+int
+ImagingPaletteCachePrepare(ImagingPalette palette)
+{
+ /* Add a colour cache to a palette */
+
+ int i;
+ int entries = 64*64*64;
+
+ if (palette->cache == NULL) {
+
+ /* The cache is 512k. It might be a good idea to break it
+ up into a pointer array (e.g. an 8-bit image?) */
+
+ palette->cache = (INT16*) malloc(entries * sizeof(INT16));
+ if (!palette->cache) {
+ (void) ImagingError_MemoryError();
+ return -1;
+ }
+
+ /* Mark all entries as empty */
+ for (i = 0; i < entries; i++)
+ palette->cache[i] = 0x100;
+
+ }
+
+ return 0;
+}
+
+
+void
+ImagingPaletteCacheDelete(ImagingPalette palette)
+{
+ /* Release the colour cache, if any */
+
+ if (palette && palette->cache) {
+ free(palette->cache);
+ palette->cache = NULL;
+ }
+}
diff --git a/libImaging/Paste.c b/libImaging/Paste.c
new file mode 100644
index 000000000..b89dd6404
--- /dev/null
+++ b/libImaging/Paste.c
@@ -0,0 +1,555 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * paste image on another image
+ *
+ * history:
+ * 96-03-27 fl Created
+ * 96-07-16 fl Support "1", "L" and "RGBA" masks
+ * 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
+ * 99-02-02 fl Added "RGBa" mask support
+ * 99-02-06 fl Rewritten. Added support for masked fill operations.
+ * 99-12-08 fl Fixed matte fill.
+ *
+ * Copyright (c) Fredrik Lundh 1996-97.
+ * Copyright (c) Secret Labs AB 1997-99.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+/* like (a * b + 127) / 255), but much faster on most platforms */
+#define MULDIV255NEW(a, b, tmp)\
+ (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
+
+#define MULDIV255OLD(a, b, tmp)\
+ (((a) * (b) + 127) / 255)
+
+#define MULDIV255 MULDIV255NEW
+
+#define BLEND(mask, in1, in2, tmp1, tmp2)\
+ (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2))
+
+#define PREBLEND(mask, in1, in2, tmp1)\
+ (MULDIV255(in1, 255 - mask, tmp1) + in2)
+
+static inline void
+paste(Imaging imOut, Imaging imIn, int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* paste opaque region */
+
+ int y;
+
+ dx *= pixelsize;
+ sx *= pixelsize;
+
+ xsize *= pixelsize;
+
+ for (y = 0; y < ysize; y++)
+ memcpy(imOut->image[y+dy]+dx, imIn->image[y+sy]+sx, xsize);
+}
+
+static inline void
+paste_mask_1(Imaging imOut, Imaging imIn, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* paste with mode "1" mask */
+
+ int x, y;
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* in = imIn->image8[y+sy]+sx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ if (*mask++)
+ *out = *in;
+ out++, in++;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ INT32* out = imOut->image32[y+dy]+dx;
+ INT32* in = imIn->image32[y+sy]+sx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ if (*mask++)
+ *out = *in;
+ out++, in++;
+ }
+ }
+ }
+}
+
+static inline void
+paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* paste with mode "L" matte */
+
+ int x, y, i;
+ unsigned int tmp1, tmp2;
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* in = imIn->image8[y+sy]+sx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ *out = BLEND(*mask, *out, *in, tmp1, tmp2);
+ out++, in++, mask++;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize;
+ UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = BLEND(*mask, *out, *in, tmp1, tmp2);
+ out++, in++;
+ }
+ mask++;
+ }
+ }
+ }
+}
+
+static inline void
+paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* paste with mode "RGBA" matte */
+
+ int x, y, i;
+ unsigned int tmp1, tmp2;
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* in = imIn->image8[y+sy]+sx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3;
+ for (x = 0; x < xsize; x++) {
+ *out = BLEND(*mask, *out, *in, tmp1, tmp2);
+ out++, in++, mask += 4;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize;
+ UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = BLEND(*mask, *out, *in, tmp1, tmp2);
+ out++, in++;
+ }
+ mask += 4;
+ }
+ }
+ }
+}
+
+
+static inline void
+paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* paste with mode "RGBa" matte */
+
+ int x, y, i;
+ unsigned int tmp1;
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* in = imIn->image8[y+sy]+sx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3;
+ for (x = 0; x < xsize; x++) {
+ *out = PREBLEND(*mask, *out, *in, tmp1);
+ out++, in++, mask += 4;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize;
+ UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = PREBLEND(*mask, *out, *in, tmp1);
+ out++, in++;
+ }
+ mask += 4;
+ }
+ }
+ }
+}
+
+int
+ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask,
+ int dx0, int dy0, int dx1, int dy1)
+{
+ int xsize, ysize;
+ int pixelsize;
+ int sx0, sy0;
+ ImagingSectionCookie cookie;
+
+ if (!imOut || !imIn) {
+ (void) ImagingError_ModeError();
+ return -1;
+ }
+
+ pixelsize = imOut->pixelsize;
+
+ xsize = dx1 - dx0;
+ ysize = dy1 - dy0;
+
+ if (xsize != imIn->xsize || ysize != imIn->ysize ||
+ pixelsize != imIn->pixelsize) {
+ (void) ImagingError_Mismatch();
+ return -1;
+ }
+
+ if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) {
+ (void) ImagingError_Mismatch();
+ return -1;
+ }
+
+ /* Determine which region to copy */
+ sx0 = sy0 = 0;
+ if (dx0 < 0)
+ xsize += dx0, sx0 = -dx0, dx0 = 0;
+ if (dx0 + xsize > imOut->xsize)
+ xsize = imOut->xsize - dx0;
+ if (dy0 < 0)
+ ysize += dy0, sy0 = -dy0, dy0 = 0;
+ if (dy0 + ysize > imOut->ysize)
+ ysize = imOut->ysize - dy0;
+
+ if (xsize <= 0 || ysize <= 0)
+ return 0;
+
+ if (!imMask) {
+ ImagingSectionEnter(&cookie);
+ paste(imOut, imIn, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "1") == 0) {
+ ImagingSectionEnter(&cookie);
+ paste_mask_1(imOut, imIn, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "L") == 0) {
+ ImagingSectionEnter(&cookie);
+ paste_mask_L(imOut, imIn, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "RGBA") == 0) {
+ ImagingSectionEnter(&cookie);
+ paste_mask_RGBA(imOut, imIn, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "RGBa") == 0) {
+ ImagingSectionEnter(&cookie);
+ paste_mask_RGBa(imOut, imIn, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else {
+ (void) ImagingError_ValueError("bad transparency mask");
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline void
+fill(Imaging imOut, const void* ink_, int dx, int dy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* fill opaque region */
+
+ int x, y;
+ UINT8 ink8 = 0;
+ INT32 ink32 = 0L;
+
+ memcpy(&ink32, ink_, pixelsize);
+ memcpy(&ink8, ink_, sizeof(ink8));
+
+ if (imOut->image8 || ink32 == 0L) {
+
+ dx *= pixelsize;
+ xsize *= pixelsize;
+ for (y = 0; y < ysize; y++)
+ memset(imOut->image[y+dy]+dx, ink8, xsize);
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ INT32* out = imOut->image32[y+dy]+dx;
+ for (x = 0; x < xsize; x++)
+ out[x] = ink32;
+ }
+
+ }
+}
+
+static inline void
+fill_mask_1(Imaging imOut, const void* ink_, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* fill with mode "1" mask */
+
+ int x, y;
+ UINT8 ink8 = 0;
+ INT32 ink32 = 0L;
+
+ memcpy(&ink32, ink_, pixelsize);
+ memcpy(&ink8, ink_, sizeof(ink8));
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ if (*mask++)
+ *out = ink8;
+ out++;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ INT32* out = imOut->image32[y+dy]+dx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ if (*mask++)
+ *out = ink32;
+ out++;
+ }
+ }
+ }
+}
+
+static inline void
+fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* fill with mode "L" matte */
+
+ int x, y, i;
+ unsigned int tmp1, tmp2;
+
+ if (imOut->image8) {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* mask = imMask->image8[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ *out = BLEND(*mask, *out, ink[0], tmp1, tmp2);
+ out++, mask++;
+ }
+ }
+
+ } else {
+
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = BLEND(*mask, *out, ink[i], tmp1, tmp2);
+ out++;
+ }
+ mask++;
+ }
+ }
+ }
+}
+
+static inline void
+fill_mask_RGBA(Imaging imOut, const UINT8* ink, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* fill with mode "RGBA" matte */
+
+ int x, y, i;
+ unsigned int tmp1, tmp2;
+
+ if (imOut->image8) {
+
+ sx = sx*4+3;
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ *out = BLEND(*mask, *out, ink[0], tmp1, tmp2);
+ out++, mask += 4;
+ }
+ }
+
+ } else {
+
+ dx *= pixelsize;
+ sx = sx*4 + 3;
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = BLEND(*mask, *out, ink[i], tmp1, tmp2);
+ out++;
+ }
+ mask += 4;
+ }
+ }
+ }
+}
+
+static inline void
+fill_mask_RGBa(Imaging imOut, const UINT8* ink, Imaging imMask,
+ int dx, int dy, int sx, int sy,
+ int xsize, int ysize, int pixelsize)
+{
+ /* fill with mode "RGBa" matte */
+
+ int x, y, i;
+ unsigned int tmp1;
+
+ if (imOut->image8) {
+
+ sx = sx*4 + 3;
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = imOut->image8[y+dy]+dx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ *out = PREBLEND(*mask, *out, ink[0], tmp1);
+ out++, mask += 4;
+ }
+ }
+
+ } else {
+
+ dx *= pixelsize;
+ sx = sx*4 + 3;
+ for (y = 0; y < ysize; y++) {
+ UINT8* out = (UINT8*) imOut->image[y+dy]+dx;
+ UINT8* mask = (UINT8*) imMask->image[y+sy]+sx;
+ for (x = 0; x < xsize; x++) {
+ for (i = 0; i < pixelsize; i++) {
+ *out = PREBLEND(*mask, *out, ink[i], tmp1);
+ out++;
+ }
+ mask += 4;
+ }
+ }
+ }
+}
+
+int
+ImagingFill2(Imaging imOut, const void* ink, Imaging imMask,
+ int dx0, int dy0, int dx1, int dy1)
+{
+ ImagingSectionCookie cookie;
+ int xsize, ysize;
+ int pixelsize;
+ int sx0, sy0;
+
+ if (!imOut || !ink) {
+ (void) ImagingError_ModeError();
+ return -1;
+ }
+
+ pixelsize = imOut->pixelsize;
+
+ xsize = dx1 - dx0;
+ ysize = dy1 - dy0;
+
+ if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) {
+ (void) ImagingError_Mismatch();
+ return -1;
+ }
+
+ /* Determine which region to fill */
+ sx0 = sy0 = 0;
+ if (dx0 < 0)
+ xsize += dx0, sx0 = -dx0, dx0 = 0;
+ if (dx0 + xsize > imOut->xsize)
+ xsize = imOut->xsize - dx0;
+ if (dy0 < 0)
+ ysize += dy0, sy0 = -dy0, dy0 = 0;
+ if (dy0 + ysize > imOut->ysize)
+ ysize = imOut->ysize - dy0;
+
+ if (xsize <= 0 || ysize <= 0)
+ return 0;
+
+ if (!imMask) {
+ ImagingSectionEnter(&cookie);
+ fill(imOut, ink, dx0, dy0, xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "1") == 0) {
+ ImagingSectionEnter(&cookie);
+ fill_mask_1(imOut, ink, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "L") == 0) {
+ ImagingSectionEnter(&cookie);
+ fill_mask_L(imOut, ink, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "RGBA") == 0) {
+ ImagingSectionEnter(&cookie);
+ fill_mask_RGBA(imOut, ink, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else if (strcmp(imMask->mode, "RGBa") == 0) {
+ ImagingSectionEnter(&cookie);
+ fill_mask_RGBa(imOut, ink, imMask, dx0, dy0, sx0, sy0,
+ xsize, ysize, pixelsize);
+ ImagingSectionLeave(&cookie);
+
+ } else {
+ (void) ImagingError_ValueError("bad transparency mask");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/libImaging/PcdDecode.c b/libImaging/PcdDecode.c
new file mode 100644
index 000000000..b6898e301
--- /dev/null
+++ b/libImaging/PcdDecode.c
@@ -0,0 +1,78 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for uncompressed PCD image data.
+ *
+ * history:
+ * 96-05-10 fl Created
+ * 96-05-18 fl New tables
+ * 97-01-25 fl Use PhotoYCC unpacker
+ *
+ * notes:
+ * This driver supports uncompressed PCD modes only
+ * (resolutions up to 768x512).
+ *
+ * Copyright (c) Fredrik Lundh 1996-97.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ int x;
+ int chunk;
+ UINT8* out;
+ UINT8* ptr;
+
+ ptr = buf;
+
+ chunk = 3 * state->xsize;
+
+ for (;;) {
+
+ /* We need data for two full lines before we can do anything */
+ if (bytes < chunk)
+ return ptr - buf;
+
+ /* Unpack first line */
+ out = state->buffer;
+ for (x = 0; x < state->xsize; x++) {
+ out[0] = ptr[x];
+ out[1] = ptr[(x+4*state->xsize)/2];
+ out[2] = ptr[(x+5*state->xsize)/2];
+ out += 4;
+ }
+
+ state->shuffle((UINT8*) im->image[state->y],
+ state->buffer, state->xsize);
+
+ if (++state->y >= state->ysize)
+ return -1; /* This can hardly happen */
+
+ /* Unpack second line */
+ out = state->buffer;
+ for (x = 0; x < state->xsize; x++) {
+ out[0] = ptr[x+state->xsize];
+ out[1] = ptr[(x+4*state->xsize)/2];
+ out[2] = ptr[(x+5*state->xsize)/2];
+ out += 4;
+ }
+
+ state->shuffle((UINT8*) im->image[state->y],
+ state->buffer, state->xsize);
+
+ if (++state->y >= state->ysize)
+ return -1;
+
+ ptr += chunk;
+ bytes -= chunk;
+
+ }
+}
diff --git a/libImaging/PcxDecode.c b/libImaging/PcxDecode.c
new file mode 100644
index 000000000..ab82b23ad
--- /dev/null
+++ b/libImaging/PcxDecode.c
@@ -0,0 +1,75 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for PCX image data.
+ *
+ * history:
+ * 95-09-14 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1995.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+int
+ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8 n;
+ UINT8* ptr;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (bytes < 1)
+ return ptr - buf;
+
+ if ((*ptr & 0xC0) == 0xC0) {
+
+ /* Run */
+ if (bytes < 2)
+ return ptr - buf;
+
+ n = ptr[0] & 0x3F;
+
+ while (n > 0) {
+ if (state->x >= state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ break;
+ }
+ state->buffer[state->x++] = ptr[1];
+ n--;
+ }
+
+ ptr += 2; bytes -= 2;
+
+ } else {
+
+ /* Literal */
+ state->buffer[state->x++] = ptr[0];
+ ptr++; bytes--;
+
+ }
+
+ if (state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ }
+}
diff --git a/libImaging/PcxEncode.c b/libImaging/PcxEncode.c
new file mode 100644
index 000000000..87d599463
--- /dev/null
+++ b/libImaging/PcxEncode.c
@@ -0,0 +1,148 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * encoder for PCX data
+ *
+ * history:
+ * 99-02-07 fl created
+ *
+ * Copyright (c) Fredrik Lundh 1999.
+ * Copyright (c) Secret Labs AB 1999.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+enum { INIT, FETCH, ENCODE };
+
+/* we're reusing "ystep" to store the last value */
+#define LAST ystep
+
+int
+ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+ int this;
+
+ ptr = buf;
+
+ if (!state->state) {
+
+ /* sanity check */
+ if (state->xsize <= 0 || state->ysize <= 0) {
+ state->errcode = IMAGING_CODEC_END;
+ return 0;
+ }
+
+ state->bytes = (state->xsize*state->bits + 7) / 8;
+ state->state = FETCH;
+
+ }
+
+ for (;;)
+
+ switch (state->state) {
+ case FETCH:
+
+ /* get a line of data */
+ if (state->y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_END;
+ return ptr - buf;
+ }
+
+ state->shuffle(state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize);
+
+ state->y++;
+
+ state->count = 1;
+ state->LAST = state->buffer[0];
+
+ state->x = 1;
+
+ state->state = ENCODE;
+ /* fall through */
+
+ case ENCODE:
+
+ /* compress this line */
+
+ /* when we arrive here, "count" contains the number of
+ bytes having the value of "LAST" that we've already
+ seen */
+
+ while (state->x < state->bytes) {
+
+ if (state->count == 63) {
+
+ /* this run is full; flush it */
+ if (bytes < 2)
+ return ptr - buf;
+ *ptr++ = 0xff;
+ *ptr++ = state->LAST;
+ bytes -= 2;
+
+ state->count = 0;
+
+ }
+
+ this = state->buffer[state->x];
+
+ if (this == state->LAST) {
+
+ /* extend the current run */
+ state->x++;
+ state->count++;
+
+ } else {
+
+ /* start a new run */
+ if (state->count == 1 && (state->LAST < 0xc0)) {
+ if (bytes < 1)
+ return ptr - buf;
+ *ptr++ = state->LAST;
+ bytes--;
+ } else {
+ if (state->count > 0) {
+ if (bytes < 2)
+ return ptr - buf;
+ *ptr++ = 0xc0 | state->count;
+ *ptr++ = state->LAST;
+ bytes -= 2;
+ }
+ }
+
+ state->LAST = this;
+ state->count = 1;
+
+ state->x++;
+
+ }
+ }
+
+ /* end of line; flush the current run */
+ if (state->count == 1 && (state->LAST < 0xc0)) {
+ if (bytes < 1)
+ return ptr - buf;
+ *ptr++ = state->LAST;
+ bytes--;
+ } else {
+ if (state->count > 0) {
+ if (bytes < 2)
+ return ptr - buf;
+ *ptr++ = 0xc0 | state->count;
+ *ptr++ = state->LAST;
+ bytes -= 2;
+ }
+ }
+
+ /* read next line */
+ state->state = FETCH;
+ break;
+
+ }
+}
diff --git a/libImaging/Point.c b/libImaging/Point.c
new file mode 100644
index 000000000..2593dca57
--- /dev/null
+++ b/libImaging/Point.c
@@ -0,0 +1,263 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * point (pixel) translation
+ *
+ * history:
+ * 1995-11-27 fl Created
+ * 1996-03-31 fl Fixed colour support
+ * 1996-08-13 fl Support 8-bit to "1" thresholding
+ * 1997-05-31 fl Added floating point transform
+ * 1998-07-02 fl Added integer point transform
+ * 1998-07-17 fl Support L to anything lookup
+ * 2004-12-18 fl Refactored; added I to L lookup
+ *
+ * Copyright (c) 1997-2004 by Secret Labs AB.
+ * Copyright (c) 1995-2004 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+typedef struct {
+ const void* table;
+} im_point_context;
+
+static void
+im_point_8_8(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 8-bit source, 8-bit destination */
+ UINT8* table = (UINT8*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = imIn->image8[y];
+ UINT8* out = imOut->image8[y];
+ for (x = 0; x < imIn->xsize; x++)
+ out[x] = table[in[x]];
+ }
+}
+
+static void
+im_point_2x8_2x8(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 2x8-bit source, 2x8-bit destination */
+ UINT8* table = (UINT8*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+ for (x = 0; x < imIn->xsize; x++) {
+ out[0] = table[in[0]];
+ out[3] = table[in[3]+256];
+ in += 4; out += 4;
+ }
+ }
+}
+
+static void
+im_point_3x8_3x8(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 3x8-bit source, 3x8-bit destination */
+ UINT8* table = (UINT8*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+ for (x = 0; x < imIn->xsize; x++) {
+ out[0] = table[in[0]];
+ out[1] = table[in[1]+256];
+ out[2] = table[in[2]+512];
+ in += 4; out += 4;
+ }
+ }
+}
+
+static void
+im_point_4x8_4x8(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 4x8-bit source, 4x8-bit destination */
+ UINT8* table = (UINT8*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = (UINT8*) imIn->image[y];
+ UINT8* out = (UINT8*) imOut->image[y];
+ for (x = 0; x < imIn->xsize; x++) {
+ out[0] = table[in[0]];
+ out[1] = table[in[1]+256];
+ out[2] = table[in[2]+512];
+ out[3] = table[in[3]+768];
+ in += 4; out += 4;
+ }
+ }
+}
+
+static void
+im_point_8_32(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 8-bit source, 32-bit destination */
+ INT32* table = (INT32*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT8* in = imIn->image8[y];
+ INT32* out = imOut->image32[y];
+ for (x = 0; x < imIn->xsize; x++)
+ out[x] = table[in[x]];
+ }
+}
+
+static void
+im_point_32_8(Imaging imOut, Imaging imIn, im_point_context* context)
+{
+ int x, y;
+ /* 32-bit source, 8-bit destination */
+ UINT8* table = (UINT8*) context->table;
+ for (y = 0; y < imIn->ysize; y++) {
+ INT32* in = imIn->image32[y];
+ UINT8* out = imOut->image8[y];
+ for (x = 0; x < imIn->xsize; x++) {
+ int v = in[x];
+ if (v < 0)
+ v = 0;
+ else if (v > 65535)
+ v = 65535;
+ out[x] = table[v];
+ }
+ }
+}
+
+Imaging
+ImagingPoint(Imaging imIn, const char* mode, const void* table)
+{
+ /* lookup table transform */
+
+ ImagingSectionCookie cookie;
+ Imaging imOut;
+ im_point_context context;
+ void (*point)(Imaging imIn, Imaging imOut, im_point_context* context);
+
+ if (!imIn)
+ return (Imaging) ImagingError_ModeError();
+
+ if (!mode)
+ mode = imIn->mode;
+
+ if (imIn->type != IMAGING_TYPE_UINT8) {
+ if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0)
+ goto mode_mismatch;
+ } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0)
+ goto mode_mismatch;
+
+ imOut = ImagingNew(mode, imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+
+ /* find appropriate handler */
+ if (imIn->type == IMAGING_TYPE_UINT8) {
+ if (imIn->bands == imOut->bands && imIn->type == imOut->type) {
+ switch (imIn->bands) {
+ case 1:
+ point = im_point_8_8;
+ break;
+ case 2:
+ point = im_point_2x8_2x8;
+ break;
+ case 3:
+ point = im_point_3x8_3x8;
+ break;
+ case 4:
+ point = im_point_4x8_4x8;
+ break;
+ default:
+ /* this cannot really happen */
+ point = im_point_8_8;
+ break;
+ }
+ } else
+ point = im_point_8_32;
+ } else
+ point = im_point_32_8;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ ImagingSectionEnter(&cookie);
+
+ context.table = table;
+ point(imOut, imIn, &context);
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+
+ mode_mismatch:
+ return (Imaging) ImagingError_ValueError(
+ "point operation not supported for this mode"
+ );
+}
+
+
+Imaging
+ImagingPointTransform(Imaging imIn, double scale, double offset)
+{
+ /* scale/offset transform */
+
+ ImagingSectionCookie cookie;
+ Imaging imOut;
+ int x, y;
+
+ if (!imIn || (strcmp(imIn->mode, "I") != 0 &&
+ strcmp(imIn->mode, "I;16") != 0 &&
+ strcmp(imIn->mode, "F") != 0))
+ return (Imaging) ImagingError_ModeError();
+
+ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+
+ ImagingCopyInfo(imOut, imIn);
+
+ switch (imIn->type) {
+ case IMAGING_TYPE_INT32:
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ INT32* in = imIn->image32[y];
+ INT32* out = imOut->image32[y];
+ /* FIXME: add clipping? */
+ for (x = 0; x < imIn->xsize; x++)
+ out[x] = in[x] * scale + offset;
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ case IMAGING_TYPE_FLOAT32:
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ FLOAT32* in = (FLOAT32*) imIn->image32[y];
+ FLOAT32* out = (FLOAT32*) imOut->image32[y];
+ for (x = 0; x < imIn->xsize; x++)
+ out[x] = in[x] * scale + offset;
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ case IMAGING_TYPE_SPECIAL:
+ if (strcmp(imIn->mode,"I;16") == 0) {
+ ImagingSectionEnter(&cookie);
+ for (y = 0; y < imIn->ysize; y++) {
+ UINT16* in = (UINT16 *)imIn->image[y];
+ UINT16* out = (UINT16 *)imOut->image[y];
+ /* FIXME: add clipping? */
+ for (x = 0; x < imIn->xsize; x++)
+ out[x] = in[x] * scale + offset;
+ }
+ ImagingSectionLeave(&cookie);
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ ImagingDelete(imOut);
+ return (Imaging) ImagingError_ValueError("internal error");
+ }
+
+ return imOut;
+}
diff --git a/libImaging/Quant.c b/libImaging/Quant.c
new file mode 100644
index 000000000..1b76a6a0f
--- /dev/null
+++ b/libImaging/Quant.c
@@ -0,0 +1,1610 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * image quantizer
+ *
+ * history:
+ * 1998-09-10 tjs Contributed
+ * 1998-12-29 fl Added to PIL 1.0b1
+ * 2004-02-21 fl Fixed bogus free() on quantization error
+ * 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.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+#include
+#include
+#include
+#include
+
+#include "Quant.h"
+
+#include "QuantDefines.h"
+#include "QuantHash.h"
+#include "QuantHeap.h"
+
+#define NO_OUTPUT
+
+typedef struct {
+ unsigned long scale;
+} PixelHashData;
+
+typedef struct _PixelList {
+ struct _PixelList *next[3],*prev[3];
+ Pixel p;
+ unsigned int flag:1;
+ int count;
+} PixelList;
+
+typedef struct _BoxNode {
+ struct _BoxNode *l,*r;
+ PixelList *head[3],*tail[3];
+ int axis;
+ int volume;
+ unsigned long pixelCount;
+} BoxNode;
+
+#define _SQR(x) ((x)*(x))
+#define _DISTSQR(p1,p2) \
+ _SQR((int)((p1)->c.r)-(int)((p2)->c.r))+ \
+ _SQR((int)((p1)->c.g)-(int)((p2)->c.g))+ \
+ _SQR((int)((p1)->c.b)-(int)((p2)->c.b))
+
+#define MAX_HASH_ENTRIES 65536
+
+#define PIXEL_HASH(r,g,b) \
+ (((unsigned int)(r) )*463 ^ \
+ ((unsigned int)(g)<< 8)*10069 ^ \
+ ((unsigned int)(b)<<16)*64997)
+
+#define PIXEL_UNSCALE(p,q,s) \
+ ((q)->c.r=(p)->c.r<<(s)), \
+ ((q)->c.g=(p)->c.g<<(s)), \
+ ((q)->c.b=(p)->c.b<<(s))
+
+#define PIXEL_SCALE(p,q,s)\
+ ((q)->c.r=(p)->c.r>>(s)), \
+ ((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)
+{
+ Pixel *pixel=(Pixel *)&p;
+ unsigned long hash=PIXEL_HASH(pixel->c.r,
+ pixel->c.g,
+ pixel->c.b);
+ return hash;
+}
+
+static int
+unshifted_pixel_cmp(const HashTable h, const void *a, const void *b)
+{
+ 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) {
+ return 0;
+ } else {
+ return (int)(pixel1->c.b)-(int)(pixel2->c.b);
+ }
+ } else {
+ return (int)(pixel1->c.g)-(int)(pixel2->c.g);
+ }
+ } else {
+ return (int)(pixel1->c.r)-(int)(pixel2->c.r);
+ }
+}
+
+static unsigned long
+pixel_hash(const HashTable h,const void *p)
+{
+ 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;
+}
+
+static int
+pixel_cmp(const HashTable h,const void *a,const void *b)
+{
+ 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);
+ return (A==B)?0:((Ascale=0;
+ timer=timer3=clock();
+ for (i=0;iMAX_HASH_ENTRIES) {
+ d->scale++;
+#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
+ timer+=timer2;
+ }
+ }
+#ifndef NO_OUTPUT
+ printf ("inserts took %f sec\n",(clock()-timer)/(double)CLOCKS_PER_SEC);
+#endif
+#ifndef NO_OUTPUT
+ printf ("total %f sec\n",(clock()-timer3)/(double)CLOCKS_PER_SEC);
+#endif
+ return hash;
+}
+
+static void
+destroy_pixel_hash(HashTable hash)
+{
+ PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash);
+ if (d) free(d);
+ hashtable_free(hash);
+}
+
+
+/* 1. hash quantized pixels. */
+/* 2. create R,G,B lists of sorted quantized pixels. */
+/* 3. median cut. */
+/* 4. build hash table from median cut boxes. */
+/* 5. for each pixel, compute entry in hash table, and hence median cut box. */
+/* 6. compute median cut box pixel averages. */
+/* 7. map each pixel to nearest average. */
+
+static int
+compute_box_volume(BoxNode *b)
+{
+ unsigned char rl,rh,gl,gh,bl,bh;
+ if (b->volume>=0) return b->volume;
+ if (!b->head[0]) {
+ b->volume=0;
+ } else {
+ rh=b->head[0]->p.c.r;
+ rl=b->tail[0]->p.c.r;
+ gh=b->head[1]->p.c.g;
+ gl=b->tail[1]->p.c.g;
+ bh=b->head[2]->p.c.b;
+ bl=b->tail[2]->p.c.b;
+ b->volume=(rh-rl+1)*(gh-gl+1)*(bh-bl+1);
+ }
+ return b->volume;
+}
+
+static void
+hash_to_list(HashTable h, const void *key, const void *val, 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);
+
+ p=malloc(sizeof(PixelList));
+ if (!p) return;
+
+ p->flag=0;
+ p->p=q;
+ p->count=count;
+ for (i=0;i<3;i++) {
+ p->next[i]=pl[i];
+ p->prev[i]=NULL;
+ if (pl[i]) pl[i]->prev[i]=p;
+ pl[i]=p;
+ }
+}
+
+static PixelList *
+mergesort_pixels(PixelList *head, int i)
+{
+ PixelList *c,*t,*a,*b,*p;
+ if (!head||!head->next[i]) {
+ if (head) {
+ head->next[i]=NULL;
+ head->prev[i]=NULL;
+ }
+ return head;
+ }
+ for (c=t=head;c&&t;c=c->next[i],t=(t->next[i])?t->next[i]->next[i]:NULL);
+ if (c) {
+ if (c->prev[i]) c->prev[i]->next[i]=NULL;
+ c->prev[i]=NULL;
+ }
+ a=mergesort_pixels(head,i);
+ b=mergesort_pixels(c,i);
+ head=NULL;
+ p=NULL;
+ while (a&&b) {
+ if (a->p.a.v[i]>b->p.a.v[i]) {
+ c=a;
+ a=a->next[i];
+ } else {
+ c=b;
+ b=b->next[i];
+ }
+ c->prev[i]=p;
+ c->next[i]=NULL;
+ if (p) p->next[i]=c;
+ p=c;
+ if (!head) head=c;
+ }
+ if (a) {
+ c->next[i]=a;
+ a->prev[i]=c;
+ } else if (b) {
+ c->next[i]=b;
+ b->prev[i]=c;
+ }
+ return head;
+}
+
+#if defined(TEST_MERGESORT) || defined(TEST_SORTED)
+static int
+test_sorted(PixelList *pl[3])
+{
+ int i,n,l;
+ PixelList *t;
+
+ for(i=0;i<3;i++) {
+ n=0;
+ l=256;
+ for (t=pl[i];t;t=t->next[i]) {
+ if (lp.a.v[i]) return 0;
+ l=t->p.a.v[i];
+ }
+ }
+ return 1;
+}
+#endif
+
+static int
+box_heap_cmp(const Heap h, const void *A, const void *B)
+{
+ BoxNode *a=(BoxNode *)A;
+ BoxNode *b=(BoxNode *)B;
+ return (int)a->pixelCount-(int)b->pixelCount;
+}
+
+#define LUMINANCE(p) (77*(p)->c.r+150*(p)->c.g+29*(p)->c.b)
+
+static int
+splitlists(PixelList *h[3],
+ PixelList *t[3],
+ PixelList *nh[2][3],
+ PixelList *nt[2][3],
+ unsigned long nCount[2],
+ int axis,
+ unsigned long pixelCount)
+{
+ unsigned long left;
+
+ PixelList *l,*r,*c,*n;
+ int i;
+ int nRight,nLeft;
+ int splitColourVal;
+
+#ifdef TEST_SPLIT
+ {
+ PixelList *_prevTest,*_nextTest;
+ int _i,_nextCount[3],_prevCount[3];
+ for (_i=0;_i<3;_i++) {
+ for (_nextCount[_i]=0,_nextTest=h[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++);
+ for (_prevCount[_i]=0,_prevTest=t[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++);
+ if (_nextTest!=t[_i]) {
+ printf ("next-list of axis %d does not end at tail\n",_i);
+ exit(1);
+ }
+ if (_prevTest!=h[_i]) {
+ printf ("prev-list of axis %d does not end at head\n",_i);
+ exit(1);
+ }
+ for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]);
+ for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]);
+ if (_nextTest!=h[_i]) {
+ printf ("next-list of axis %d does not loop back to head\n",_i);
+ exit(1);
+ }
+ if (_prevTest!=t[_i]) {
+ printf ("prev-list of axis %d does not loop back to tail\n",_i);
+ exit(1);
+ }
+ }
+ for (_i=1;_i<3;_i++) {
+ if (_prevCount[_i]!=_prevCount[_i-1] ||
+ _nextCount[_i]!=_nextCount[_i-1] ||
+ _prevCount[_i]!=_nextCount[_i]) {
+ printf ("{%d %d %d} {%d %d %d}\n",
+ _prevCount[0],
+ _prevCount[1],
+ _prevCount[2],
+ _nextCount[0],
+ _nextCount[1],
+ _nextCount[2]);
+ exit(1);
+ }
+ }
+ }
+#endif
+ nCount[0]=nCount[1]=0;
+ nLeft=nRight=0;
+ for (left=0,c=h[axis];c;) {
+ left=left+c->count;
+ nCount[0]+=c->count;
+ c->flag=0;
+ nLeft++;
+ c=c->next[axis];
+ if (left*2>pixelCount) {
+ break;
+ }
+ }
+ if (c) {
+ splitColourVal=c->prev[axis]->p.a.v[axis];
+ for (;c;c=c->next[axis]) {
+ if (splitColourVal!=c->p.a.v[axis]) {
+ break;
+ }
+ c->flag=0;
+ nLeft++;
+ nCount[0]+=c->count;
+ }
+ }
+ for (;c;c=c->next[axis]) {
+ c->flag=1;
+ nRight++;
+ nCount[1]+=c->count;
+ }
+ if (!nRight) {
+ for (c=t[axis],splitColourVal=t[axis]->p.a.v[axis];c;c=c->prev[axis]) {
+ if (splitColourVal!=c->p.a.v[axis]) {
+ break;
+ }
+ c->flag=1;
+ nRight++;
+ nLeft--;
+ nCount[0]-=c->count;
+ nCount[1]+=c->count;
+ }
+ }
+#ifndef NO_OUTPUT
+ if (!nLeft) {
+ for (c=h[axis];c;c=c->next[axis]) {
+ printf ("[%d %d %d]\n",c->p.c.r,c->p.c.g,c->p.c.b);
+ }
+ printf ("warning... trivial split\n");
+ }
+#endif
+
+ for (i=0;i<3;i++) {
+ l=r=NULL;
+ nh[0][i]=nt[0][i]=NULL;
+ nh[1][i]=nt[1][i]=NULL;
+ for (c=h[i];c;c=n) {
+ n=c->next[i];
+ if (c->flag) { /* move pixel to right list*/
+ if (r) r->next[i]=c; else nh[1][i]=c;
+ c->prev[i]=r;
+ r=c;
+ } else { /* move pixel to left list */
+ if (l) l->next[i]=c; else nh[0][i]=c;
+ c->prev[i]=l;
+ l=c;
+ }
+ }
+ if (l) l->next[i]=NULL;
+ if (r) r->next[i]=NULL;
+ nt[0][i]=l;
+ nt[1][i]=r;
+ }
+ return 1;
+}
+
+static int
+split(BoxNode *node)
+{
+ unsigned char rl,rh,gl,gh,bl,bh;
+ int f[3];
+ int best,axis;
+ int i;
+ PixelList *heads[2][3];
+ PixelList *tails[2][3];
+ unsigned long newCounts[2];
+ BoxNode *left,*right;
+
+ rh=node->head[0]->p.c.r;
+ rl=node->tail[0]->p.c.r;
+ gh=node->head[1]->p.c.g;
+ gl=node->tail[1]->p.c.g;
+ bh=node->head[2]->p.c.b;
+ bl=node->tail[2]->p.c.b;
+#ifdef TEST_SPLIT
+ printf ("splitting node [%d %d %d] [%d %d %d] ",rl,gl,bl,rh,gh,bh);
+#endif
+ f[0]=(rh-rl)*77;
+ f[1]=(gh-gl)*150;
+ f[2]=(bh-bl)*29;
+
+ best=f[0];
+ axis=0;
+ for (i=1;i<3;i++) {
+ if (besttail[_i]->next[_i]) {
+ printf ("tail is not tail\n");
+ printf ("node->tail[%d]->next[%d]=%p\n",_i,_i,node->tail[_i]->next[_i]);
+ }
+ if (node->head[_i]->prev[_i]) {
+ printf ("head is not head\n");
+ printf ("node->head[%d]->prev[%d]=%p\n",_i,_i,node->head[_i]->prev[_i]);
+ }
+ }
+
+ for (_i=0;_i<3;_i++) {
+ for (_nextCount[_i]=0,_nextTest=node->head[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++);
+ for (_prevCount[_i]=0,_prevTest=node->tail[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++);
+ if (_nextTest!=node->tail[_i]) {
+ printf ("next-list of axis %d does not end at tail\n",_i);
+ }
+ if (_prevTest!=node->head[_i]) {
+ printf ("prev-list of axis %d does not end at head\n",_i);
+ }
+ for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]);
+ for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]);
+ if (_nextTest!=node->head[_i]) {
+ printf ("next-list of axis %d does not loop back to head\n",_i);
+ }
+ if (_prevTest!=node->tail[_i]) {
+ printf ("prev-list of axis %d does not loop back to tail\n",_i);
+ }
+ }
+ for (_i=1;_i<3;_i++) {
+ if (_prevCount[_i]!=_prevCount[_i-1] ||
+ _nextCount[_i]!=_nextCount[_i-1] ||
+ _prevCount[_i]!=_nextCount[_i]) {
+ printf ("{%d %d %d} {%d %d %d}\n",
+ _prevCount[0],
+ _prevCount[1],
+ _prevCount[2],
+ _nextCount[0],
+ _nextCount[1],
+ _nextCount[2]);
+ }
+ }
+ }
+#endif
+ node->axis=axis;
+ if (!splitlists(node->head,
+ node->tail,
+ heads,
+ tails,
+ newCounts,
+ axis,
+ node->pixelCount)) {
+#ifndef NO_OUTPUT
+ printf ("list split failed.\n");
+#endif
+ return 0;
+ }
+#ifdef TEST_SPLIT
+ if (!test_sorted(heads[0])) {
+ printf ("bug in split");
+ exit(1);
+ }
+ if (!test_sorted(heads[1])) {
+ printf ("bug in split");
+ exit(1);
+ }
+#endif
+ left=malloc(sizeof(BoxNode));
+ right=malloc(sizeof(BoxNode));
+ if (!left||!right) {
+ return 0;
+ }
+ for(i=0;i<3;i++) {
+ left->head[i]=heads[0][i];
+ left->tail[i]=tails[0][i];
+ right->head[i]=heads[1][i];
+ right->tail[i]=tails[1][i];
+ node->head[i]=NULL;
+ node->tail[i]=NULL;
+ }
+#ifdef TEST_SPLIT
+ if (left->head[0]) {
+ rh=left->head[0]->p.c.r;
+ rl=left->tail[0]->p.c.r;
+ gh=left->head[1]->p.c.g;
+ gl=left->tail[1]->p.c.g;
+ bh=left->head[2]->p.c.b;
+ bl=left->tail[2]->p.c.b;
+ printf (" left node [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh);
+ }
+ if (right->head[0]) {
+ rh=right->head[0]->p.c.r;
+ rl=right->tail[0]->p.c.r;
+ gh=right->head[1]->p.c.g;
+ gl=right->tail[1]->p.c.g;
+ bh=right->head[2]->p.c.b;
+ bl=right->tail[2]->p.c.b;
+ printf (" right node [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh);
+ }
+#endif
+ left->l=left->r=NULL;
+ right->l=right->r=NULL;
+ left->axis=right->axis=-1;
+ left->volume=right->volume=-1;
+ left->pixelCount=newCounts[0];
+ right->pixelCount=newCounts[1];
+ node->l=left;
+ node->r=right;
+ return 1;
+}
+
+static BoxNode *
+median_cut(PixelList *hl[3],
+ unsigned long imPixelCount,
+ int nPixels)
+{
+ PixelList *tl[3];
+ int i;
+ BoxNode *root;
+ Heap h;
+ BoxNode *thisNode;
+
+ h=ImagingQuantHeapNew(box_heap_cmp);
+ root=malloc(sizeof(BoxNode));
+ if (!root) { ImagingQuantHeapFree(h); return NULL; }
+ for(i=0;i<3;i++) {
+ for (tl[i]=hl[i];tl[i]&&tl[i]->next[i];tl[i]=tl[i]->next[i]);
+ root->head[i]=hl[i];
+ root->tail[i]=tl[i];
+ }
+ root->l=root->r=NULL;
+ root->axis=-1;
+ root->volume=-1;
+ root->pixelCount=imPixelCount;
+
+ ImagingQuantHeapAdd(h,(void *)root);
+ while (--nPixels) {
+ do {
+ if (!ImagingQuantHeapRemove(h,(void **)&thisNode)) {
+ goto done;
+ }
+ } while (compute_box_volume(thisNode)==1);
+ if (!split(thisNode)) {
+#ifndef NO_OUTPUT
+ printf ("Oops, split failed...\n");
+#endif
+ exit (1);
+ }
+ ImagingQuantHeapAdd(h,(void *)(thisNode->l));
+ ImagingQuantHeapAdd(h,(void *)(thisNode->r));
+ }
+done:
+ ImagingQuantHeapFree(h);
+ return root;
+}
+
+static void
+free_box_tree(BoxNode *n)
+{
+ PixelList *p,*pp;
+ if (n->l) free_box_tree(n->l);
+ if (n->r) free_box_tree(n->r);
+ for (p=n->head[0];p;p=pp) {
+ pp=p->next[0];
+ free(p);
+ }
+ free(n);
+}
+
+#ifdef TEST_SPLIT_INTEGRITY
+static int
+checkContained(BoxNode *n,Pixel *pp)
+{
+ if (n->l&&n->r) {
+ return checkContained(n->l,pp)+checkContained(n->r,pp);
+ }
+ if (n->l||n->r) {
+#ifndef NO_OUTPUT
+ printf ("box tree is dead\n");
+#endif
+ return 0;
+ }
+ if (
+ pp->c.r<=n->head[0]->p.c.r &&
+ pp->c.r>=n->tail[0]->p.c.r &&
+ pp->c.g<=n->head[1]->p.c.g &&
+ pp->c.g>=n->tail[1]->p.c.g &&
+ pp->c.b<=n->head[2]->p.c.b &&
+ pp->c.b>=n->tail[2]->p.c.b) {
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+static int
+annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box)
+{
+ PixelList *p;
+ PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h);
+ Pixel q;
+ if (n->l&&n->r) {
+ return annotate_hash_table(n->l,h,box) && annotate_hash_table(n->r,h,box);
+ }
+ if (n->l||n->r) {
+#ifndef NO_OUTPUT
+ printf ("box tree is dead\n");
+#endif
+ return 0;
+ }
+ 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)) {
+#ifndef NO_OUTPUT
+ printf ("hashtable insert failed\n");
+#endif
+ return 0;
+ }
+ }
+ if (n->head[0]) (*box)++;
+ return 1;
+}
+
+static int
+_sort_ulong_ptr_keys(const void *a, const void *b)
+{
+ unsigned long A=**(unsigned long **)a;
+ unsigned long B=**(unsigned long **)b;
+ return (A==B)?0:((A*(skRow[k]));k--) {
+ skRow[k]=skRow[k-1];
+ }
+ if (k!=j) skRow[k]=skElt;
+ }
+ }
+ return 1;
+}
+
+static int
+build_distance_tables(unsigned long *avgDist,
+ unsigned long **avgDistSortKey,
+ Pixel *p,
+ unsigned long nEntries)
+{
+ unsigned long i,j;
+
+ for (i=0;i1) {
+ printf ("pixel in two boxes\n");
+ for(i=0;i<3;i++) free (avg[i]);
+ free(count);
+ return 0;
+ }
+#endif
+ if (!hashtable_lookup(medianBoxHash,(void *)pixelData[i].v,(void **)&paletteEntry)) {
+#ifndef NO_OUTPUT
+ printf ("pixel lookup failed\n");
+#endif
+ for(i=0;i<3;i++) free (avg[i]);
+ free(count);
+ return 0;
+ }
+ if (paletteEntry>=nPaletteEntries) {
+#ifndef NO_OUTPUT
+ printf ("panic - paletteEntry>=nPaletteEntries (%d>=%d)\n",(int)paletteEntry,(int)nPaletteEntries);
+#endif
+ for(i=0;i<3;i++) free (avg[i]);
+ free(count);
+ return 0;
+ }
+ avg[0][paletteEntry]+=pixelData[i].c.r;
+ avg[1][paletteEntry]+=pixelData[i].c.g;
+ avg[2][paletteEntry]+=pixelData[i].c.b;
+ count[paletteEntry]++;
+ }
+ p=malloc(sizeof(Pixel)*nPaletteEntries);
+ if (!p) {
+ for(i=0;i<3;i++) free (avg[i]);
+ free(count);
+ return 0;
+ }
+ for (i=0;i=nPaletteEntries) {
+#ifndef NO_OUTPUT
+ printf ("scream\n");
+#endif
+ return 0;
+ }
+ avg[0][qp[i]]+=pixelData[i].c.r;
+ avg[1][qp[i]]+=pixelData[i].c.g;
+ avg[2][qp[i]]+=pixelData[i].c.b;
+ count[qp[i]]++;
+ }
+ for (i=0;i
+ {
+ unsigned long 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);
+ if (data->secondPixel || newDistdata->furthestDistance) {
+ data->furthestDistance=oldDist;
+ data->furthest.v=pixel->v;
+ }
+}
+
+int
+quantize2(Pixel *pixelData,
+ unsigned long nPixels,
+ unsigned long nQuantPixels,
+ Pixel **palette,
+ unsigned long *paletteLength,
+ unsigned long **quantizedPixels,
+ int kmeans)
+{
+ HashTable h;
+ unsigned long i;
+ unsigned long mean[3];
+ Pixel *p;
+ DistanceData data;
+
+ unsigned long *qp;
+ unsigned long *avgDist;
+ unsigned long **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;i 256)
+ /* FIXME: for colors > 256, consider returning an RGB image
+ instead (see @PIL205) */
+ return (Imaging) ImagingError_ValueError("bad number of colors");
+
+ if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 &&
+ strcmp(im->mode, "RGB"))
+ return ImagingError_ModeError();
+
+ p = malloc(sizeof(Pixel) * im->xsize * im->ysize);
+ if (!p)
+ return ImagingError_MemoryError();
+
+ /* collect statistics */
+
+ /* FIXME: maybe we could load the hash tables directly from the
+ image data? */
+
+ if (!strcmp(im->mode, "L")) {
+ /* greyscale */
+
+ /* FIXME: converting a "L" image to "P" with 256 colors
+ should be done by a simple copy... */
+
+ for (i = y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++, i++)
+ p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x];
+
+ } else if (!strcmp(im->mode, "P")) {
+ /* palette */
+
+ pp = im->palette->palette;
+
+ for (i = y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++, i++) {
+ v = im->image8[y][x];
+ p[i].c.r = pp[v*4+0];
+ p[i].c.g = pp[v*4+1];
+ p[i].c.b = pp[v*4+2];
+ }
+
+ } else if (!strcmp(im->mode, "RGB")) {
+ /* true colour */
+
+ for (i = y = 0; y < im->ysize; y++)
+ for (x = 0; x < im->xsize; x++, i++)
+ p[i].v = im->image32[y][x];
+
+ } else {
+ free(p);
+ return (Imaging) ImagingError_ValueError("internal error");
+ }
+
+ switch (mode) {
+ case 0:
+ /* median cut */
+ result = quantize(
+ p,
+ im->xsize*im->ysize,
+ colors,
+ &palette,
+ &paletteLength,
+ &newData,
+ kmeans
+ );
+ break;
+ case 1:
+ /* maximum coverage */
+ result = quantize2(
+ p,
+ im->xsize*im->ysize,
+ colors,
+ &palette,
+ &paletteLength,
+ &newData,
+ kmeans
+ );
+ break;
+ default:
+ result = 0;
+ break;
+ }
+
+ free(p);
+
+ if (result) {
+
+ imOut = ImagingNew("P", im->xsize, im->ysize);
+
+ for (i = y = 0; y < im->ysize; y++)
+ for (x=0; x < im->xsize; x++)
+ imOut->image8[y][x] = (unsigned char) newData[i++];
+
+ free(newData);
+
+ pp = imOut->palette->palette;
+
+ for (i = j = 0; i < (int) paletteLength; i++) {
+ *pp++ = palette[i].c.r;
+ *pp++ = palette[i].c.g;
+ *pp++ = palette[i].c.b;
+ *pp++ = 255;
+ }
+ for (; i < 256; i++) {
+ *pp++ = 0;
+ *pp++ = 0;
+ *pp++ = 0;
+ *pp++ = 255;
+ }
+
+ free(palette);
+
+ return imOut;
+
+ } else {
+
+ return (Imaging) ImagingError_ValueError("quantization error");
+
+ }
+}
diff --git a/libImaging/Quant.h b/libImaging/Quant.h
new file mode 100644
index 000000000..0de485a1a
--- /dev/null
+++ b/libImaging/Quant.h
@@ -0,0 +1,40 @@
+/*
+ * 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
new file mode 100644
index 000000000..5b080104e
--- /dev/null
+++ b/libImaging/QuantDefines.h
@@ -0,0 +1,25 @@
+/*
+ * 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
new file mode 100644
index 000000000..e6438402d
--- /dev/null
+++ b/libImaging/QuantHash.c
@@ -0,0 +1,477 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * hash tables used by the image quantizer
+ *
+ * history:
+ * 98-09-10 tjs Contributed
+ * 98-12-29 fl Added to PIL 1.0b1
+ *
+ * Written by Toby J Sargeant .
+ *
+ * Copyright (c) 1998 by Toby J Sargeant
+ * Copyright (c) 1998 by Secret Labs AB
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "QuantHash.h"
+#include "QuantDefines.h"
+
+typedef struct _IntHashNode {
+ struct _IntHashNode *next;
+ void *key,*value;
+} IntHashNode;
+
+typedef struct _IntHashTable {
+ IntHashNode **table;
+ unsigned long length;
+ unsigned long count;
+ HashFunc hashFunc;
+ HashCmpFunc cmpFunc;
+ DestroyFunc keyDestroyFunc;
+ DestroyFunc valDestroyFunc;
+ void *userData;
+} IntHashTable;
+
+#define MIN_LENGTH 11
+#define RESIZE_FACTOR 3
+
+static int _hashtable_insert_node(IntHashTable *,IntHashNode *,int,int,CollisionFunc);
+#if 0
+static int _hashtable_test(IntHashTable *);
+#endif
+
+HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) {
+ IntHashTable *h;
+ h=malloc(sizeof(IntHashTable));
+ if (!h) { return NULL; }
+ h->hashFunc=hf;
+ h->cmpFunc=cf;
+ h->keyDestroyFunc=NULL;
+ h->valDestroyFunc=NULL;
+ h->length=MIN_LENGTH;
+ h->count=0;
+ h->userData=NULL;
+ h->table=malloc(sizeof(IntHashNode *)*h->length);
+ if (!h->table) { free(h); return NULL; }
+ memset (h->table,0,sizeof(IntHashNode *)*h->length);
+ return (HashTable)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);
+ }
+ if (h->valDestroyFunc&&val) {
+ h->valDestroyFunc((HashTable)h,(void *)val);
+ }
+}
+
+static unsigned long _findPrime(unsigned long 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;
+ while (start>1) {
+ if (!unit[start&0x0f]) {
+ start+=dir;
+ continue;
+ }
+ for (t=2;t=sqrt((double)start)) {
+ break;
+ }
+ start+=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;
+ oldSize=h->length;
+ h->table=malloc(sizeof(IntHashNode *)*newSize);
+ if (!h->table) {
+ h->table=oldTable;
+ return;
+ }
+ h->length=newSize;
+ h->count=0;
+ memset (h->table,0,sizeof(IntHashNode *)*h->length);
+ for (i=0;inext;
+ _hashtable_insert_node(h,n,0,0,cf);
+ }
+ }
+ free(oldTable);
+}
+
+static void _hashtable_resize(IntHashTable *h) {
+ unsigned long newSize;
+ unsigned long oldSize;
+ oldSize=h->length;
+ newSize=oldSize;
+ if (h->count*RESIZE_FACTORlength) {
+ newSize=_findPrime(h->length/2-1,-1);
+ } else if (h->length*RESIZE_FACTORcount) {
+ newSize=_findPrime(h->length*2+1,+1);
+ }
+ if (newSizelength;i++) {
+ for (n=h->table[i];n&&n->next;n=n->next) {
+ j=h->cmpFunc((HashTable)h,n->key,n->next->key);
+ printf ("%c",j?(j<0?'-':'+'):'=');
+ }
+ printf ("\n");
+ }
+ return 0;
+}
+#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;
+ int i;
+
+ for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
+ nv=*n;
+ i=h->cmpFunc((HashTable)h,nv->key,node->key);
+ if (!i) {
+ if (cf) {
+ nv->key=node->key;
+ cf((HashTable)h,&(nv->key),&(nv->value),node->key,node->value);
+ free(node);
+ return 1;
+ } else {
+ if (h->valDestroyFunc) {
+ h->valDestroyFunc((HashTable)h,nv->value);
+ }
+ if (h->keyDestroyFunc) {
+ h->keyDestroyFunc((HashTable)h,nv->key);
+ }
+ nv->key=node->key;
+ nv->value=node->value;
+ free(node);
+ return 1;
+ }
+ } else if (i>0) {
+ break;
+ }
+ }
+ if (!update) {
+ node->next=*n;
+ *n=node;
+ h->count++;
+ if (resize) _hashtable_resize(h);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int update) {
+ IntHashNode **n,*nv;
+ IntHashNode *t;
+ int i;
+ unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
+
+ for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
+ nv=*n;
+ i=h->cmpFunc((HashTable)h,nv->key,key);
+ if (!i) {
+ if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,nv->value); }
+ nv->value=val;
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ if (!update) {
+ t=malloc(sizeof(IntHashNode));
+ if (!t) return 0;
+ t->next=*n;
+ *n=t;
+ t->key=key;
+ t->value=val;
+ h->count++;
+ if (resize) _hashtable_resize(h);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,void *newVal,int resize) {
+ IntHashNode **n,*nv;
+ IntHashNode *t;
+ int i;
+ unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
+
+ for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
+ nv=*n;
+ i=h->cmpFunc((HashTable)h,nv->key,key);
+ if (!i) {
+ *retVal=nv->value;
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ t=malloc(sizeof(IntHashNode));
+ if (!t) return 0;
+ t->next=*n;
+ *n=t;
+ t->key=key;
+ t->value=newVal;
+ *retVal=newVal;
+ h->count++;
+ if (resize) _hashtable_resize(h);
+ return 1;
+}
+
+int hashtable_insert_or_update_computed(HashTable H,
+ void *key,
+ ComputeFunc newFunc,
+ ComputeFunc existsFunc) {
+ IntHashTable *h=(IntHashTable *)H;
+ IntHashNode **n,*nv;
+ IntHashNode *t;
+ int i;
+ unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
+
+ for (n=&(h->table[hash]);*n;n=&((*n)->next)) {
+ nv=*n;
+ i=h->cmpFunc((HashTable)h,nv->key,key);
+ if (!i) {
+ void *old=nv->value;
+ if (existsFunc) {
+ existsFunc(H,nv->key,&(nv->value));
+ if (nv->value!=old) {
+ if (h->valDestroyFunc) {
+ h->valDestroyFunc((HashTable)h,old);
+ }
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ t=malloc(sizeof(IntHashNode));
+ if (!t) return 0;
+ t->key=key;
+ t->next=*n;
+ *n=t;
+ if (newFunc) {
+ newFunc(H,t->key,&(t->value));
+ } else {
+ free(t);
+ return 0;
+ }
+ h->count++;
+ _hashtable_resize(h);
+ return 1;
+}
+
+int hashtable_update(HashTable H,void *key,void *val) {
+ IntHashTable *h=(IntHashTable *)H;
+ return _hashtable_insert(h,key,val,1,0);
+}
+
+int hashtable_insert(HashTable H,void *key,void *val) {
+ IntHashTable *h=(IntHashTable *)H;
+ 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;
+
+ 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);
+ }
+ }
+ }
+}
+
+void hashtable_foreach(HashTable H,IteratorFunc i,void *u) {
+ IntHashTable *h=(IntHashTable *)H;
+ IntHashNode *n;
+ unsigned long 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);
+ }
+ }
+ }
+}
+
+void hashtable_free(HashTable H) {
+ IntHashTable *h=(IntHashTable *)H;
+ IntHashNode *n,*nn;
+ unsigned long i;
+
+ if (h->table) {
+ if (h->keyDestroyFunc || h->keyDestroyFunc) {
+ hashtable_foreach(H,_hashtable_destroy,NULL);
+ }
+ for (i=0;ilength;i++) {
+ for (n=h->table[i];n;n=nn) {
+ nn=n->next;
+ free(n);
+ }
+ }
+ free(h->table);
+ }
+ free(h);
+}
+
+DestroyFunc hashtable_set_value_destroy_func(HashTable H,DestroyFunc d) {
+ IntHashTable *h=(IntHashTable *)H;
+ DestroyFunc 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;
+ h->keyDestroyFunc=d;
+ return r;
+}
+
+static int _hashtable_remove(IntHashTable *h,
+ const void *key,
+ void **keyRet,
+ void **valRet,
+ int resize) {
+ unsigned long hash=h->hashFunc((HashTable)h,key)%h->length;
+ IntHashNode *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);
+ if (!i) {
+ if (p) p=n->next; else h->table[hash]=n->next;
+ *keyRet=n->key;
+ *valRet=n->value;
+ free(n);
+ h->count++;
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ 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;
+ int i;
+
+ for (p=NULL,n=h->table[hash];n;p=n,n=n->next) {
+ i=h->cmpFunc((HashTable)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); }
+ free(n);
+ h->count++;
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+int hashtable_remove(HashTable H,const void *key,void **keyRet,void **valRet) {
+ IntHashTable *h=(IntHashTable *)H;
+ return _hashtable_remove(h,key,keyRet,valRet,1);
+}
+
+int hashtable_delete(HashTable H,const void *key) {
+ IntHashTable *h=(IntHashTable *)H;
+ return _hashtable_delete(h,key,1);
+}
+
+void hashtable_rehash_compute(HashTable H,CollisionFunc cf) {
+ IntHashTable *h=(IntHashTable *)H;
+ _hashtable_rehash(h,cf,h->length);
+}
+
+void hashtable_rehash(HashTable H) {
+ IntHashTable *h=(IntHashTable *)H;
+ _hashtable_rehash(h,NULL,h->length);
+}
+
+int hashtable_lookup_or_insert(HashTable H,void *key,void **valp,void *val) {
+ IntHashTable *h=(IntHashTable *)H;
+ 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 i;
+
+ for (n=h->table[hash];n;n=n->next) {
+ i=h->cmpFunc((HashTable)h,n->key,key);
+ if (!i) {
+ *valp=n->value;
+ return 1;
+ } else if (i>0) {
+ break;
+ }
+ }
+ return 0;
+}
+
+unsigned long hashtable_get_count(const HashTable H) {
+ IntHashTable *h=(IntHashTable *)H;
+ return h->count;
+}
+
+void *hashtable_get_user_data(const HashTable H) {
+ IntHashTable *h=(IntHashTable *)H;
+ return h->userData;
+}
+
+void *hashtable_set_user_data(HashTable H,void *data) {
+ IntHashTable *h=(IntHashTable *)H;
+ void *r=h->userData;
+ h->userData=data;
+ return r;
+}
diff --git a/libImaging/QuantHash.h b/libImaging/QuantHash.h
new file mode 100644
index 000000000..b98b56eaa
--- /dev/null
+++ b/libImaging/QuantHash.h
@@ -0,0 +1,36 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * image quantizer
+ *
+ * Written by Toby J Sargeant .
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#ifndef __HASH_H__
+#define __HASH_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);
+
+#endif
diff --git a/libImaging/QuantHeap.c b/libImaging/QuantHeap.c
new file mode 100644
index 000000000..9332a5cd7
--- /dev/null
+++ b/libImaging/QuantHeap.c
@@ -0,0 +1,150 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * heap data type used by the image quantizer
+ *
+ * history:
+ * 98-09-10 tjs Contributed
+ * 98-12-29 fl Added to PIL 1.0b1
+ *
+ * Written by Toby J Sargeant .
+ *
+ * Copyright (c) 1998 by Toby J Sargeant
+ * Copyright (c) 1998 by Secret Labs AB
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include
+#include
+#include
+#include
+
+#include "QuantHash.h"
+#include "QuantDefines.h"
+
+typedef struct {
+ void **heap;
+ int heapsize;
+ int heapcount;
+ HeapCmpFunc cf;
+} IntHeap;
+
+#define INITIAL_SIZE 256
+
+#define DEBUG
+
+#ifdef DEBUG
+static int _heap_test(Heap);
+#endif
+
+void ImagingQuantHeapFree(Heap H) {
+ IntHeap *h=(IntHeap *)H;
+ free(h->heap);
+ free(h);
+}
+
+static int _heap_grow(IntHeap *h,int newsize) {
+ void *newheap;
+ if (!newsize) newsize=h->heapsize<<1;
+ if (newsizeheapsize) return 0;
+ newheap=malloc(sizeof(void *)*newsize);
+ if (!newheap) return 0;
+ memcpy(newheap,h->heap,sizeof(void *)*h->heapsize);
+ free(h->heap);
+ h->heap=newheap;
+ h->heapsize=newsize;
+ return 1;
+}
+
+#ifdef DEBUG
+static int _heap_test(Heap H) {
+ IntHeap *h=(IntHeap *)H;
+ int k;
+ for (k=1;k*2<=h->heapcount;k++) {
+ 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) {
+ printf ("heap is bad\n");
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+int ImagingQuantHeapRemove(Heap H,void **r) {
+ IntHeap *h=(IntHeap *)H;
+ int k,l;
+ void *v;
+
+ if (!h->heapcount) {
+ return 0;
+ }
+ *r=h->heap[1];
+ v=h->heap[h->heapcount--];
+ 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) {
+ l++;
+ }
+ }
+ 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); }
+#endif
+ return 1;
+}
+
+int ImagingQuantHeapAdd(Heap H,void *val) {
+ IntHeap *h=(IntHeap *)H;
+ 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) {
+ break;
+ }
+ h->heap[k]=h->heap[k/2];
+ k>>=1;
+ }
+ h->heap[k]=val;
+#ifdef DEBUG
+ 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;
+ if (!h->heapcount) {
+ return 0;
+ }
+ *r=h->heap[1];
+ return 1;
+}
+
+Heap *ImagingQuantHeapNew(HeapCmpFunc cf) {
+ IntHeap *h;
+
+ h=malloc(sizeof(IntHeap));
+ 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;
+}
+
diff --git a/libImaging/QuantHeap.h b/libImaging/QuantHeap.h
new file mode 100644
index 000000000..5a213c42a
--- /dev/null
+++ b/libImaging/QuantHeap.h
@@ -0,0 +1,23 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * image quantizer
+ *
+ * Written by Toby J Sargeant .
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#ifndef __HEAP_H__
+#define __HEAP_H__
+
+#include "QuantTypes.h"
+
+void ImagingQuantHeapFree(Heap);
+int ImagingQuantHeapRemove(Heap,void **);
+int ImagingQuantHeapAdd(Heap,void *);
+int ImagingQuantHeapTop(Heap,void **);
+Heap *ImagingQuantHeapNew(HeapCmpFunc);
+
+#endif
diff --git a/libImaging/QuantTypes.h b/libImaging/QuantTypes.h
new file mode 100644
index 000000000..308b51c13
--- /dev/null
+++ b/libImaging/QuantTypes.h
@@ -0,0 +1,28 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * image quantizer
+ *
+ * Written by Toby J Sargeant .
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#ifndef __TYPES_H__
+#define __TYPES_H__
+
+typedef void *HashTable;
+typedef void *Heap;
+
+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 *);
+
+#endif
diff --git a/libImaging/RankFilter.c b/libImaging/RankFilter.c
new file mode 100644
index 000000000..84090f751
--- /dev/null
+++ b/libImaging/RankFilter.c
@@ -0,0 +1,105 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * min, max, median filters
+ *
+ * history:
+ * 2002-06-08 fl Created
+ *
+ * Copyright (c) Secret Labs AB 2002. All rights reserved.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+/* Fast rank algorithm (due to Wirth), based on public domain code
+ by Nicolas Devillard, available at http://ndevilla.free.fr */
+
+#define SWAP(type,a,b) { register type t=(a);(a)=(b);(b)=t; }
+
+#define MakeRankFunction(type)\
+static type Rank##type(type a[], int n, int k)\
+{\
+ register int i, j, l, m;\
+ register type x;\
+ l = 0; m = n-1;\
+ while (l < m) {\
+ x = a[k];\
+ i = l;\
+ j = m;\
+ do {\
+ while (a[i] < x) i++;\
+ while (x < a[j]) j--;\
+ if (i <= j) {\
+ SWAP(type, a[i], a[j]);\
+ i++; j--;\
+ }\
+ } while (i <= j);\
+ if (j < k) l = i;\
+ if (k < i) m = j;\
+ }\
+ return a[k];\
+}
+
+MakeRankFunction(UINT8)
+MakeRankFunction(INT32)
+MakeRankFunction(FLOAT32)
+
+Imaging
+ImagingRankFilter(Imaging im, int size, int rank)
+{
+ Imaging imOut = NULL;
+ int x, y;
+ int i, margin, size2;
+
+ if (!im || im->bands != 1 || im->type == IMAGING_TYPE_SPECIAL)
+ return (Imaging) ImagingError_ModeError();
+
+ if (!(size & 1))
+ return (Imaging) ImagingError_ValueError("bad filter size");
+
+ size2 = size * size;
+ margin = (size-1) / 2;
+
+ if (rank < 0 || rank >= size2)
+ return (Imaging) ImagingError_ValueError("bad rank value");
+
+ imOut = ImagingNew(im->mode, im->xsize - 2*margin, im->ysize - 2*margin);
+ if (!imOut)
+ return NULL;
+
+#define RANK_BODY(type) do {\
+ type* buf = malloc(size2 * sizeof(type));\
+ if (!buf)\
+ goto nomemory;\
+ for (y = 0; y < imOut->ysize; y++)\
+ for (x = 0; x < imOut->xsize; x++) {\
+ for (i = 0; i < size; i++)\
+ memcpy(buf + i*size, &IMAGING_PIXEL_##type(im, x, y+i),\
+ size * sizeof(type));\
+ IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\
+ }\
+} while (0)
+
+ if (im->image8)
+ RANK_BODY(UINT8);
+ else if (im->type == IMAGING_TYPE_INT32)
+ RANK_BODY(INT32);
+ else if (im->type == IMAGING_TYPE_FLOAT32)
+ RANK_BODY(FLOAT32);
+ else {
+ /* safety net (we shouldn't end up here) */
+ ImagingDelete(imOut);
+ return (Imaging) ImagingError_ModeError();
+ }
+
+ ImagingCopyInfo(imOut, im);
+
+ return imOut;
+
+nomemory:
+ ImagingDelete(imOut);
+ return (Imaging) ImagingError_MemoryError();
+}
diff --git a/libImaging/Raw.h b/libImaging/Raw.h
new file mode 100644
index 000000000..4d28fa546
--- /dev/null
+++ b/libImaging/Raw.h
@@ -0,0 +1,15 @@
+/* Raw.h */
+
+typedef struct {
+
+ /* CONFIGURATION */
+
+ /* Distance between lines (0=no padding) */
+ int stride;
+
+ /* PRIVATE (initialized by decoder) */
+
+ /* Padding between lines */
+ int skip;
+
+} RAWSTATE;
diff --git a/libImaging/RawDecode.c b/libImaging/RawDecode.c
new file mode 100644
index 000000000..5aadb2b44
--- /dev/null
+++ b/libImaging/RawDecode.c
@@ -0,0 +1,89 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for raw (uncompressed) image data
+ *
+ * history:
+ * 96-03-07 fl rewritten
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#include "Raw.h"
+
+
+int
+ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ enum { LINE = 1, SKIP };
+ RAWSTATE* rawstate = state->context;
+
+ UINT8* ptr;
+
+ if (state->state == 0) {
+
+ /* Initialize context variables */
+
+ /* get size of image data and padding */
+ state->bytes = (state->xsize * state->bits + 7) / 8;
+ rawstate->skip = (rawstate->stride) ?
+ rawstate->stride - state->bytes : 0;
+
+ /* check image orientation */
+ if (state->ystep < 0) {
+ state->y = state->ysize-1;
+ state->ystep = -1;
+ } else
+ state->ystep = 1;
+
+ state->state = LINE;
+
+ }
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (state->state == SKIP) {
+
+ /* Skip padding between lines */
+
+ if (bytes < rawstate->skip)
+ return ptr - buf;
+
+ ptr += rawstate->skip;
+ bytes -= rawstate->skip;
+
+ state->state = LINE;
+
+ }
+
+ if (bytes < state->bytes)
+ return ptr - buf;
+
+ /* Unpack data */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, ptr, state->xsize);
+
+ ptr += state->bytes;
+ bytes -= state->bytes;
+
+ state->y += state->ystep;
+
+ if (state->y < 0 || state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+
+ state->state = SKIP;
+
+ }
+
+}
diff --git a/libImaging/RawEncode.c b/libImaging/RawEncode.c
new file mode 100644
index 000000000..a3b74b8cf
--- /dev/null
+++ b/libImaging/RawEncode.c
@@ -0,0 +1,89 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * coder for raw data
+ *
+ * FIXME: This encoder will fail if the buffer is not large enough to
+ * hold one full line of data. There's a workaround for this problem
+ * in ImageFile.py, but it should be solved here instead.
+ *
+ * history:
+ * 96-04-30 fl created
+ * 97-01-03 fl fixed padding
+ *
+ * Copyright (c) Fredrik Lundh 1996-97.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution. */
+
+
+#include "Imaging.h"
+
+int
+ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ UINT8* ptr;
+
+ if (!state->state) {
+
+ /* The "count" field holds the stride, if specified. Fix
+ things up so "bytes" is the full size, and "count" the
+ packed size */
+
+ if (state->count > 0) {
+ int bytes = state->count;
+
+ /* stride must not be less than real size */
+ if (state->count < state->bytes) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+ state->count = state->bytes;
+ state->bytes = bytes;
+ } else
+ state->count = state->bytes;
+
+ /* The "ystep" field specifies the orientation */
+
+ if (state->ystep < 0) {
+ state->y = state->ysize-1;
+ state->ystep = -1;
+ } else
+ state->ystep = 1;
+
+ state->state = 1;
+
+ }
+
+ if (bytes < state->bytes) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return 0;
+ }
+
+ ptr = buf;
+
+ while (bytes >= state->bytes) {
+
+ state->shuffle(ptr, (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize);
+
+ if (state->bytes > state->count)
+ /* zero-pad the buffer, if necessary */
+ memset(ptr + state->count, 0, state->bytes - state->count);
+
+ ptr += state->bytes;
+ bytes -= state->bytes;
+
+ state->y += state->ystep;
+
+ if (state->y < 0 || state->y >= state->ysize) {
+ state->errcode = IMAGING_CODEC_END;
+ break;
+ }
+
+ }
+
+ return ptr - buf;
+
+}
diff --git a/libImaging/Storage.c b/libImaging/Storage.c
new file mode 100644
index 000000000..76a66c2a5
--- /dev/null
+++ b/libImaging/Storage.c
@@ -0,0 +1,413 @@
+/*
+ * The Python Imaging Library
+ * $Id$
+ *
+ * imaging storage object
+ *
+ * This baseline implementation is designed to efficiently handle
+ * large images, provided they fit into the available memory.
+ *
+ * history:
+ * 1995-06-15 fl Created
+ * 1995-09-12 fl Updated API, compiles silently under ANSI C++
+ * 1995-11-26 fl Compiles silently under Borland 4.5 as well
+ * 1996-05-05 fl Correctly test status from Prologue
+ * 1997-05-12 fl Increased THRESHOLD (to speed up Tk interface)
+ * 1997-05-30 fl Added support for floating point images
+ * 1997-11-17 fl Added support for "RGBX" images
+ * 1998-01-11 fl Added support for integer images
+ * 1998-03-05 fl Exported Prologue/Epilogue functions
+ * 1998-07-01 fl Added basic "YCrCb" support
+ * 1998-07-03 fl Attach palette in prologue for "P" images
+ * 1998-07-09 hk Don't report MemoryError on zero-size images
+ * 1998-07-12 fl Change "YCrCb" to "YCbCr" (!)
+ * 1998-10-26 fl Added "I;16" and "I;16B" storage modes (experimental)
+ * 1998-12-29 fl Fixed allocation bug caused by previous fix
+ * 1999-02-03 fl Added "RGBa" and "BGR" modes (experimental)
+ * 2001-04-22 fl Fixed potential memory leak in ImagingCopyInfo
+ * 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) 1995-2005 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int ImagingNewCount = 0;
+
+/* --------------------------------------------------------------------
+ * Standard image object.
+ */
+
+Imaging
+ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize,
+ int size)
+{
+ Imaging im;
+ ImagingSectionCookie cookie;
+
+ im = (Imaging) calloc(1, size);
+ if (!im)
+ return (Imaging) ImagingError_MemoryError();
+
+ /* Setup image descriptor */
+ im->xsize = xsize;
+ im->ysize = ysize;
+
+ im->type = IMAGING_TYPE_UINT8;
+
+ if (strcmp(mode, "1") == 0) {
+ /* 1-bit images */
+ im->bands = im->pixelsize = 1;
+ im->linesize = xsize;
+
+ } else if (strcmp(mode, "P") == 0) {
+ /* 8-bit palette mapped images */
+ im->bands = im->pixelsize = 1;
+ im->linesize = xsize;
+ im->palette = ImagingPaletteNew("RGB");
+
+ } else if (strcmp(mode, "PA") == 0) {
+ /* 8-bit palette with alpha */
+ im->bands = 2;
+ im->pixelsize = 4; /* store in image32 memory */
+ im->linesize = xsize * 4;
+ im->palette = ImagingPaletteNew("RGB");
+
+ } else if (strcmp(mode, "L") == 0) {
+ /* 8-bit greyscale (luminance) images */
+ im->bands = im->pixelsize = 1;
+ im->linesize = xsize;
+
+ } else if (strcmp(mode, "LA") == 0) {
+ /* 8-bit greyscale (luminance) with alpha */
+ im->bands = 2;
+ im->pixelsize = 4; /* store in image32 memory */
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "F") == 0) {
+ /* 32-bit floating point images */
+ im->bands = 1;
+ im->pixelsize = 4;
+ im->linesize = xsize * 4;
+ im->type = IMAGING_TYPE_FLOAT32;
+
+ } else if (strcmp(mode, "I") == 0) {
+ /* 32-bit integer images */
+ im->bands = 1;
+ im->pixelsize = 4;
+ 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) {
+ /* EXPERIMENTAL */
+ /* 16-bit raw integer images */
+ im->bands = 1;
+ im->pixelsize = 2;
+ im->linesize = xsize * 2;
+ im->type = IMAGING_TYPE_SPECIAL;
+
+ } else if (strcmp(mode, "RGB") == 0) {
+ /* 24-bit true colour images */
+ im->bands = 3;
+ im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "BGR;15") == 0) {
+ /* EXPERIMENTAL */
+ /* 15-bit true colour */
+ im->bands = 1;
+ im->pixelsize = 2;
+ im->linesize = (xsize*2 + 3) & -4;
+ im->type = IMAGING_TYPE_SPECIAL;
+
+ } else if (strcmp(mode, "BGR;16") == 0) {
+ /* EXPERIMENTAL */
+ /* 16-bit reversed true colour */
+ im->bands = 1;
+ im->pixelsize = 2;
+ im->linesize = (xsize*2 + 3) & -4;
+ im->type = IMAGING_TYPE_SPECIAL;
+
+ } else if (strcmp(mode, "BGR;24") == 0) {
+ /* EXPERIMENTAL */
+ /* 24-bit reversed true colour */
+ im->bands = 1;
+ im->pixelsize = 3;
+ im->linesize = (xsize*3 + 3) & -4;
+ im->type = IMAGING_TYPE_SPECIAL;
+
+ } else if (strcmp(mode, "BGR;32") == 0) {
+ /* EXPERIMENTAL */
+ /* 32-bit reversed true colour */
+ im->bands = 1;
+ im->pixelsize = 4;
+ im->linesize = (xsize*4 + 3) & -4;
+ im->type = IMAGING_TYPE_SPECIAL;
+
+ } else if (strcmp(mode, "RGBX") == 0) {
+ /* 32-bit true colour images with padding */
+ im->bands = im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "RGBA") == 0) {
+ /* 32-bit true colour images with alpha */
+ im->bands = im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "RGBa") == 0) {
+ /* EXPERIMENTAL */
+ /* 32-bit true colour images with premultiplied alpha */
+ im->bands = im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "CMYK") == 0) {
+ /* 32-bit colour separation */
+ im->bands = im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else if (strcmp(mode, "YCbCr") == 0) {
+ /* 24-bit video format */
+ im->bands = 3;
+ im->pixelsize = 4;
+ im->linesize = xsize * 4;
+
+ } else {
+ free(im);
+ return (Imaging) ImagingError_ValueError("unrecognized mode");
+ }
+
+ /* Setup image descriptor */
+ strcpy(im->mode, mode);
+
+ ImagingSectionEnter(&cookie);
+
+ /* Pointer array (allocate at least one line, to avoid MemoryError
+ exceptions on platforms where calloc(0, x) returns NULL) */
+ im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *));
+
+ ImagingSectionLeave(&cookie);
+
+ if (!im->image) {
+ free(im);
+ return (Imaging) ImagingError_MemoryError();
+ }
+
+ ImagingNewCount++;
+
+ return im;
+}
+
+Imaging
+ImagingNewPrologue(const char *mode, unsigned xsize, unsigned ysize)
+{
+ return ImagingNewPrologueSubtype(
+ mode, xsize, ysize, sizeof(struct ImagingMemoryInstance)
+ );
+}
+
+Imaging
+ImagingNewEpilogue(Imaging im)
+{
+ /* If the raster data allocator didn't setup a destructor,
+ assume that it couldn't allocate the required amount of
+ memory. */
+ if (!im->destroy)
+ 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;
+ case 4:
+ im->image32 = (INT32 **) im->image;
+ break;
+ }
+
+ return im;
+}
+
+void
+ImagingDelete(Imaging im)
+{
+ if (!im)
+ return;
+
+ if (im->palette)
+ ImagingPaletteDelete(im->palette);
+
+ if (im->destroy)
+ im->destroy(im);
+
+ if (im->image)
+ free(im->image);
+
+ free(im);
+}
+
+
+/* Array Storage Type */
+/* ------------------ */
+/* Allocate image as an array of line buffers. */
+
+static void
+ImagingDestroyArray(Imaging im)
+{
+ int y;
+
+ if (im->image)
+ for (y = 0; y < im->ysize; y++)
+ if (im->image[y])
+ free(im->image[y]);
+}
+
+Imaging
+ImagingNewArray(const char *mode, int xsize, int ysize)
+{
+ Imaging im;
+ ImagingSectionCookie cookie;
+
+ int y;
+ char* p;
+
+ im = ImagingNewPrologue(mode, xsize, ysize);
+ if (!im)
+ 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;
+ }
+ im->image[y] = p;
+ }
+
+ ImagingSectionLeave(&cookie);
+
+ if (y == im->ysize)
+ im->destroy = ImagingDestroyArray;
+
+ return ImagingNewEpilogue(im);
+}
+
+
+/* Block Storage Type */
+/* ------------------ */
+/* Allocate image as a single block. */
+
+static void
+ImagingDestroyBlock(Imaging im)
+{
+ if (im->block)
+ free(im->block);
+}
+
+Imaging
+ImagingNewBlock(const char *mode, int xsize, int ysize)
+{
+ Imaging im;
+ int y, i;
+ int bytes;
+
+ im = ImagingNewPrologue(mode, xsize, ysize);
+ if (!im)
+ return NULL;
+
+ /* Use a single block */
+ bytes = im->ysize * im->linesize;
+ if (bytes <= 0)
+ /* some platforms return NULL for malloc(0); this fix
+ prevents MemoryError on zero-sized images on such
+ platforms */
+ bytes = 1;
+ im->block = (char *) malloc(bytes);
+
+ if (im->block) {
+
+ for (y = i = 0; y < im->ysize; y++) {
+ im->image[y] = im->block + i;
+ i += im->linesize;
+ }
+
+ im->destroy = ImagingDestroyBlock;
+
+ }
+
+ return ImagingNewEpilogue(im);
+}
+
+/* --------------------------------------------------------------------
+ * Create a new, internally allocated, image.
+ */
+#if defined(IMAGING_SMALL_MODEL)
+#define THRESHOLD 16384L
+#else
+#define THRESHOLD (2048*2048*4L)
+#endif
+
+Imaging
+ImagingNew(const char* mode, int xsize, int ysize)
+{
+ int bytes;
+ Imaging im;
+
+ if (strlen(mode) == 1) {
+ if (mode[0] == 'F' || mode[0] == 'I')
+ bytes = 4;
+ else
+ bytes = 1;
+ } else
+ bytes = strlen(mode); /* close enough */
+
+ if ((long) xsize * ysize * bytes <= THRESHOLD) {
+ im = ImagingNewBlock(mode, xsize, ysize);
+ if (im)
+ return im;
+ /* assume memory error; try allocating in array mode instead */
+ ImagingError_Clear();
+ }
+
+ return ImagingNewArray(mode, xsize, ysize);
+}
+
+Imaging
+ImagingNew2(const char* mode, Imaging imOut, Imaging imIn)
+{
+ /* allocate or validate output image */
+
+ if (imOut) {
+ /* make sure images match */
+ if (strcmp(imOut->mode, mode) != 0
+ || imOut->xsize != imIn->xsize
+ || imOut->ysize != imIn->ysize) {
+ return ImagingError_Mismatch();
+ }
+ } else {
+ /* create new image */
+ imOut = ImagingNew(mode, imIn->xsize, imIn->ysize);
+ if (!imOut)
+ return NULL;
+ }
+
+ return imOut;
+}
+
+void
+ImagingCopyInfo(Imaging destination, Imaging source)
+{
+ if (source->palette) {
+ if (destination->palette)
+ ImagingPaletteDelete(destination->palette);
+ destination->palette = ImagingPaletteDuplicate(source->palette);
+ }
+}
diff --git a/libImaging/SunRleDecode.c b/libImaging/SunRleDecode.c
new file mode 100644
index 000000000..bf34d9382
--- /dev/null
+++ b/libImaging/SunRleDecode.c
@@ -0,0 +1,112 @@
+/*
+ * THIS IS WORK IN PROGRESS
+ *
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for SUN RLE data.
+ *
+ * history:
+ * 97-01-04 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1997.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ int n;
+ UINT8* ptr;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (bytes < 1)
+ return ptr - buf;
+
+ if (ptr[0] == 0x80) {
+
+ if (bytes < 2)
+ break;
+
+ n = ptr[1];
+
+ if (n == 0) {
+
+ /* Literal 0x80 (2 bytes) */
+ n = 1;
+
+ state->buffer[state->x] = 0x80;
+
+ ptr += 2;
+ bytes -= 2;
+
+ } else {
+
+ /* Run (3 bytes) */
+ if (bytes < 3)
+ break;
+
+ if (state->x + n > state->bytes) {
+ /* FIXME: is this correct? */
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ memset(state->buffer + state->x, ptr[2], n);
+
+ ptr += 3;
+ bytes -= 3;
+
+ }
+
+ } else {
+
+ /* Literal (1+n bytes block) */
+ n = ptr[0];
+
+ if (bytes < 1 + n)
+ break;
+
+ if (state->x + n > state->bytes) {
+ /* FIXME: is this correct? */
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ memcpy(state->buffer + state->x, ptr + 1, n);
+
+ ptr += 1 + n;
+ bytes -= 1 + n;
+
+ }
+
+ state->x += n;
+
+ if (state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/TgaRleDecode.c b/libImaging/TgaRleDecode.c
new file mode 100644
index 000000000..cad6bc3bc
--- /dev/null
+++ b/libImaging/TgaRleDecode.c
@@ -0,0 +1,118 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for Targa RLE data.
+ *
+ * history:
+ * 97-01-04 fl created
+ * 98-09-11 fl don't one byte per pixel; take orientation into account
+ *
+ * Copyright (c) Fredrik Lundh 1997.
+ * Copyright (c) Secret Labs AB 1997-98.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingTgaRleDecode(Imaging im, ImagingCodecState state,
+ UINT8* buf, int bytes)
+{
+ int n, depth;
+ UINT8* ptr;
+
+ ptr = buf;
+
+ if (state->state == 0) {
+
+ /* check image orientation */
+ if (state->ystep < 0) {
+ state->y = state->ysize-1;
+ state->ystep = -1;
+ } else
+ state->ystep = 1;
+
+ state->state = 1;
+
+ }
+
+ depth = state->count;
+
+ for (;;) {
+
+ if (bytes < 1)
+ return ptr - buf;
+
+ if (ptr[0] & 0x80) {
+
+ /* Run (1 + pixelsize bytes) */
+
+ if (bytes < 1 + depth)
+ break;
+
+ n = depth * ((ptr[0] & 0x7f) + 1);
+
+ if (state->x + n > state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ if (depth == 1)
+ memset(state->buffer + state->x, ptr[1], n);
+ else {
+ int i;
+ for (i = 0; i < n; i += depth)
+ memcpy(state->buffer + state->x + i, ptr+1, depth);
+ }
+
+ ptr += 1 + depth;
+ bytes -= 1 + depth;
+
+ } else {
+
+ /* Literal (1+n+1 bytes block) */
+ n = depth * (ptr[0] + 1);
+
+ if (bytes < 1 + n)
+ break;
+
+ if (state->x + n > state->bytes) {
+ state->errcode = IMAGING_CODEC_OVERRUN;
+ return -1;
+ }
+
+ memcpy(state->buffer + state->x, ptr + 1, n);
+
+ ptr += 1 + n;
+ bytes -= 1 + n;
+
+ }
+
+ state->x += n;
+
+ if (state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ state->y += state->ystep;
+
+ if (state->y < 0 || state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+
+ }
+
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c
new file mode 100644
index 000000000..90a76feb3
--- /dev/null
+++ b/libImaging/Unpack.c
@@ -0,0 +1,1028 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * code to unpack raw data from various file formats
+ *
+ * history:
+ * 1996-03-07 fl Created (from various decoders)
+ * 1996-04-19 fl Added band unpackers
+ * 1996-05-12 fl Published RGB unpackers
+ * 1996-05-27 fl Added nibble unpacker
+ * 1996-12-10 fl Added complete set of PNG unpackers
+ * 1996-12-29 fl Set alpha byte in RGB unpackers
+ * 1997-01-05 fl Added remaining TGA unpackers
+ * 1997-01-18 fl Added inverting band unpackers
+ * 1997-01-25 fl Added FlashPix unpackers
+ * 1997-05-31 fl Added floating point unpackers
+ * 1998-02-08 fl Added I unpacker
+ * 1998-07-01 fl Added YCbCr unpacker
+ * 1998-07-02 fl Added full set of integer unpackers
+ * 1998-12-29 fl Added mode field, I;16 unpackers
+ * 1998-12-30 fl Added RGBX modes
+ * 1999-02-04 fl Fixed I;16 unpackers
+ * 2003-05-13 fl Added L/RGB reversed unpackers
+ * 2003-09-26 fl Added LA/PA and RGBa->RGB unpackers
+ *
+ * Copyright (c) 1997-2003 by Secret Labs AB.
+ * Copyright (c) 1996-1997 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Imaging.h"
+
+
+#define R 0
+#define G 1
+#define B 2
+#define X 3
+
+#define A 3
+
+#define C 0
+#define M 1
+#define Y 2
+#define K 3
+
+#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255)
+
+/* byte-swapping macros */
+
+#define C16N\
+ (tmp[0]=in[0], tmp[1]=in[1]);
+#define C16S\
+ (tmp[1]=in[0], tmp[0]=in[1]);
+#define C32N\
+ (tmp[0]=in[0], tmp[1]=in[1], tmp[2]=in[2], tmp[3]=in[3]);
+#define C32S\
+ (tmp[3]=in[0], tmp[2]=in[1], tmp[1]=in[2], tmp[0]=in[3]);
+#define C64N\
+ (tmp[0]=in[0], tmp[1]=in[1], tmp[2]=in[2], tmp[3]=in[3],\
+ tmp[4]=in[4], tmp[5]=in[5], tmp[6]=in[6], tmp[7]=in[7]);
+#define C64S\
+ (tmp[7]=in[0], tmp[6]=in[1], tmp[5]=in[2], tmp[4]=in[3],\
+ tmp[3]=in[4], tmp[2]=in[5], tmp[1]=in[6], tmp[0]=in[7]);
+
+#ifdef WORDS_BIGENDIAN
+#define C16B C16N
+#define C16L C16S
+#define C32B C32N
+#define C32L C32S
+#define C64B C64N
+#define C64L C64S
+#else
+#define C16B C16S
+#define C16L C16N
+#define C32B C32S
+#define C32L C32N
+#define C64B C64S
+#define C64L C64N
+#endif
+
+/* bit-swapping */
+
+static UINT8 BITFLIP[] = {
+ 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112,
+ 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184,
+ 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52,
+ 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220,
+ 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82,
+ 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154,
+ 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22,
+ 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238,
+ 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97,
+ 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169,
+ 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37,
+ 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205,
+ 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67,
+ 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139,
+ 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7,
+ 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
+ 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127,
+ 255
+};
+
+/* Unpack to "1" image */
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+
+/* Unpack to "L" image */
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+unpackLI(UINT8* out, const UINT8* in, int pixels)
+{
+ /* negative */
+ int i;
+ for (i = 0; i < pixels; i++)
+ out[i] = ~in[i];
+}
+
+static void
+unpackLR(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* RGB, bit reversed */
+ for (i = 0; i < pixels; i++) {
+ out[i] = BITFLIP[in[i]];
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+
+/* Unpack to "P" image */
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+unpackP2L(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, j, m, s;
+ /* bit layers */
+ 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++;
+ }
+ }
+}
+
+static void
+unpackP4L(UINT8* out, const UINT8* in, int pixels)
+{
+ int i, j, m, s;
+ /* bit layers (trust the optimizer ;-) */
+ 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++;
+ }
+ }
+}
+
+/* Unpack to "RGB" image */
+
+void
+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;
+ }
+}
+
+void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+void
+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;
+ }
+}
+
+void
+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;
+ }
+}
+
+void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+/* Unpack to "RGBA" image */
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+unpackRGBa(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* premultiplied RGBA */
+ for (i = 0; i < pixels; i++) {
+ int a = in[3];
+ if (!a)
+ out[R] = out[G] = out[B] = out[A] = 0;
+ else {
+ out[R] = CLIP(in[0] * 255 / a);
+ out[G] = CLIP(in[1] * 255 / a);
+ out[B] = CLIP(in[2] * 255 / a);
+ out[A] = a;
+ }
+ out += 4; in += 4;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+unpackRGBAL(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+
+ /* 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;
+ }
+}
+
+void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+
+/* Unpack to "CMYK" image */
+
+static void
+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;
+ }
+}
+
+static void
+copy1(UINT8* out, const UINT8* in, int pixels)
+{
+ /* L, P */
+ memcpy(out, in, pixels);
+}
+
+static void
+copy2(UINT8* out, const UINT8* in, int pixels)
+{
+ /* I;16 */
+ memcpy(out, in, pixels*2);
+}
+
+static void
+copy4(UINT8* out, const UINT8* in, int pixels)
+{
+ /* RGBA, CMYK quadruples */
+ memcpy(out, in, 4 * pixels);
+}
+
+
+/* Unpack to "I" and "F" images */
+
+#define UNPACK_RAW(NAME, GET, INTYPE, OUTTYPE)\
+static void NAME(UINT8* out_, const UINT8* in, int pixels)\
+{\
+ int i;\
+ OUTTYPE* out = (OUTTYPE*) out_;\
+ for (i = 0; i < pixels; i++, in += sizeof(INTYPE))\
+ out[i] = (OUTTYPE) ((INTYPE) GET);\
+}
+
+#define UNPACK(NAME, COPY, INTYPE, OUTTYPE)\
+static void NAME(UINT8* out_, const UINT8* in, int pixels)\
+{\
+ int i;\
+ OUTTYPE* out = (OUTTYPE*) out_;\
+ INTYPE tmp_;\
+ UINT8* tmp = (UINT8*) &tmp_;\
+ for (i = 0; i < pixels; i++, in += sizeof(INTYPE)) {\
+ COPY;\
+ out[i] = (OUTTYPE) tmp_;\
+ }\
+}
+
+UNPACK_RAW(unpackI8, in[0], UINT8, INT32)
+UNPACK_RAW(unpackI8S, in[0], INT8, INT32)
+UNPACK(unpackI16, C16L, UINT16, INT32)
+UNPACK(unpackI16S, C16L, INT16, INT32)
+UNPACK(unpackI16B, C16B, UINT16, INT32)
+UNPACK(unpackI16BS, C16B, INT16, INT32)
+UNPACK(unpackI16N, C16N, UINT16, INT32)
+UNPACK(unpackI16NS, C16N, INT16, INT32)
+UNPACK(unpackI32, C32L, UINT32, INT32)
+UNPACK(unpackI32S, C32L, INT32, INT32)
+UNPACK(unpackI32B, C32B, UINT32, INT32)
+UNPACK(unpackI32BS, C32B, INT32, INT32)
+UNPACK(unpackI32N, C32N, UINT32, INT32)
+UNPACK(unpackI32NS, C32N, INT32, INT32)
+
+UNPACK_RAW(unpackF8, in[0], UINT8, FLOAT32)
+UNPACK_RAW(unpackF8S, in[0], INT8, FLOAT32)
+UNPACK(unpackF16, C16L, UINT16, FLOAT32)
+UNPACK(unpackF16S, C16L, INT16, FLOAT32)
+UNPACK(unpackF16B, C16B, UINT16, FLOAT32)
+UNPACK(unpackF16BS, C16B, INT16, FLOAT32)
+UNPACK(unpackF16N, C16N, UINT16, FLOAT32)
+UNPACK(unpackF16NS, C16N, INT16, FLOAT32)
+UNPACK(unpackF32, C32L, UINT32, FLOAT32)
+UNPACK(unpackF32S, C32L, INT32, FLOAT32)
+UNPACK(unpackF32B, C32B, UINT32, FLOAT32)
+UNPACK(unpackF32BS, C32B, INT32, FLOAT32)
+UNPACK(unpackF32N, C32N, UINT32, FLOAT32)
+UNPACK(unpackF32NS, C32N, INT32, FLOAT32)
+UNPACK(unpackF32F, C32L, FLOAT32, FLOAT32)
+UNPACK(unpackF32BF, C32B, FLOAT32, FLOAT32)
+UNPACK(unpackF32NF, C32N, FLOAT32, FLOAT32)
+#ifdef FLOAT64
+UNPACK(unpackF64F, C64L, FLOAT64, FLOAT32)
+UNPACK(unpackF64BF, C64B, FLOAT64, FLOAT32)
+UNPACK(unpackF64NF, C64N, FLOAT64, FLOAT32)
+#endif
+
+
+/* Misc. unpackers */
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static void
+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;
+ }
+}
+
+static struct {
+ const char* mode;
+ const char* rawmode;
+ int bits;
+ ImagingShuffler unpack;
+} unpackers[] = {
+
+ /* raw mode syntax is ";" where "bits" defaults
+ depending on mode (1 for "1", 8 for "P" and "L", etc), and
+ "flags" should be given in alphabetical order. if both bits
+ and flags have their default values, the ; should be left out */
+
+ /* flags: "I" inverted data; "R" reversed bit order; "B" big
+ endian byte order (default is little endian); "L" line
+ 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},
+
+ /* 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},
+
+ /* greyscale w. alpha */
+ {"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},
+
+ /* palette w. alpha */
+ {"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},
+
+ /* 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},
+
+ /* 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},
+
+ /* 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},
+
+ /* video (YCbCr) */
+ {"YCbCr", "YCbCr", 24, ImagingUnpackRGB},
+ {"YCbCr", "YCbCr;L", 24, unpackRGBL},
+ {"YCbCr", "YCbCrX", 32, copy4},
+ {"YCbCr", "YCbCrK", 32, copy4},
+
+ /* 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},
+
+ /* 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},
+#ifdef FLOAT64
+ {"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},
+
+ {NULL} /* sentinel */
+};
+
+
+ImagingShuffler
+ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out)
+{
+ int i;
+
+ /* find a suitable pixel unpacker */
+ for (i = 0; unpackers[i].rawmode; i++)
+ 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;
+ }
+
+ /* FIXME: configure a general unpacker based on the type codes... */
+
+ return NULL;
+}
diff --git a/libImaging/UnpackYCC.c b/libImaging/UnpackYCC.c
new file mode 100644
index 000000000..19da1f654
--- /dev/null
+++ b/libImaging/UnpackYCC.c
@@ -0,0 +1,162 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * code to convert and unpack PhotoYCC data
+ *
+ * history:
+ * 97-01-25 fl Moved from PcdDecode.c
+ *
+ * Copyright (c) Fredrik Lundh 1996-97.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+/* Tables generated by pcdtables.py, based on transforms taken from
+ the "Colour Space Conversions FAQ" by Roberts/Ford. */
+
+static INT16 L[] = { 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18,
+19, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41,
+42, 43, 45, 46, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 62, 64,
+65, 67, 68, 69, 71, 72, 73, 75, 76, 77, 79, 80, 82, 83, 84, 86, 87,
+88, 90, 91, 92, 94, 95, 96, 98, 99, 101, 102, 103, 105, 106, 107, 109,
+110, 111, 113, 114, 115, 117, 118, 120, 121, 122, 124, 125, 126, 128,
+129, 130, 132, 133, 134, 136, 137, 139, 140, 141, 143, 144, 145, 147,
+148, 149, 151, 152, 153, 155, 156, 158, 159, 160, 162, 163, 164, 166,
+167, 168, 170, 171, 173, 174, 175, 177, 178, 179, 181, 182, 183, 185,
+186, 187, 189, 190, 192, 193, 194, 196, 197, 198, 200, 201, 202, 204,
+205, 206, 208, 209, 211, 212, 213, 215, 216, 217, 219, 220, 221, 223,
+224, 225, 227, 228, 230, 231, 232, 234, 235, 236, 238, 239, 240, 242,
+243, 245, 246, 247, 249, 250, 251, 253, 254, 255, 257, 258, 259, 261,
+262, 264, 265, 266, 268, 269, 270, 272, 273, 274, 276, 277, 278, 280,
+281, 283, 284, 285, 287, 288, 289, 291, 292, 293, 295, 296, 297, 299,
+300, 302, 303, 304, 306, 307, 308, 310, 311, 312, 314, 315, 317, 318,
+319, 321, 322, 323, 325, 326, 327, 329, 330, 331, 333, 334, 336, 337,
+338, 340, 341, 342, 344, 345, 346 };
+
+static INT16 CB[] = { -345, -343, -341, -338, -336, -334, -332, -329,
+-327, -325, -323, -321, -318, -316, -314, -312, -310, -307, -305,
+-303, -301, -298, -296, -294, -292, -290, -287, -285, -283, -281,
+-278, -276, -274, -272, -270, -267, -265, -263, -261, -258, -256,
+-254, -252, -250, -247, -245, -243, -241, -239, -236, -234, -232,
+-230, -227, -225, -223, -221, -219, -216, -214, -212, -210, -207,
+-205, -203, -201, -199, -196, -194, -192, -190, -188, -185, -183,
+-181, -179, -176, -174, -172, -170, -168, -165, -163, -161, -159,
+-156, -154, -152, -150, -148, -145, -143, -141, -139, -137, -134,
+-132, -130, -128, -125, -123, -121, -119, -117, -114, -112, -110,
+-108, -105, -103, -101, -99, -97, -94, -92, -90, -88, -85, -83, -81,
+-79, -77, -74, -72, -70, -68, -66, -63, -61, -59, -57, -54, -52, -50,
+-48, -46, -43, -41, -39, -37, -34, -32, -30, -28, -26, -23, -21, -19,
+-17, -15, -12, -10, -8, -6, -3, -1, 0, 2, 4, 7, 9, 11, 13, 16, 18, 20,
+22, 24, 27, 29, 31, 33, 35, 38, 40, 42, 44, 47, 49, 51, 53, 55, 58,
+60, 62, 64, 67, 69, 71, 73, 75, 78, 80, 82, 84, 86, 89, 91, 93, 95,
+98, 100, 102, 104, 106, 109, 111, 113, 115, 118, 120, 122, 124, 126,
+129, 131, 133, 135, 138, 140, 142, 144, 146, 149, 151, 153, 155, 157,
+160, 162, 164, 166, 169, 171, 173, 175, 177, 180, 182, 184, 186, 189,
+191, 193, 195, 197, 200, 202, 204, 206, 208, 211, 213, 215, 217, 220 };
+
+static INT16 GB[] = { 67, 67, 66, 66, 65, 65, 65, 64, 64, 63, 63, 62,
+62, 62, 61, 61, 60, 60, 59, 59, 59, 58, 58, 57, 57, 56, 56, 56, 55,
+55, 54, 54, 53, 53, 52, 52, 52, 51, 51, 50, 50, 49, 49, 49, 48, 48,
+47, 47, 46, 46, 46, 45, 45, 44, 44, 43, 43, 43, 42, 42, 41, 41, 40,
+40, 40, 39, 39, 38, 38, 37, 37, 37, 36, 36, 35, 35, 34, 34, 34, 33,
+33, 32, 32, 31, 31, 31, 30, 30, 29, 29, 28, 28, 28, 27, 27, 26, 26,
+25, 25, 25, 24, 24, 23, 23, 22, 22, 22, 21, 21, 20, 20, 19, 19, 19,
+18, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11,
+11, 10, 10, 9, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 3, 3, 2, 2,
+1, 1, 0, 0, 0, 0, 0, -1, -1, -2, -2, -2, -3, -3, -4, -4, -5, -5, -5,
+-6, -6, -7, -7, -8, -8, -8, -9, -9, -10, -10, -11, -11, -11, -12, -12,
+-13, -13, -14, -14, -14, -15, -15, -16, -16, -17, -17, -18, -18, -18,
+-19, -19, -20, -20, -21, -21, -21, -22, -22, -23, -23, -24, -24, -24,
+-25, -25, -26, -26, -27, -27, -27, -28, -28, -29, -29, -30, -30, -30,
+-31, -31, -32, -32, -33, -33, -33, -34, -34, -35, -35, -36, -36, -36,
+-37, -37, -38, -38, -39, -39, -39, -40, -40, -41, -41, -42 };
+
+static INT16 CR[] = { -249, -247, -245, -243, -241, -239, -238, -236,
+-234, -232, -230, -229, -227, -225, -223, -221, -219, -218, -216,
+-214, -212, -210, -208, -207, -205, -203, -201, -199, -198, -196,
+-194, -192, -190, -188, -187, -185, -183, -181, -179, -178, -176,
+-174, -172, -170, -168, -167, -165, -163, -161, -159, -157, -156,
+-154, -152, -150, -148, -147, -145, -143, -141, -139, -137, -136,
+-134, -132, -130, -128, -127, -125, -123, -121, -119, -117, -116,
+-114, -112, -110, -108, -106, -105, -103, -101, -99, -97, -96, -94,
+-92, -90, -88, -86, -85, -83, -81, -79, -77, -76, -74, -72, -70, -68,
+-66, -65, -63, -61, -59, -57, -55, -54, -52, -50, -48, -46, -45, -43,
+-41, -39, -37, -35, -34, -32, -30, -28, -26, -25, -23, -21, -19, -17,
+-15, -14, -12, -10, -8, -6, -4, -3, -1, 0, 2, 4, 5, 7, 9, 11, 13, 15,
+16, 18, 20, 22, 24, 26, 27, 29, 31, 33, 35, 36, 38, 40, 42, 44, 46,
+47, 49, 51, 53, 55, 56, 58, 60, 62, 64, 66, 67, 69, 71, 73, 75, 77,
+78, 80, 82, 84, 86, 87, 89, 91, 93, 95, 97, 98, 100, 102, 104, 106,
+107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 129, 131,
+133, 135, 137, 138, 140, 142, 144, 146, 148, 149, 151, 153, 155, 157,
+158, 160, 162, 164, 166, 168, 169, 171, 173, 175, 177, 179, 180, 182,
+184, 186, 188, 189, 191, 193, 195, 197, 199, 200, 202, 204, 206, 208,
+209, 211, 213, 215 };
+
+static INT16 GR[] = { 127, 126, 125, 124, 123, 122, 121, 121, 120, 119,
+118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 108, 107, 106,
+105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 95, 94, 93, 92, 91,
+90, 89, 88, 87, 86, 85, 84, 83, 83, 82, 81, 80, 79, 78, 77, 76, 75,
+74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59,
+58, 57, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 45, 44,
+43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 32, 31, 30, 29, 28,
+27, 26, 25, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12,
+11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2, -3, -4, -5, -5,
+-6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19,
+-20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -31, -32,
+-33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45,
+-46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58,
+-59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -69, -70, -71,
+-72, -73, -74, -75, -76, -77, -78, -79, -80, -81, -82, -82, -83, -84,
+-85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -94, -95, -96, -97,
+-98, -99, -100, -101, -102, -103, -104, -105, -106, -107, -107, -108 };
+
+#define R 0
+#define G 1
+#define B 2
+#define A 3
+
+#define YCC2RGB(rgb, y, cb, cr) {\
+ int l = L[y];\
+ int r = l + CR[cr];\
+ int g = l + GR[cr] + GB[cb];\
+ int b = l + CB[cb];\
+ rgb[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r;\
+ rgb[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g;\
+ rgb[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b;\
+}
+
+void
+ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* PhotoYCC triplets */
+ for (i = 0; i < pixels; i++) {
+ YCC2RGB(out, in[0], in[1], in[2]);
+ out[A] = 255;
+ out += 4; in += 3;
+ }
+}
+
+void
+ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels)
+{
+ int i;
+ /* PhotoYCC triplets plus premultiplied alpha */
+ for (i = 0; i < pixels; i++) {
+ /* Divide by alpha */
+ UINT8 rgb[3];
+ rgb[0] = (in[3] == 0) ? 0 : (((int) in[0] * 255) / in[3]);
+ rgb[1] = (in[3] == 0) ? 0 : (((int) in[1] * 255) / in[3]);
+ rgb[2] = (in[3] == 0) ? 0 : (((int) in[2] * 255) / in[3]);
+ /* Convert non-multiplied data to RGB */
+ YCC2RGB(out, rgb[0], rgb[1], rgb[2]);
+ out[A] = in[3];
+ out += 4; in += 4;
+ }
+}
diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c
new file mode 100644
index 000000000..231826245
--- /dev/null
+++ b/libImaging/UnsharpMask.c
@@ -0,0 +1,398 @@
+/* PILusm, a gaussian blur and unsharp masking library for PIL
+ By Kevin Cazabon, copyright 2003
+ kevin_cazabon@hotmail.com
+ kevin@cazabon.com */
+
+/* Originally released under LGPL. Graciously donated to PIL
+ for distribution under the standard PIL license in 2009." */
+
+#include "Python.h"
+#include "Imaging.h"
+
+#define PILUSMVERSION "0.6.1"
+
+/* version history
+
+0.6.1 converted to C and added to PIL 1.1.7
+
+0.6.0 fixed/improved float radius support (oops!)
+ now that radius can be a float (properly), changed radius value to
+ be an actual radius (instead of diameter). So, you should get
+ similar results from PIL_usm as from other paint programs when
+ using the SAME values (no doubling of radius required any more).
+ Be careful, this may "break" software if you had it set for 2x
+ or 5x the radius as was recommended with earlier versions.
+ made PILusm thread-friendly (release GIL before lengthly operations,
+ and re-acquire it before returning to Python). This makes a huge
+ difference with multi-threaded applications on dual-processor
+ or "Hyperthreading"-enabled systems (Pentium4, Xeon, etc.)
+
+0.5.0 added support for float radius values!
+
+0.4.0 tweaked gaussian curve calculation to be closer to consistent shape
+ across a wide range of radius values
+
+0.3.0 changed deviation calculation in gausian algorithm to be dynamic
+ _gblur now adds 1 to the user-supplied radius before using it so
+ that a value of "0" returns the original image instead of a
+ black one.
+ fixed handling of alpha channel in RGBX, RGBA images
+ improved speed of gblur by reducing unnecessary checks and assignments
+
+0.2.0 fixed L-mode image support
+
+0.1.0 initial release
+
+*/
+
+static inline UINT8 clip(double in)
+{
+ if (in >= 255.0)
+ return (UINT8) 255;
+ if (in <= 0.0)
+ return (UINT8) 0;
+ return (UINT8) in;
+}
+
+static Imaging
+gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding)
+{
+ ImagingSectionCookie cookie;
+
+ float *maskData = NULL;
+ int y = 0;
+ int x = 0;
+ float z = 0;
+ float sum = 0.0;
+ float dev = 0.0;
+
+ float *buffer = NULL;
+
+ int *line = NULL;
+ UINT8 *line8 = NULL;
+
+ int pix = 0;
+ float newPixel[4];
+ int channel = 0;
+ int offset = 0;
+ INT32 newPixelFinals;
+
+ int radius = 0;
+ float remainder = 0.0;
+
+ int i;
+
+ /* Do the gaussian blur */
+
+ /* For a symmetrical gaussian blur, instead of doing a radius*radius
+ matrix lookup, you get the EXACT same results by doing a radius*1
+ transform, followed by a 1*radius transform. This reduces the
+ number of lookups exponentially (10 lookups per pixel for a
+ radius of 5 instead of 25 lookups). So, we blur the lines first,
+ then we blur the resulting columns. */
+
+ /* first, round radius off to the next higher integer and hold the
+ remainder this is used so we can support float radius values
+ properly. */
+
+ remainder = floatRadius - ((int) floatRadius);
+ floatRadius = ceil(floatRadius);
+
+ /* Next, double the radius and offset by 2.0... that way "0" returns
+ the original image instead of a black one. We multiply it by 2.0
+ so that it is a true "radius", not a diameter (the results match
+ other paint programs closer that way too). */
+ radius = (int) ((floatRadius * 2.0) + 2.0);
+
+ /* create the maskData for the gaussian curve */
+ maskData = malloc(radius * sizeof(float));
+ /* FIXME: error checking */
+ for (x = 0; x < radius; x++) {
+ z = ((float) (x + 2) / ((float) radius));
+ dev = 0.5 + (((float) (radius * radius)) * 0.001);
+ /* you can adjust this factor to change the shape/center-weighting
+ of the gaussian */
+ maskData[x] = (float) pow((1.0 / sqrt(2.0 * 3.14159265359 * dev)),
+ ((-(z - 1.0) * -(x - 1.0)) /
+ (2.0 * dev)));
+ }
+
+ /* if there's any remainder, multiply the first/last values in
+ MaskData it. this allows us to support float radius values. */
+ if (remainder > 0.0) {
+ maskData[0] *= remainder;
+ maskData[radius - 1] *= remainder;
+ }
+
+ for (x = 0; x < radius; x++) {
+ /* this is done separately now due to the correction for float
+ radius values above */
+ sum += maskData[x];
+ }
+
+ for (i = 0; i < radius; i++) {
+ maskData[i] *= (1.0 / sum);
+ /* printf("%f\n", maskData[i]); */
+ }
+
+ /* create a temporary memory buffer for the data for the first pass
+ memset the buffer to 0 so we can use it directly with += */
+
+ /* don't bother about alpha/padding */
+ buffer = calloc((size_t) (im->xsize * im->ysize * channels),
+ sizeof(float));
+ if (buffer == NULL)
+ return ImagingError_MemoryError();
+
+ /* be nice to other threads while you go off to lala land */
+ ImagingSectionEnter(&cookie);
+
+ /* memset(buffer, 0, sizeof(buffer)); */
+
+ newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0;
+
+ /* perform a blur on each line, and place in the temporary storage buffer */
+ for (y = 0; y < im->ysize; y++) {
+ if (channels == 1 && im->image8 != NULL) {
+ line8 = (UINT8 *) im->image8[y];
+ } else {
+ line = im->image32[y];
+ }
+ for (x = 0; x < im->xsize; x++) {
+ newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0;
+ /* for each neighbor pixel, factor in its value/weighting to the
+ current pixel */
+ for (pix = 0; pix < radius; pix++) {
+ /* figure the offset of this neighbor pixel */
+ offset =
+ (int) ((-((float) radius / 2.0) + (float) pix) + 0.5);
+ if (x + offset < 0)
+ offset = -x;
+ else if (x + offset >= im->xsize)
+ offset = im->xsize - x - 1;
+
+ /* add (neighbor pixel value * maskData[pix]) to the current
+ pixel value */
+ if (channels == 1) {
+ buffer[(y * im->xsize) + x] +=
+ ((float) ((UINT8 *) & line8[x + offset])[0]) *
+ (maskData[pix]);
+ } else {
+ for (channel = 0; channel < channels; channel++) {
+ buffer[(y * im->xsize * channels) +
+ (x * channels) + channel] +=
+ ((float) ((UINT8 *) & line[x + offset])
+ [channel]) * (maskData[pix]);
+ }
+ }
+ }
+ }
+ }
+
+ /* perform a blur on each column in the buffer, and place in the
+ output image */
+ for (x = 0; x < im->xsize; x++) {
+ for (y = 0; y < im->ysize; y++) {
+ newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0;
+ /* for each neighbor pixel, factor in its value/weighting to the
+ current pixel */
+ for (pix = 0; pix < radius; pix++) {
+ /* figure the offset of this neighbor pixel */
+ offset =
+ (int) (-((float) radius / 2.0) + (float) pix + 0.5);
+ if (y + offset < 0)
+ offset = -y;
+ else if (y + offset >= im->ysize)
+ offset = im->ysize - y - 1;
+ /* add (neighbor pixel value * maskData[pix]) to the current
+ pixel value */
+ for (channel = 0; channel < channels; channel++) {
+ newPixel[channel] +=
+ (buffer
+ [((y + offset) * im->xsize * channels) +
+ (x * channels) + channel]) * (maskData[pix]);
+ }
+ }
+ /* if the image is RGBX or RGBA, copy the 4th channel data to
+ newPixel, so it gets put in imOut */
+ if (strcmp(im->mode, "RGBX") == 0
+ || strcmp(im->mode, "RGBA") == 0) {
+ newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3];
+ }
+
+ /* pack the channels into an INT32 so we can put them back in
+ the PIL image */
+ newPixelFinals = 0;
+ if (channels == 1) {
+ newPixelFinals = clip(newPixel[0]);
+ } else {
+ /* for RGB, the fourth channel isn't used anyways, so just
+ pack a 0 in there, this saves checking the mode for each
+ pixel. */
+ /* this doesn't work on little-endian machines... fix it! */
+ newPixelFinals =
+ clip(newPixel[0]) | clip(newPixel[1]) << 8 |
+ clip(newPixel[2]) << 16 | clip(newPixel[3]) << 24;
+ }
+ /* set the resulting pixel in imOut */
+ if (channels == 1) {
+ imOut->image8[y][x] = (UINT8) newPixelFinals;
+ } else {
+ imOut->image32[y][x] = newPixelFinals;
+ }
+ }
+ }
+
+ /* free the buffer */
+ free(buffer);
+
+ /* get the GIL back so Python knows who you are */
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
+
+Imaging ImagingGaussianBlur(Imaging im, Imaging imOut, float radius)
+{
+ int channels = 0;
+ int padding = 0;
+
+ if (strcmp(im->mode, "RGB") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "RGBA") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "RGBX") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "CMYK") == 0) {
+ channels = 4;
+ padding = 0;
+ } else if (strcmp(im->mode, "L") == 0) {
+ channels = 1;
+ padding = 0;
+ } else
+ return ImagingError_ModeError();
+
+ return gblur(im, imOut, radius, channels, padding);
+}
+
+Imaging
+ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent,
+ int threshold)
+{
+ ImagingSectionCookie cookie;
+
+ Imaging result;
+ int channel = 0;
+ int channels = 0;
+ int padding = 0;
+
+ int x = 0;
+ int y = 0;
+
+ int *lineIn = NULL;
+ int *lineOut = NULL;
+ UINT8 *lineIn8 = NULL;
+ UINT8 *lineOut8 = NULL;
+
+ int diff = 0;
+
+ INT32 newPixel = 0;
+
+ if (strcmp(im->mode, "RGB") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "RGBA") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "RGBX") == 0) {
+ channels = 3;
+ padding = 1;
+ } else if (strcmp(im->mode, "CMYK") == 0) {
+ channels = 4;
+ padding = 0;
+ } else if (strcmp(im->mode, "L") == 0) {
+ channels = 1;
+ padding = 0;
+ } else
+ return ImagingError_ModeError();
+
+ /* first, do a gaussian blur on the image, putting results in imOut
+ temporarily */
+ result = gblur(im, imOut, radius, channels, padding);
+ if (!result)
+ return NULL;
+
+ /* now, go through each pixel, compare "normal" pixel to blurred
+ pixel. if the difference is more than threshold values, apply
+ the OPPOSITE correction to the amount of blur, multiplied by
+ percent. */
+
+ ImagingSectionEnter(&cookie);
+
+ for (y = 0; y < im->ysize; y++) {
+ if (channels == 1) {
+ lineIn8 = im->image8[y];
+ lineOut8 = imOut->image8[y];
+ } else {
+ lineIn = im->image32[y];
+ lineOut = imOut->image32[y];
+ }
+ for (x = 0; x < im->xsize; x++) {
+ newPixel = 0;
+ /* compare in/out pixels, apply sharpening */
+ if (channels == 1) {
+ diff =
+ ((UINT8 *) & lineIn8[x])[0] -
+ ((UINT8 *) & lineOut8[x])[0];
+ if (abs(diff) > threshold) {
+ /* add the diff*percent to the original pixel */
+ imOut->image8[y][x] =
+ clip((((UINT8 *) & lineIn8[x])[0]) +
+ (diff * ((float) percent) / 100.0));
+ } else {
+ /* newPixel is the same as imIn */
+ imOut->image8[y][x] = ((UINT8 *) & lineIn8[x])[0];
+ }
+ }
+
+ else {
+ for (channel = 0; channel < channels; channel++) {
+ diff = (int) ((((UINT8 *) & lineIn[x])[channel]) -
+ (((UINT8 *) & lineOut[x])[channel]));
+ if (abs(diff) > threshold) {
+ /* add the diff*percent to the original pixel
+ this may not work for little-endian systems, fix it! */
+ newPixel =
+ newPixel |
+ clip((float) (((UINT8 *) & lineIn[x])[channel])
+ +
+ (diff *
+ (((float) percent /
+ 100.0)))) << (channel * 8);
+ } else {
+ /* newPixel is the same as imIn
+ this may not work for little-endian systems, fix it! */
+ newPixel =
+ newPixel | ((UINT8 *) & lineIn[x])[channel] <<
+ (channel * 8);
+ }
+ }
+ if (strcmp(im->mode, "RGBX") == 0
+ || strcmp(im->mode, "RGBA") == 0) {
+ /* preserve the alpha channel
+ this may not work for little-endian systems, fix it! */
+ newPixel =
+ newPixel | ((UINT8 *) & lineIn[x])[channel] << 24;
+ }
+ imOut->image32[y][x] = newPixel;
+ }
+ }
+ }
+
+ ImagingSectionLeave(&cookie);
+
+ return imOut;
+}
diff --git a/libImaging/XbmDecode.c b/libImaging/XbmDecode.c
new file mode 100644
index 000000000..8a203841b
--- /dev/null
+++ b/libImaging/XbmDecode.c
@@ -0,0 +1,81 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for XBM hex image data
+ *
+ * history:
+ * 96-04-13 fl Created
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\
+ (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\
+ (v >= 'A' && v <= 'F') ? v - 'A' + 10 : 0)
+
+int
+ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ enum { BYTE = 1, SKIP };
+
+ UINT8* ptr;
+
+ if (!state->state)
+ state->state = SKIP;
+
+ ptr = buf;
+
+ for (;;) {
+
+ if (state->state == SKIP) {
+
+ /* Skip forward until next 'x' */
+
+ while (bytes > 0) {
+ if (*ptr == 'x')
+ break;
+ ptr++;
+ bytes--;
+ }
+
+ if (bytes == 0)
+ return ptr - buf;
+
+ state->state = BYTE;
+
+ }
+
+ if (bytes < 3)
+ return ptr - buf;
+
+ state->buffer[state->x] = (HEX(ptr[1])<<4) + HEX(ptr[2]);
+
+ if (++state->x >= state->bytes) {
+
+ /* Got a full line, unpack it */
+ state->shuffle((UINT8*) im->image[state->y], state->buffer,
+ state->xsize);
+
+ state->x = 0;
+
+ if (++state->y >= state->ysize) {
+ /* End of file (errcode = 0) */
+ return -1;
+ }
+ }
+
+ ptr += 3;
+ bytes -= 3;
+
+ state->state = SKIP;
+
+ }
+
+}
diff --git a/libImaging/XbmEncode.c b/libImaging/XbmEncode.c
new file mode 100644
index 000000000..e066fd6b5
--- /dev/null
+++ b/libImaging/XbmEncode.c
@@ -0,0 +1,106 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * encoder for Xbm data
+ *
+ * history:
+ * 96-11-01 fl created
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+
+int
+ImagingXbmEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ const char *hex = "0123456789abcdef";
+
+ UINT8* ptr = buf;
+ int i, n;
+
+ if (!state->state) {
+
+ /* 8 pixels are stored in no more than 6 bytes */
+ state->bytes = 6*(state->xsize+7)/8;
+
+ state->state = 1;
+
+ }
+
+ if (bytes < state->bytes) {
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return 0;
+ }
+
+ ptr = buf;
+
+ while (bytes >= state->bytes) {
+
+ state->shuffle(state->buffer,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize, state->xsize);
+
+ if (state->y < state->ysize-1) {
+
+ /* any line but the last */
+ for (n = 0; n < state->xsize; n += 8) {
+
+ i = state->buffer[n/8];
+
+ *ptr++ = '0';
+ *ptr++ = 'x';
+ *ptr++ = hex[(i>>4)&15];
+ *ptr++ = hex[i&15];
+ *ptr++ = ',';
+ bytes -= 5;
+
+ if (++state->count >= 79/5) {
+ *ptr++ = '\n';
+ bytes--;
+ state->count = 0;
+ }
+
+ }
+
+ state->y++;
+
+ } else {
+
+ /* last line */
+ for (n = 0; n < state->xsize; n += 8) {
+
+ i = state->buffer[n/8];
+
+ *ptr++ = '0';
+ *ptr++ = 'x';
+ *ptr++ = hex[(i>>4)&15];
+ *ptr++ = hex[i&15];
+
+ if (n < state->xsize-8) {
+ *ptr++ = ',';
+ if (++state->count >= 79/5) {
+ *ptr++ = '\n';
+ bytes--;
+ state->count = 0;
+ }
+ } else
+ *ptr++ = '\n';
+
+ bytes -= 5;
+
+ }
+
+ state->errcode = IMAGING_CODEC_END;
+ break;
+ }
+ }
+
+ return ptr - buf;
+}
diff --git a/libImaging/Zip.h b/libImaging/Zip.h
new file mode 100644
index 000000000..d961407e3
--- /dev/null
+++ b/libImaging/Zip.h
@@ -0,0 +1,57 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * declarations for the ZIP codecs
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ */
+
+
+#include "zlib.h"
+
+
+/* modes */
+#define ZIP_PNG 0 /* continuous, filtered image data */
+#define ZIP_PNG_PALETTE 1 /* non-continuous data, disable filtering */
+#define ZIP_TIFF_PREDICTOR 2 /* TIFF, with predictor */
+#define ZIP_TIFF 3 /* TIFF, without predictor */
+
+
+typedef struct {
+
+ /* CONFIGURATION */
+
+ /* Codec mode */
+ int mode;
+
+ /* Optimize (max compression) SLOW!!! */
+ int optimize;
+
+ /* Predefined dictionary (experimental) */
+ char* dictionary;
+ int dictionary_size;
+
+ /* PRIVATE CONTEXT (set by decoder/encoder) */
+
+ z_stream z_stream; /* (de)compression stream */
+
+ UINT8* previous; /* previous line (allocated) */
+
+ int last_output; /* # bytes last output by inflate */
+
+ /* Compressor specific stuff */
+ UINT8* prior; /* filter storage (allocated) */
+ UINT8* up;
+ UINT8* average;
+ UINT8* paeth;
+
+ 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
new file mode 100644
index 000000000..08cdc7189
--- /dev/null
+++ b/libImaging/ZipDecode.c
@@ -0,0 +1,271 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * decoder for ZIP (deflated) image data.
+ *
+ * history:
+ * 1996-12-14 fl Created (for PNG)
+ * 1997-01-15 fl Prepared to read TIFF/ZIP
+ * 2001-11-19 fl PNG incomplete read patch (from Bernhard Herzog)
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997-2001.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#ifdef HAVE_LIBZ
+
+#include "Zip.h"
+
+static const int OFFSET[] = { 7, 3, 3, 1, 1, 0, 0 };
+static const int STARTING_COL[] = { 0, 4, 0, 2, 0, 1, 0 };
+static const int STARTING_ROW[] = { 0, 0, 4, 0, 2, 0, 1 };
+static const int COL_INCREMENT[] = { 8, 8, 4, 4, 2, 2, 1 };
+static const int ROW_INCREMENT[] = { 8, 8, 8, 4, 4, 2, 2 };
+
+/* Get the length in bytes of a scanline in the pass specified,
+ * for interlaced images */
+static int get_row_len(ImagingCodecState state, int pass)
+{
+ int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass];
+ return ((row_len * state->bits) + 7) / 8;
+}
+
+/* -------------------------------------------------------------------- */
+/* Decoder */
+/* -------------------------------------------------------------------- */
+
+int
+ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ ZIPSTATE* context = (ZIPSTATE*) state->context;
+ int err;
+ int n;
+ UINT8* ptr;
+ int i, bpp;
+ int row_len;
+
+ if (!state->state) {
+
+ /* Initialization */
+ if (context->mode == ZIP_PNG || context->mode == ZIP_PNG_PALETTE)
+ context->prefix = 1; /* PNG */
+
+ /* Expand standard buffer to make room for the (optional) filter
+ prefix, and allocate a buffer to hold the previous line */
+ free(state->buffer);
+ state->buffer = (UINT8*) malloc(state->bytes+1);
+ context->previous = (UINT8*) malloc(state->bytes+1);
+ if (!state->buffer || !context->previous) {
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return -1;
+ }
+
+ context->last_output = 0;
+
+ /* Initialize to black */
+ memset(context->previous, 0, state->bytes+1);
+
+ /* Setup decompression context */
+ context->z_stream.zalloc = (alloc_func) NULL;
+ context->z_stream.zfree = (free_func) NULL;
+ context->z_stream.opaque = (voidpf) NULL;
+
+ err = inflateInit(&context->z_stream);
+ if (err < 0) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ if (context->interlaced) {
+ context->pass = 0;
+ state->y = STARTING_ROW[context->pass];
+ }
+
+ /* Ready to decode */
+ state->state = 1;
+
+ }
+
+ if (context->interlaced) {
+ row_len = get_row_len(state, context->pass);
+ } else {
+ row_len = state->bytes;
+ }
+
+ /* Setup the source buffer */
+ context->z_stream.next_in = buf;
+ context->z_stream.avail_in = bytes;
+
+ /* Decompress what we've got this far */
+ while (context->z_stream.avail_in > 0) {
+
+ context->z_stream.next_out = state->buffer + context->last_output;
+ context->z_stream.avail_out =
+ row_len + context->prefix - context->last_output;
+
+ err = inflate(&context->z_stream, Z_NO_FLUSH);
+
+ if (err < 0) {
+ /* Something went wrong inside the compression library */
+ if (err == Z_DATA_ERROR)
+ state->errcode = IMAGING_CODEC_BROKEN;
+ else if (err == Z_MEM_ERROR)
+ state->errcode = IMAGING_CODEC_MEMORY;
+ else
+ state->errcode = IMAGING_CODEC_CONFIG;
+ free(context->previous);
+ inflateEnd(&context->z_stream);
+ return -1;
+ }
+
+ n = row_len + context->prefix - context->z_stream.avail_out;
+
+ if (n < row_len + context->prefix) {
+ context->last_output = n;
+ break; /* need more input data */
+ }
+
+ /* Apply predictor */
+ switch (context->mode) {
+ case ZIP_PNG:
+ switch (state->buffer[0]) {
+ case 0:
+ break;
+ case 1:
+ /* prior */
+ bpp = (state->bits + 7) / 8;
+ for (i = bpp+1; i <= row_len; i++)
+ state->buffer[i] += state->buffer[i-bpp];
+ break;
+ case 2:
+ /* up */
+ for (i = 1; i <= row_len; i++)
+ state->buffer[i] += context->previous[i];
+ break;
+ case 3:
+ /* average */
+ bpp = (state->bits + 7) / 8;
+ for (i = 1; i <= bpp; i++)
+ state->buffer[i] += context->previous[i]/2;
+ for (; i <= row_len; i++)
+ state->buffer[i] +=
+ (state->buffer[i-bpp] + context->previous[i])/2;
+ break;
+ case 4:
+ /* paeth filtering */
+ bpp = (state->bits + 7) / 8;
+ for (i = 1; i <= bpp; i++)
+ state->buffer[i] += context->previous[i];
+ for (; i <= row_len; i++) {
+ int a, b, c;
+ int pa, pb, pc;
+
+ /* fetch pixels */
+ a = state->buffer[i-bpp];
+ b = context->previous[i];
+ c = context->previous[i-bpp];
+
+ /* distances to surrounding pixels */
+ pa = abs(b - c);
+ pb = abs(a - c);
+ pc = abs(a + b - 2*c);
+
+ /* pick predictor with the shortest distance */
+ state->buffer[i] +=
+ (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
+
+ }
+ break;
+ default:
+ state->errcode = IMAGING_CODEC_UNKNOWN;
+ free(context->previous);
+ inflateEnd(&context->z_stream);
+ return -1;
+ }
+ break;
+ case ZIP_TIFF_PREDICTOR:
+ bpp = (state->bits + 7) / 8;
+ for (i = bpp+1; i <= row_len; i++)
+ state->buffer[i] += state->buffer[i-bpp];
+ break;
+ }
+
+ /* Stuff data into the image */
+ if (context->interlaced) {
+ int col = STARTING_COL[context->pass];
+ 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] +
+ col * im->pixelsize,
+ state->buffer + context->prefix + i, 1);
+ col += COL_INCREMENT[context->pass];
+ }
+ } else {
+ /* Handle case with more than a pixel in each byte */
+ int row_bits = ((state->xsize + OFFSET[context->pass])
+ / COL_INCREMENT[context->pass]) * state->bits;
+ 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] +
+ col * im->pixelsize, &byte, 1);
+ col += COL_INCREMENT[context->pass];
+ }
+ }
+ /* Find next valid scanline */
+ state->y += ROW_INCREMENT[context->pass];
+ while (state->y >= state->ysize || row_len <= 0) {
+ context->pass++;
+ if (context->pass == 7) {
+ /* Force exit below */
+ state->y = state->ysize;
+ break;
+ }
+ state->y = STARTING_ROW[context->pass];
+ row_len = get_row_len(state, context->pass);
+ /* Since we're moving to the "first" line, the previous line
+ * should be black to make filters work corectly */
+ memset(state->buffer, 0, state->bytes+1);
+ }
+ } else {
+ state->shuffle((UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize,
+ state->buffer + context->prefix,
+ state->xsize);
+ state->y++;
+ }
+
+ /* all inflate output has been consumed */
+ context->last_output = 0;
+
+ if (state->y >= state->ysize || err == Z_STREAM_END) {
+
+ /* The image and the data should end simultaneously */
+ /* if (state->y < state->ysize || err != Z_STREAM_END)
+ state->errcode = IMAGING_CODEC_BROKEN; */
+
+ free(context->previous);
+ inflateEnd(&context->z_stream);
+ return -1; /* end of file (errcode=0) */
+
+ }
+
+ /* Swap buffer pointers */
+ ptr = state->buffer;
+ state->buffer = context->previous;
+ context->previous = ptr;
+
+ }
+
+ return bytes; /* consumed all of it */
+
+}
+
+#endif
diff --git a/libImaging/ZipEncode.c b/libImaging/ZipEncode.c
new file mode 100644
index 000000000..19b2b7787
--- /dev/null
+++ b/libImaging/ZipEncode.c
@@ -0,0 +1,341 @@
+/*
+ * The Python Imaging Library.
+ * $Id$
+ *
+ * coder for ZIP (deflated) image data
+ *
+ * History:
+ * 96-12-29 fl created
+ * 96-12-30 fl adaptive filter selection, encoder tuning
+ *
+ * Copyright (c) Fredrik Lundh 1996.
+ * Copyright (c) Secret Labs AB 1997.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Imaging.h"
+
+#ifdef HAVE_LIBZ
+
+#include "Zip.h"
+
+int
+ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
+{
+ ZIPSTATE* context = (ZIPSTATE*) state->context;
+ int err;
+ UINT8* ptr;
+ int i, bpp, s, sum;
+ ImagingSectionCookie cookie;
+
+ if (!state->state) {
+
+ /* Initialization */
+
+ /* Valid modes are ZIP_PNG, ZIP_PNG_PALETTE, and ZIP_TIFF */
+
+ /* Expand standard buffer to make room for the filter selector,
+ and allocate filter buffers */
+ free(state->buffer);
+ state->buffer = (UINT8*) malloc(state->bytes+1);
+ context->previous = (UINT8*) malloc(state->bytes+1);
+ context->prior = (UINT8*) malloc(state->bytes+1);
+ context->up = (UINT8*) malloc(state->bytes+1);
+ context->average = (UINT8*) malloc(state->bytes+1);
+ context->paeth = (UINT8*) malloc(state->bytes+1);
+ if (!state->buffer || !context->previous || !context->prior ||
+ !context->up || !context->average || !context->paeth) {
+ free(context->paeth);
+ free(context->average);
+ free(context->up);
+ free(context->prior);
+ free(context->previous);
+ state->errcode = IMAGING_CODEC_MEMORY;
+ return -1;
+ }
+
+ /* Initalise filter buffers */
+ state->buffer[0] = 0;
+ context->prior[0] = 1;
+ context->up[0] = 2;
+ context->average[0] = 3;
+ context->paeth[0] = 4;
+
+ /* Initialise previous buffer to black */
+ memset(context->previous, 0, state->bytes+1);
+
+ /* Setup compression context */
+ context->z_stream.zalloc = (alloc_func)0;
+ context->z_stream.zfree = (free_func)0;
+ context->z_stream.opaque = (voidpf)0;
+ context->z_stream.next_in = 0;
+ context->z_stream.avail_in = 0;
+
+ err = deflateInit2(&context->z_stream,
+ /* compression level */
+ (context->optimize) ? Z_BEST_COMPRESSION
+ : Z_DEFAULT_COMPRESSION,
+ /* compression method */
+ Z_DEFLATED,
+ /* compression memory resources */
+ 15, 9,
+ /* compression strategy (image data are filtered)*/
+ (context->mode == ZIP_PNG) ? Z_FILTERED
+ : Z_DEFAULT_STRATEGY);
+ if (err < 0) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+
+ if (context->dictionary && context->dictionary_size > 0) {
+ err = deflateSetDictionary(&context->z_stream, (unsigned char *)context->dictionary,
+ context->dictionary_size);
+ if (err < 0) {
+ state->errcode = IMAGING_CODEC_CONFIG;
+ return -1;
+ }
+ }
+
+ /* Ready to decode */
+ state->state = 1;
+
+ }
+
+ /* Setup the destination buffer */
+ context->z_stream.next_out = buf;
+ context->z_stream.avail_out = bytes;
+ if (context->z_stream.next_in && context->z_stream.avail_in > 0) {
+ /* We have some data from previous round, deflate it first */
+ err = deflate(&context->z_stream, Z_NO_FLUSH);
+
+ if (err < 0) {
+ /* Something went wrong inside the compression library */
+ if (err == Z_DATA_ERROR)
+ state->errcode = IMAGING_CODEC_BROKEN;
+ else if (err == Z_MEM_ERROR)
+ state->errcode = IMAGING_CODEC_MEMORY;
+ else
+ state->errcode = IMAGING_CODEC_CONFIG;
+ free(context->paeth);
+ free(context->average);
+ free(context->up);
+ free(context->prior);
+ free(context->previous);
+ deflateEnd(&context->z_stream);
+ return -1;
+ }
+ }
+
+ ImagingSectionEnter(&cookie);
+ for (;;) {
+
+ switch (state->state) {
+
+ case 1:
+
+ /* Compress image data */
+ while (context->z_stream.avail_out > 0) {
+
+ if (state->y >= state->ysize) {
+ /* End of image; now flush compressor buffers */
+ state->state = 2;
+ break;
+
+ }
+
+ /* Stuff image data into the compressor */
+ state->shuffle(state->buffer+1,
+ (UINT8*) im->image[state->y + state->yoff] +
+ state->xoff * im->pixelsize,
+ state->xsize);
+
+ state->y++;
+
+ context->output = state->buffer;
+
+ if (context->mode == ZIP_PNG) {
+
+ /* Filter the image data. For each line, select
+ the filter that gives the least total distance
+ from zero for the filtered data (taken from
+ LIBPNG) */
+
+ bpp = (state->bits + 7) / 8;
+
+ /* 0. No filter */
+ for (i = 1, sum = 0; i <= state->bytes; i++) {
+ UINT8 v = state->buffer[i];
+ sum += (v < 128) ? v : 256 - v;
+ }
+
+ /* 2. Up. We'll test this first to save time when
+ an image line is identical to the one above. */
+ if (sum > 0) {
+ for (i = 1, s = 0; i <= state->bytes; i++) {
+ UINT8 v = state->buffer[i] - context->previous[i];
+ context->up[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ if (s < sum) {
+ context->output = context->up;
+ sum = s; /* 0 if line was duplicated */
+ }
+ }
+
+ /* 1. Prior */
+ if (sum > 0) {
+ for (i = 1, s = 0; i <= bpp; i++) {
+ UINT8 v = state->buffer[i];
+ context->prior[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ for (; i <= state->bytes; i++) {
+ UINT8 v = state->buffer[i] - state->buffer[i-bpp];
+ context->prior[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ if (s < sum) {
+ context->output = context->prior;
+ sum = s; /* 0 if line is solid */
+ }
+ }
+
+ /* 3. Average (not very common in real-life images,
+ so its only used with the optimize option) */
+ if (context->optimize && sum > 0) {
+ for (i = 1, s = 0; i <= bpp; i++) {
+ UINT8 v = state->buffer[i] - context->previous[i]/2;
+ context->average[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ for (; i <= state->bytes; i++) {
+ UINT8 v = state->buffer[i] -
+ (state->buffer[i-bpp] + context->previous[i])/2;
+ context->average[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ if (s < sum) {
+ context->output = context->average;
+ sum = s;
+ }
+ }
+
+ /* 4. Paeth */
+ if (sum > 0) {
+ for (i = 1, s = 0; i <= bpp; i++) {
+ UINT8 v = state->buffer[i] - context->previous[i];
+ context->paeth[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ for (; i <= state->bytes; i++) {
+ UINT8 v;
+ int a, b, c;
+ int pa, pb, pc;
+
+ /* fetch pixels */
+ a = state->buffer[i-bpp];
+ b = context->previous[i];
+ c = context->previous[i-bpp];
+
+ /* distances to surrounding pixels */
+ pa = abs(b - c);
+ pb = abs(a - c);
+ pc = abs(a + b - 2*c);
+
+ /* pick predictor with the shortest distance */
+ v = state->buffer[i] -
+ ((pa <= pb && pa <= pc) ? a :
+ (pb <= pc) ? b : c);
+ context->paeth[i] = v;
+ s += (v < 128) ? v : 256 - v;
+ }
+ if (s < sum) {
+ context->output = context->paeth;
+ sum = s;
+ }
+ }
+ }
+
+ /* Compress this line */
+ context->z_stream.next_in = context->output;
+ context->z_stream.avail_in = state->bytes+1;
+
+ err = deflate(&context->z_stream, Z_NO_FLUSH);
+
+ if (err < 0) {
+ /* Something went wrong inside the compression library */
+ if (err == Z_DATA_ERROR)
+ state->errcode = IMAGING_CODEC_BROKEN;
+ else if (err == Z_MEM_ERROR)
+ state->errcode = IMAGING_CODEC_MEMORY;
+ else
+ state->errcode = IMAGING_CODEC_CONFIG;
+ free(context->paeth);
+ free(context->average);
+ free(context->up);
+ free(context->prior);
+ free(context->previous);
+ deflateEnd(&context->z_stream);
+ ImagingSectionLeave(&cookie);
+ return -1;
+ }
+
+ /* Swap buffer pointers */
+ ptr = state->buffer;
+ state->buffer = context->previous;
+ context->previous = ptr;
+
+ }
+
+ if (context->z_stream.avail_out == 0)
+ break; /* Buffer full */
+
+ case 2:
+
+ /* End of image data; flush compressor buffers */
+
+ while (context->z_stream.avail_out > 0) {
+
+ err = deflate(&context->z_stream, Z_FINISH);
+
+ if (err == Z_STREAM_END) {
+
+ free(context->paeth);
+ free(context->average);
+ free(context->up);
+ free(context->prior);
+ free(context->previous);
+
+ deflateEnd(&context->z_stream);
+
+ state->errcode = IMAGING_CODEC_END;
+
+ break;
+ }
+
+ if (context->z_stream.avail_out == 0)
+ break; /* Buffer full */
+
+ }
+
+ }
+ ImagingSectionLeave(&cookie);
+ return bytes - context->z_stream.avail_out;
+
+ }
+
+ /* Should never ever arrive here... */
+ state->errcode = IMAGING_CODEC_CONFIG;
+ ImagingSectionLeave(&cookie);
+ return -1;
+}
+
+const char*
+ImagingZipVersion(void)
+{
+ return ZLIB_VERSION;
+}
+
+#endif
diff --git a/map.c b/map.c
new file mode 100644
index 000000000..f15b8200f
--- /dev/null
+++ b/map.c
@@ -0,0 +1,379 @@
+/*
+ * The Python Imaging Library.
+ *
+ * standard memory mapping interface for the Imaging library
+ *
+ * history:
+ * 1998-03-05 fl added Win32 read mapping
+ * 1999-02-06 fl added "I;16" support
+ * 2003-04-21 fl added PyImaging_MapBuffer primitive
+ *
+ * Copyright (c) 1998-2003 by Secret Labs AB.
+ * Copyright (c) 2003 by Fredrik Lundh.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+/*
+ * FIXME: should move the memory mapping primitives into libImaging!
+ */
+
+#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 INT32
+#undef INT64
+#undef UINT32
+#include "windows.h"
+#endif
+
+/* compatibility wrappers (defined in _imaging.c) */
+extern int PyImaging_CheckBuffer(PyObject* buffer);
+extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr);
+
+/* -------------------------------------------------------------------- */
+/* Standard mapper */
+
+typedef struct {
+ PyObject_HEAD
+ char* base;
+ int size;
+ int offset;
+#ifdef WIN32
+ HANDLE hFile;
+ HANDLE hMap;
+#endif
+} ImagingMapperObject;
+
+staticforward PyTypeObject ImagingMapperType;
+
+ImagingMapperObject*
+PyImaging_MapperNew(const char* filename, int readonly)
+{
+ ImagingMapperObject *mapper;
+
+ ImagingMapperType.ob_type = &PyType_Type;
+
+ mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType);
+ if (mapper == NULL)
+ return NULL;
+
+ mapper->base = NULL;
+ mapper->size = mapper->offset = 0;
+
+#ifdef WIN32
+ mapper->hFile = (HANDLE)-1;
+ mapper->hMap = (HANDLE)-1;
+
+ /* FIXME: currently supports readonly mappings only */
+ mapper->hFile = CreateFile(
+ filename,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (mapper->hFile == (HANDLE)-1) {
+ PyErr_SetString(PyExc_IOError, "cannot open file");
+ PyObject_Del(mapper);
+ return NULL;
+ }
+
+ mapper->hMap = CreateFileMapping(
+ mapper->hFile, NULL,
+ PAGE_READONLY,
+ 0, 0, NULL);
+ if (mapper->hMap == (HANDLE)-1) {
+ CloseHandle(mapper->hFile);
+ PyErr_SetString(PyExc_IOError, "cannot map file");
+ PyObject_Del(mapper);
+ return NULL;
+ }
+
+ mapper->base = (char*) MapViewOfFile(
+ mapper->hMap,
+ FILE_MAP_READ,
+ 0, 0, 0);
+
+ mapper->size = GetFileSize(mapper->hFile, 0);
+#endif
+
+ return mapper;
+}
+
+static void
+mapping_dealloc(ImagingMapperObject* mapper)
+{
+#ifdef WIN32
+ if (mapper->base != 0)
+ UnmapViewOfFile(mapper->base);
+ if (mapper->hMap != (HANDLE)-1)
+ CloseHandle(mapper->hMap);
+ if (mapper->hFile != (HANDLE)-1)
+ CloseHandle(mapper->hFile);
+ mapper->base = 0;
+ mapper->hMap = mapper->hFile = (HANDLE)-1;
+#endif
+ PyObject_Del(mapper);
+}
+
+/* -------------------------------------------------------------------- */
+/* standard file operations */
+
+static PyObject*
+mapping_read(ImagingMapperObject* mapper, PyObject* args)
+{
+ PyObject* buf;
+
+ int size = -1;
+ if (!PyArg_ParseTuple(args, "|i", &size))
+ return NULL;
+
+ /* check size */
+ if (size < 0 || mapper->offset + size > mapper->size)
+ size = mapper->size - mapper->offset;
+ if (size < 0)
+ size = 0;
+
+ buf = PyString_FromStringAndSize(NULL, size);
+ if (!buf)
+ return NULL;
+
+ if (size > 0) {
+ memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size);
+ mapper->offset += size;
+ }
+
+ return buf;
+}
+
+static PyObject*
+mapping_seek(ImagingMapperObject* mapper, PyObject* args)
+{
+ int offset;
+ int whence = 0;
+ if (!PyArg_ParseTuple(args, "i|i", &offset, &whence))
+ return NULL;
+
+ switch (whence) {
+ case 0: /* SEEK_SET */
+ mapper->offset = offset;
+ break;
+ case 1: /* SEEK_CUR */
+ mapper->offset += offset;
+ break;
+ case 2: /* SEEK_END */
+ mapper->offset = mapper->size + offset;
+ break;
+ default:
+ /* FIXME: raise ValueError? */
+ break;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* -------------------------------------------------------------------- */
+/* map entire image */
+
+extern PyObject*PyImagingNew(Imaging im);
+
+static void
+ImagingDestroyMap(Imaging im)
+{
+ return; /* nothing to do! */
+}
+
+static PyObject*
+mapping_readimage(ImagingMapperObject* mapper, PyObject* args)
+{
+ int y, size;
+ Imaging im;
+
+ char* mode;
+ int xsize;
+ int ysize;
+ int stride;
+ int orientation;
+ if (!PyArg_ParseTuple(args, "s(ii)ii", &mode, &xsize, &ysize,
+ &stride, &orientation))
+ return NULL;
+
+ if (stride <= 0) {
+ /* FIXME: maybe we should call ImagingNewPrologue instead */
+ if (!strcmp(mode, "L") || !strcmp(mode, "P"))
+ stride = xsize;
+ else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B"))
+ stride = xsize * 2;
+ else
+ stride = xsize * 4;
+ }
+
+ size = ysize * stride;
+
+ if (mapper->offset + size > mapper->size) {
+ PyErr_SetString(PyExc_IOError, "image file truncated");
+ return NULL;
+ }
+
+ im = ImagingNewPrologue(mode, xsize, ysize);
+ if (!im)
+ return NULL;
+
+ /* setup file pointers */
+ if (orientation > 0)
+ for (y = 0; y < ysize; y++)
+ im->image[y] = mapper->base + mapper->offset + y * stride;
+ else
+ for (y = 0; y < ysize; y++)
+ im->image[ysize-y-1] = mapper->base + mapper->offset + y * stride;
+
+ im->destroy = ImagingDestroyMap;
+
+ if (!ImagingNewEpilogue(im))
+ return NULL;
+
+ mapper->offset += size;
+
+ return PyImagingNew(im);
+}
+
+static struct PyMethodDef methods[] = {
+ /* standard file interface */
+ {"read", (PyCFunction)mapping_read, 1},
+ {"seek", (PyCFunction)mapping_seek, 1},
+ /* extensions */
+ {"readimage", (PyCFunction)mapping_readimage, 1},
+ {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*/
+};
+
+PyObject*
+PyImaging_Mapper(PyObject* self, PyObject* args)
+{
+ char* filename;
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ return (PyObject*) PyImaging_MapperNew(filename, 1);
+}
+
+/* -------------------------------------------------------------------- */
+/* Buffer mapper */
+
+typedef struct ImagingBufferInstance {
+ struct ImagingMemoryInstance im;
+ PyObject* target;
+} ImagingBufferInstance;
+
+static void
+mapping_destroy_buffer(Imaging im)
+{
+ ImagingBufferInstance* buffer = (ImagingBufferInstance*) im;
+
+ Py_XDECREF(buffer->target);
+}
+
+PyObject*
+PyImaging_MapBuffer(PyObject* self, PyObject* args)
+{
+ int y, size;
+ Imaging im;
+ char* ptr;
+ int bytes;
+
+ PyObject* target;
+ char* mode;
+ char* codec;
+ PyObject* bbox;
+ int offset;
+ int xsize, ysize;
+ int stride;
+ int ystep;
+
+ if (!PyArg_ParseTuple(args, "O(ii)sOi(sii)", &target, &xsize, &ysize,
+ &codec, &bbox, &offset, &mode, &stride, &ystep))
+ return NULL;
+
+ if (!PyImaging_CheckBuffer(target)) {
+ PyErr_SetString(PyExc_TypeError, "expected string or buffer");
+ return NULL;
+ }
+
+ if (stride <= 0) {
+ if (!strcmp(mode, "L") || !strcmp(mode, "P"))
+ stride = xsize;
+ else if (!strncmp(mode, "I;16", 4))
+ stride = xsize * 2;
+ else
+ stride = xsize * 4;
+ }
+
+ size = ysize * stride;
+
+ /* check buffer size */
+ bytes = PyImaging_ReadBuffer(target, (const void**) &ptr);
+ if (bytes < 0) {
+ PyErr_SetString(PyExc_ValueError, "buffer has negative size");
+ return NULL;
+ }
+ if (offset + size > bytes) {
+ PyErr_SetString(PyExc_ValueError, "buffer is not large enough");
+ return NULL;
+ }
+
+ im = ImagingNewPrologueSubtype(
+ mode, xsize, ysize, sizeof(ImagingBufferInstance)
+ );
+ if (!im)
+ return NULL;
+
+ /* setup file pointers */
+ if (ystep > 0)
+ for (y = 0; y < ysize; y++)
+ im->image[y] = ptr + offset + y * stride;
+ else
+ for (y = 0; y < ysize; y++)
+ im->image[ysize-y-1] = ptr + offset + y * stride;
+
+ im->destroy = mapping_destroy_buffer;
+
+ Py_INCREF(target);
+ ((ImagingBufferInstance*) im)->target = target;
+
+ if (!ImagingNewEpilogue(im))
+ return NULL;
+
+ return PyImagingNew(im);
+}
+
diff --git a/outline.c b/outline.c
new file mode 100644
index 000000000..6bb2ebb54
--- /dev/null
+++ b/outline.c
@@ -0,0 +1,179 @@
+/*
+ * THIS IS WORK IN PROGRESS.
+ *
+ * The Python Imaging Library.
+ *
+ * "arrow" outline stuff. the contents of this module
+ * will be merged with the path module and the rest of
+ * the arrow graphics package, but not before PIL 1.1.
+ * use at your own risk.
+ *
+ * history:
+ * 99-01-10 fl Added to PIL (experimental)
+ *
+ * Copyright (c) Secret Labs AB 1999.
+ * Copyright (c) Fredrik Lundh 1999.
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+#include "Python.h"
+
+#if PY_VERSION_HEX < 0x01060000
+#define PyObject_New PyObject_NEW
+#define PyObject_Del PyMem_DEL
+#endif
+
+#include "Imaging.h"
+
+
+/* -------------------------------------------------------------------- */
+/* Class */
+
+typedef struct {
+ PyObject_HEAD
+ ImagingOutline outline;
+} OutlineObject;
+
+staticforward PyTypeObject OutlineType;
+
+#define PyOutline_Check(op) ((op)->ob_type == &OutlineType)
+
+static OutlineObject*
+_outline_new(void)
+{
+ OutlineObject *self;
+
+ self = PyObject_New(OutlineObject, &OutlineType);
+ if (self == NULL)
+ return NULL;
+
+ self->outline = ImagingOutlineNew();
+
+ return self;
+}
+
+static void
+_outline_dealloc(OutlineObject* self)
+{
+ ImagingOutlineDelete(self->outline);
+ PyObject_Del(self);
+}
+
+ImagingOutline
+PyOutline_AsOutline(PyObject* outline)
+{
+ if (PyOutline_Check(outline))
+ return ((OutlineObject*) outline)->outline;
+
+ return NULL;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Factories */
+
+PyObject*
+PyOutline_Create(PyObject* self, PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ":outline"))
+ return NULL;
+
+ return (PyObject*) _outline_new();
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Methods */
+
+static PyObject*
+_outline_move(OutlineObject* self, PyObject* args)
+{
+ float x0, y0;
+ if (!PyArg_ParseTuple(args, "ff", &x0, &y0))
+ return NULL;
+
+ ImagingOutlineMove(self->outline, x0, y0);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_outline_line(OutlineObject* self, PyObject* args)
+{
+ float x1, y1;
+ if (!PyArg_ParseTuple(args, "ff", &x1, &y1))
+ return NULL;
+
+ ImagingOutlineLine(self->outline, x1, y1);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_outline_curve(OutlineObject* self, PyObject* args)
+{
+ float x1, y1, x2, y2, x3, y3;
+ if (!PyArg_ParseTuple(args, "ffffff", &x1, &y1, &x2, &y2, &x3, &y3))
+ return NULL;
+
+ ImagingOutlineCurve(self->outline, x1, y1, x2, y2, x3, y3);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_outline_close(OutlineObject* self, PyObject* args)
+{
+ if (!PyArg_ParseTuple(args, ":close"))
+ return NULL;
+
+ ImagingOutlineClose(self->outline);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject*
+_outline_transform(OutlineObject* self, PyObject* args)
+{
+ double a[6];
+ if (!PyArg_ParseTuple(args, "(dddddd)", a+0, a+1, a+2, a+3, a+4, a+5))
+ return NULL;
+
+ ImagingOutlineTransform(self->outline, a);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef _outline_methods[] = {
+ {"line", (PyCFunction)_outline_line, 1},
+ {"curve", (PyCFunction)_outline_curve, 1},
+ {"move", (PyCFunction)_outline_move, 1},
+ {"close", (PyCFunction)_outline_close, 1},
+ {"transform", (PyCFunction)_outline_transform, 1},
+ {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*/
+ "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*/
+};
diff --git a/path.c b/path.c
new file mode 100644
index 000000000..f083b971d
--- /dev/null
+++ b/path.c
@@ -0,0 +1,587 @@
+/*
+ * The Python Imaging Library.
+ *
+ * 2D path utilities
+ *
+ * history:
+ * 1996-11-04 fl Added to PIL (incomplete)
+ * 1996-11-05 fl Added sequence semantics
+ * 1997-02-28 fl Fixed getbbox
+ * 1997-06-12 fl Added id attribute
+ * 1997-06-14 fl Added slicing and setitem
+ * 1998-12-29 fl Improved sequence handling (from Richard Jones)
+ * 1999-01-10 fl Fixed IndexError test for 1.5 (from Fred Drake)
+ * 2000-10-12 fl Added special cases for tuples and lists
+ * 2002-10-27 fl Added clipping boilerplate
+ * 2004-09-19 fl Added tolist(flat) variant
+ * 2005-05-06 fl Added buffer interface support to path constructor
+ *
+ * notes:
+ * FIXME: fill in remaining slots in the sequence api
+ *
+ * Copyright (c) 1997-2005 by Secret Labs AB
+ * Copyright (c) 1997-2005 by Fredrik Lundh
+ *
+ * See the README file for information on usage and redistribution.
+ */
+
+
+#include "Python.h"
+
+#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
+
+/* compatibility wrappers (defined in _imaging.c) */
+extern int PyImaging_CheckBuffer(PyObject* buffer);
+extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr);
+
+/* -------------------------------------------------------------------- */
+/* Class */
+/* -------------------------------------------------------------------- */
+
+typedef struct {
+ PyObject_HEAD
+ Py_ssize_t count;
+ double *xy;
+ int index; /* temporary use, e.g. in decimate */
+} PyPathObject;
+
+staticforward PyTypeObject PyPathType;
+
+static double*
+alloc_array(int count)
+{
+ double* xy;
+ if (count < 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ xy = malloc(2 * count * sizeof(double) + 1);
+ if (!xy)
+ PyErr_NoMemory();
+ return xy;
+}
+
+static PyPathObject*
+path_new(Py_ssize_t count, double* xy, int duplicate)
+{
+ PyPathObject *path;
+
+ if (duplicate) {
+ /* duplicate path */
+ double* p = alloc_array(count);
+ if (!p)
+ return NULL;
+ memcpy(p, xy, count * 2 * sizeof(double));
+ xy = p;
+ }
+
+ path = PyObject_New(PyPathObject, &PyPathType);
+ if (path == NULL)
+ return NULL;
+
+ path->count = count;
+ path->xy = xy;
+
+ return path;
+}
+
+static void
+path_dealloc(PyPathObject* path)
+{
+ free(path->xy);
+ PyObject_Del(path);
+}
+
+/* -------------------------------------------------------------------- */
+/* Helpers */
+/* -------------------------------------------------------------------- */
+
+#define PyPath_Check(op) ((op)->ob_type == &PyPathType)
+
+int
+PyPath_Flatten(PyObject* data, double **pxy)
+{
+ int i, j, n;
+ double *xy;
+
+ if (PyPath_Check(data)) {
+ /* This was another path object. */
+ PyPathObject *path = (PyPathObject*) data;
+ xy = alloc_array(path->count);
+ if (!xy)
+ return -1;
+ memcpy(xy, path->xy, 2 * path->count * sizeof(double));
+ *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;
+ }
+
+ if (!PySequence_Check(data)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be sequence");
+ return -1;
+ }
+
+ j = 0;
+ n = PyObject_Length(data);
+ /* Just in case __len__ breaks (or doesn't exist) */
+ if (PyErr_Occurred())
+ return -1;
+
+ /* Allocate for worst case */
+ xy = alloc_array(n);
+ if (!xy)
+ return -1;
+
+ /* Copy table to path array */
+ if (PyList_Check(data)) {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PyList_GET_ITEM(data, i);
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ } else if (PyTuple_Check(data)) {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PyTuple_GET_ITEM(data, i);
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ } else {
+ for (i = 0; i < n; i++) {
+ double x, y;
+ PyObject *op = PySequence_GetItem(data, i);
+ if (!op) {
+ /* treat IndexError as end of sequence */
+ if (PyErr_Occurred() &&
+ PyErr_ExceptionMatches(PyExc_IndexError)) {
+ PyErr_Clear();
+ break;
+ } else {
+ free(xy);
+ return -1;
+ }
+ }
+ if (PyFloat_Check(op))
+ xy[j++] = PyFloat_AS_DOUBLE(op);
+ else if (PyInt_Check(op))
+ xy[j++] = (float) PyInt_AS_LONG(op);
+ else if (PyNumber_Check(op))
+ xy[j++] = PyFloat_AsDouble(op);
+ else if (PyArg_ParseTuple(op, "dd", &x, &y)) {
+ xy[j++] = x;
+ xy[j++] = y;
+ } else {
+ Py_DECREF(op);
+ free(xy);
+ return -1;
+ }
+ Py_DECREF(op);
+ }
+ }
+
+ if (j & 1) {
+ PyErr_SetString(PyExc_ValueError, "wrong number of coordinates");
+ free(xy);
+ return -1;
+ }
+
+ *pxy = xy;
+ return j/2;
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Factories */
+/* -------------------------------------------------------------------- */
+
+PyObject*
+PyPath_Create(PyObject* self, PyObject* args)
+{
+ PyObject* data;
+ Py_ssize_t count;
+ double *xy;
+
+ if (PyArg_ParseTuple(args, "i:Path", &count)) {
+
+ /* number of vertices */
+ xy = alloc_array(count);
+ if (!xy)
+ return NULL;
+
+ } else {
+
+ /* sequence or other path */
+ PyErr_Clear();
+ if (!PyArg_ParseTuple(args, "O", &data))
+ return NULL;
+
+ count = PyPath_Flatten(data, &xy);
+ if (count < 0)
+ return NULL;
+ }
+
+ return (PyObject*) path_new(count, xy, 0);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Methods */
+/* -------------------------------------------------------------------- */
+
+static PyObject*
+path_compact(PyPathObject* self, PyObject* args)
+{
+ /* Simple-minded method to shorten path. A point is removed if
+ the city block distance to the previous point is less than the
+ given distance */
+ int i, j;
+ double *xy;
+
+ double cityblock = 2.0;
+
+ if (!PyArg_ParseTuple(args, "|d:compact", &cityblock))
+ return NULL;
+
+ xy = self->xy;
+
+ /* remove bogus vertices */
+ for (i = j = 1; i < self->count; i++) {
+ if (fabs(xy[j+j-2]-xy[i+i]) + fabs(xy[j+j-1]-xy[i+i+1]) >= cityblock) {
+ xy[j+j] = xy[i+i];
+ xy[j+j+1] = xy[i+i+1];
+ j++;
+ }
+ }
+
+ i = self->count - j;
+ self->count = j;
+
+ /* shrink coordinate array */
+ self->xy = realloc(self->xy, 2 * self->count * sizeof(double));
+
+ return Py_BuildValue("i", i); /* number of removed vertices */
+}
+
+static PyObject*
+path_clip_polygon(PyPathObject* self, PyObject* args)
+{
+ /* Clip path representing a single polygon */
+ PyErr_SetString(PyExc_RuntimeError, "not yet implemented");
+ return NULL;
+}
+
+static PyObject*
+path_clip_polyline(PyPathObject* self, PyObject* args)
+{
+ /* Clip path representing a single polyline (outline) */
+ PyErr_SetString(PyExc_RuntimeError, "not yet implemented");
+ return NULL;
+}
+
+static PyObject*
+path_getbbox(PyPathObject* self, PyObject* args)
+{
+ /* Find bounding box */
+ int i;
+ double *xy;
+ double x0, y0, x1, y1;
+
+ if (!PyArg_ParseTuple(args, ":getbbox"))
+ return NULL;
+
+ xy = self->xy;
+
+ x0 = x1 = xy[0];
+ y0 = y1 = xy[1];
+
+ for (i = 1; i < self->count; i++) {
+ if (xy[i+i] < x0)
+ x0 = xy[i+i];
+ if (xy[i+i] > x1)
+ x1 = xy[i+i];
+ if (xy[i+i+1] < y0)
+ y0 = xy[i+i+1];
+ if (xy[i+i+1] > y1)
+ y1 = xy[i+i+1];
+ }
+
+ return Py_BuildValue("dddd", x0, y0, x1, y1);
+}
+
+static PyObject*
+path_getitem(PyPathObject* self, int i)
+{
+ if (i < 0 || i >= self->count) {
+ PyErr_SetString(PyExc_IndexError, "path index out of range");
+ return NULL;
+ }
+
+ return Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]);
+}
+
+static PyObject*
+path_getslice(PyPathObject* self, Py_ssize_t ilow, Py_ssize_t ihigh)
+{
+ /* adjust arguments */
+ if (ilow < 0)
+ ilow = 0;
+ else if (ilow >= self->count)
+ ilow = self->count;
+ if (ihigh < 0)
+ ihigh = 0;
+ if (ihigh < ilow)
+ ihigh = ilow;
+ else if (ihigh > self->count)
+ ihigh = self->count;
+
+ return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1);
+}
+
+static Py_ssize_t
+path_len(PyPathObject* self)
+{
+ return self->count;
+}
+
+static PyObject*
+path_map(PyPathObject* self, PyObject* args)
+{
+ /* Map coordinate set through function */
+ int i;
+ double *xy;
+ PyObject* function;
+
+ if (!PyArg_ParseTuple(args, "O:map", &function))
+ return NULL;
+
+ xy = self->xy;
+
+ /* apply function to coordinate set */
+ for (i = 0; i < self->count; i++) {
+ double x = xy[i+i];
+ double y = xy[i+i+1];
+ PyObject* item = PyObject_CallFunction(function, "dd", x, y);
+ if (!item || !PyArg_ParseTuple(item, "dd", &x, &y)) {
+ Py_XDECREF(item);
+ return NULL;
+ }
+ xy[i+i] = x;
+ xy[i+i+1] = y;
+ Py_DECREF(item);
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static int
+path_setitem(PyPathObject* self, int i, PyObject* op)
+{
+ double* xy;
+
+ if (i < 0 || i >= self->count) {
+ PyErr_SetString(PyExc_IndexError,
+ "path assignment index out of range");
+ return -1;
+ }
+
+ if (op == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot delete from path");
+ return -1;
+ }
+
+ xy = &self->xy[i+i];
+
+ if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1]))
+ return -1;
+
+ return 0;
+}
+
+static PyObject*
+path_tolist(PyPathObject* self, PyObject* args)
+{
+ PyObject *list;
+ int i;
+
+ int flat = 0;
+ if (!PyArg_ParseTuple(args, "|i:tolist", &flat))
+ return NULL;
+
+ if (flat) {
+ list = PyList_New(self->count*2);
+ for (i = 0; i < self->count*2; i++) {
+ PyObject* item;
+ item = PyFloat_FromDouble(self->xy[i]);
+ if (!item)
+ goto error;
+ PyList_SetItem(list, i, item);
+ }
+ } else {
+ list = PyList_New(self->count);
+ for (i = 0; i < self->count; i++) {
+ PyObject* item;
+ item = Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]);
+ if (!item)
+ goto error;
+ PyList_SetItem(list, i, item);
+ }
+ }
+
+ return list;
+
+error:
+ Py_DECREF(list);
+ return NULL;
+}
+
+static PyObject*
+path_transform(PyPathObject* self, PyObject* args)
+{
+ /* Apply affine transform to coordinate set */
+ int i;
+ double *xy;
+ double a, b, c, d, e, f;
+
+ double wrap = 0.0;
+
+ if (!PyArg_ParseTuple(args, "(dddddd)|d:transform",
+ &a, &b, &c, &d, &e, &f,
+ &wrap))
+ return NULL;
+
+ xy = self->xy;
+
+ /* transform the coordinate set */
+ if (b == 0.0 && d == 0.0)
+ /* scaling */
+ for (i = 0; i < self->count; i++) {
+ xy[i+i] = a*xy[i+i]+c;
+ xy[i+i+1] = e*xy[i+i+1]+f;
+ }
+ else
+ /* affine transform */
+ for (i = 0; i < self->count; i++) {
+ double x = xy[i+i];
+ double y = xy[i+i+1];
+ xy[i+i] = a*x+b*y+c;
+ xy[i+i+1] = d*x+e*y+f;
+ }
+
+ /* special treatment of geographical map data */
+ if (wrap != 0.0)
+ for (i = 0; i < self->count; i++)
+ xy[i+i] = fmod(xy[i+i], wrap);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static struct PyMethodDef methods[] = {
+ {"getbbox", (PyCFunction)path_getbbox, 1},
+ {"tolist", (PyCFunction)path_tolist, 1},
+ {"clip_polygon", (PyCFunction)path_clip_polygon, 1},
+ {"clip_polyline", (PyCFunction)path_clip_polyline, 1},
+ {"compact", (PyCFunction)path_compact, 1},
+ {"map", (PyCFunction)path_map, 1},
+ {"transform", (PyCFunction)path_transform, 1},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject*
+path_getattr(PyPathObject* self, char* name)
+{
+ PyObject* res;
+
+ res = Py_FindMethod(methods, (PyObject*) self, name);
+ if (res)
+ return res;
+
+ PyErr_Clear();
+
+ if (strcmp(name, "id") == 0)
+ return Py_BuildValue("l", (long) self->xy);
+
+ PyErr_SetString(PyExc_AttributeError, name);
+ return NULL;
+}
+
+static PySequenceMethods path_as_sequence = {
+ (lenfunc)path_len, /*sq_length*/
+ (binaryfunc)0, /*sq_concat*/
+ (ssizeargfunc)0, /*sq_repeat*/
+ (ssizeargfunc)path_getitem, /*sq_item*/
+ (ssizessizeargfunc)path_getslice, /*sq_slice*/
+ (ssizeobjargproc)path_setitem, /*sq_ass_item*/
+ (ssizessizeobjargproc)0, /*sq_ass_slice*/
+};
+
+statichere PyTypeObject PyPathType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "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_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number */
+ &path_as_sequence, /*tp_as_sequence */
+ 0, /*tp_as_mapping */
+ 0, /*tp_hash*/
+};
diff --git a/selftest.py b/selftest.py
new file mode 100644
index 000000000..22bbe433b
--- /dev/null
+++ b/selftest.py
@@ -0,0 +1,202 @@
+# minimal sanity check
+
+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
+ 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:
+
+ >>> im = Image.new("1", (128, 128)) # monochrome
+ >>> _info(im)
+ (None, '1', (128, 128))
+ >>> _info(Image.new("L", (128, 128))) # grayscale (luminance)
+ (None, 'L', (128, 128))
+ >>> _info(Image.new("P", (128, 128))) # palette
+ (None, 'P', (128, 128))
+ >>> _info(Image.new("RGB", (128, 128))) # truecolor
+ (None, 'RGB', (128, 128))
+ >>> _info(Image.new("I", (128, 128))) # 32-bit integer
+ (None, 'I', (128, 128))
+ >>> _info(Image.new("F", (128, 128))) # 32-bit floating point
+ (None, 'F', (128, 128))
+
+ Or open existing files:
+
+ >>> im = Image.open(os.path.join(ROOT, "Images/lena.gif"))
+ >>> _info(im)
+ ('GIF', 'P', (128, 128))
+ >>> _info(Image.open(os.path.join(ROOT, "Images/lena.ppm")))
+ ('PPM', 'RGB', (128, 128))
+ >>> try:
+ ... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg")))
+ ... except IOError, 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
+ None
+ >>> a = im.load()
+ >>> type(im.im)
+
+
+ You can apply many different operations on images. Most
+ operations return a new image:
+
+ >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm"))
+ >>> _info(im.convert("L"))
+ (None, 'L', (128, 128))
+ >>> _info(im.copy())
+ (None, 'RGB', (128, 128))
+ >>> _info(im.crop((32, 32, 96, 96)))
+ (None, 'RGB', (64, 64))
+ >>> _info(im.filter(ImageFilter.BLUR))
+ (None, 'RGB', (128, 128))
+ >>> im.getbands()
+ ('R', 'G', 'B')
+ >>> im.getbbox()
+ (0, 0, 128, 128)
+ >>> len(im.getdata())
+ 16384
+ >>> im.getextrema()
+ ((61, 255), (26, 234), (44, 223))
+ >>> im.getpixel((0, 0))
+ (223, 162, 133)
+ >>> len(im.getprojection())
+ 2
+ >>> len(im.histogram())
+ 768
+ >>> _info(im.point(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())
+ [(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))]
+ >>> len(im.convert("1").tobitmap())
+ 10456
+ >>> len(im.tostring())
+ 49152
+ >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0)))
+ (None, 'RGB', (512, 512))
+ >>> _info(im.transform((512, 512), Image.EXTENT, (32,32,96,96)))
+ (None, 'RGB', (512, 512))
+
+ The ImageDraw module lets you draw stuff in raster images:
+
+ >>> im = Image.new("L", (128, 128), 64)
+ >>> d = ImageDraw.ImageDraw(im)
+ >>> d.line((0, 0, 128, 128), fill=128)
+ >>> d.line((0, 128, 128, 0), fill=128)
+ >>> im.getextrema()
+ (64, 128)
+
+ In 1.1.4, you can specify colors in a number of ways:
+
+ >>> xy = 0, 0, 128, 128
+ >>> im = Image.new("RGB", (128, 128), 0)
+ >>> d = ImageDraw.ImageDraw(im)
+ >>> d.rectangle(xy, "#f00")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+ >>> d.rectangle(xy, "#ff0000")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+ >>> d.rectangle(xy, "rgb(255,0,0)")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+ >>> d.rectangle(xy, "rgb(100%,0%,0%)")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+ >>> d.rectangle(xy, "hsl(0, 100%, 50%)")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+ >>> d.rectangle(xy, "red")
+ >>> im.getpixel((0, 0))
+ (255, 0, 0)
+
+ In 1.1.6, you can use the ImageMath module to do image
+ calculations.
+
+ >>> im = ImageMath.eval("float(im + 20)", im=im.convert("L"))
+ >>> im.mode, im.size
+ ('F', (128, 128))
+
+ PIL can do many other things, but I'll leave that for another
+ day. If you're curious, check the handbook, available from:
+
+ http://www.pythonware.com
+
+ Cheers /F
+ """
+
+
+def check_module(feature, module):
+ try:
+ __import__("PIL." + module)
+ except ImportError:
+ print "***", feature, "support not installed"
+ else:
+ print "---", feature, "support ok"
+
+def check_codec(feature, codec):
+ if codec + "_encoder" not in dir(Image.core):
+ print "***", feature, "support not installed"
+ else:
+ print "---", feature, "support ok"
+
+
+if __name__ == "__main__":
+ # check build sanity
+
+ 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")
+ check_codec("JPEG", "jpeg")
+ check_codec("ZLIB (PNG/ZIP)", "zip")
+ check_module("FREETYPE2", "_imagingft")
+ check_module("LITTLECMS", "_imagingcms")
+ print "-"*68
+
+ # use doctest to make sure the test program behaves as documented!
+ import doctest, selftest
+ print "Running selftest:"
+ status = doctest.testmod(selftest)
+ if status[0]:
+ print "*** %s tests of %d failed." % status
+ exit_status = 1
+ else:
+ print "--- %s tests passed." % status[1]
+
+ sys.exit(exit_status)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 000000000..861a9f554
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 000000000..5e1a0a7c1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,477 @@
+#!/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
+#
+
+import glob, os, re, struct, string, sys
+
+def libinclude(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")
+
+TCL_ROOT = None
+JPEG_ROOT = None
+ZLIB_ROOT = None
+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):
+
+ def build_extensions(self):
+
+ global TCL_ROOT
+
+ library_dirs = []
+ include_dirs = []
+
+ add_directory(include_dirs, "libImaging")
+
+ #
+ # 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"
+ ))
+
+ 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")
+ # fink installation directories
+ 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, "/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
+
+ if _tkinter:
+ TCL_VERSION = _tkinter.TCL_VERSION[:3]
+
+ if _tkinter and not TCL_ROOT:
+ # we have Tkinter but the TCL_ROOT variable was not set;
+ # try to locate appropriate Tcl/Tk libraries
+ PYVERSION = sys.version[0] + sys.version[2]
+ TCLVERSION = TCL_VERSION[0] + TCL_VERSION[2]
+ roots = [
+ # common installation directories, mostly for Windows
+ # (for Unix-style platforms, we'll check in well-known
+ # locations later)
+ 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"),
+ ]
+ 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)
+ 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)
+
+ # standard locations
+ 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")
+
+ #
+ # insert new dirs *before* default libs, to avoid conflicts
+ # between Python PYD stub libs and real libraries
+
+ self.compiler.library_dirs = library_dirs + self.compiler.library_dirs
+ self.compiler.include_dirs = include_dirs + self.compiler.include_dirs
+
+ #
+ # look for available libraries
+
+ class feature:
+ zlib = jpeg = tiff = freetype = tcl = tk = lcms = None
+ feature = 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 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 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")
+ 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 _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
+
+ #
+ # core library
+
+ files = ["_imaging.c"]
+ for file in IMAGING:
+ files.append(file + ".c")
+ for file in LIBIMAGING:
+ files.append(os.path.join("libImaging", file + ".c"))
+
+ libs = []
+ defs = []
+ if feature.jpeg:
+ libs.append(feature.jpeg)
+ defs.append(("HAVE_LIBJPEG", None))
+ if feature.zlib:
+ libs.append(feature.zlib)
+ defs.append(("HAVE_LIBZ", None))
+ if sys.platform == "win32":
+ libs.extend(["kernel32", "user32", "gdi32"])
+ if struct.unpack("h", "\0\1")[0] == 1:
+ defs.append(("WORDS_BIGENDIAN", None))
+
+ exts = [(Extension(
+ "_imaging", files, libraries=libs, define_macros=defs
+ ))]
+
+ #
+ # additional libraries
+
+ if feature.freetype:
+ defs = []
+ if feature.freetype_version == 20:
+ defs.append(("USE_FREETYPE_2_0", None))
+ exts.append(Extension(
+ "_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"]
+ ))
+
+ 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
+ ))
+
+ if sys.platform == "darwin":
+ # locate Tcl/Tk frameworks
+ frameworks = []
+ framework_roots = [
+ "/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
+ frameworks = ["-framework", "Tcl", "-framework", "Tk"]
+ dir = os.path.join(root, "Tcl.framework", "Headers")
+ add_directory(self.compiler.include_dirs, dir, 0)
+ dir = os.path.join(root, "Tk.framework", "Headers")
+ 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
+ elif feature.tcl and feature.tk:
+ exts.append(Extension(
+ "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
+ libraries=[feature.tcl, feature.tk]
+ ))
+
+ if os.path.isfile("_imagingmath.c"):
+ exts.append(Extension("_imagingmath", ["_imagingmath.c"]))
+
+ self.extensions[:] = exts
+
+ build_ext.build_extensions(self)
+
+ #
+ # sanity and security checks
+
+ unsafe_zlib = None
+
+ if feature.zlib:
+ unsafe_zlib = self.check_zlib_version(self.compiler.include_dirs)
+
+ self.summary_report(feature, unsafe_zlib)
+
+ 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])
+ for v in v[1:]:
+ print " ", string.strip("[" + v)
+ 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.freetype, "FREETYPE2"),
+ (feature.lcms, "LITTLECMS"),
+ ]
+
+ all = 1
+ for option in options:
+ if option[0]:
+ print "---", option[1], "support available"
+ else:
+ print "***", option[1], "support not available",
+ if option[1] == "TKINTER" and _tkinter:
+ version = _tkinter.TCL_VERSION
+ print "(Tcl/Tk %s libraries needed)" % version,
+ print
+ 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 "-" * 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 check the build, run the selftest.py script."
+
+ def check_zlib_version(self, include_dirs):
+ # look for unsafe versions of zlib
+ for dir in include_dirs:
+ zlibfile = os.path.join(dir, "zlib.h")
+ if os.path.isfile(zlibfile):
+ break
+ else:
+ return
+ for line in open(zlibfile).readlines():
+ m = re.match('#define\s+ZLIB_VERSION\s+"([^"]*)"', line)
+ if not m:
+ continue
+ if m.group(1) < "1.2.3":
+ return m.group(1)
+
+#
+# build!
+
+if __name__ == "__main__":
+
+ 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,
+ )