diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..0d357a22f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ppm binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..a2a3dc417 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.pyc +*.egg-info +build +dist +.tox +*.so +docs/_build diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..1d0f5c191 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: python + +# for python-qt4 +virtualenv: + system_site_packages: true + +python: + - 2.6 + - 2.7 + - 3.2 + - 3.3 + +install: "sudo apt-get -qq install libfreetype6-dev liblcms2-dev libwebp-dev python-qt4 ghostscript" + +script: + - python setup.py clean + - python setup.py build_ext --inplace + - python selftest.py + - python Tests/run.py diff --git a/CHANGES b/CHANGES deleted file mode 100644 index b516d32bb..000000000 --- a/CHANGES +++ /dev/null @@ -1,1767 +0,0 @@ --*- coding: utf-8 -*- - -The Python Imaging Library -$Id$ - -ACKNOWLEDGEMENTS: PIL wouldn't be what it is without the help of: -David Ascher, Phil Austin, Douglas Bagnall, Larry Bates, Anthony -Baxter, William Baxter, Denis Benoit, Jan Blom, Duncan Booth, Alexey -Borzenkov, Jeff Breidenbach, Roger Burnham, Zac Burns, Gene Cash, -Kevin Cazabon, Fred Clare, Greg Coats, Chris Cogdon, Greg Couch, Bill -Crutchfield, Abel Deuring, Tim Docker, Fred Drake, Graham Dumpleton, -Matthew Ellis, Eric Etheridge, Daniel Fetchinson, Robin Friedrich, -Pier Paolo Glave, Federico Di Gregorio, Markus Gritsch, Daniel -Haertle, Greg Hamilton, Mark Hammond, Bernhard Herzog, Rob Hooft, Bob -Ippolito, Jack Jansen, Bill Janssen, Edward Jones, Richard Jones, -Håkan Karlsson, Robert Kern, David Kirtley, Bob Klimek, Matthias -Klose, Andrew Kuchling, Magnus Källström, Victor Lacina, Ben Last, -Hamish Lawson, Cesare Leonardi, Andrew MacIntyre, Jan Matejek, Naveen -Michaud-Agrawal, Gordon McMillan, Skip Montanaro, Fredrik Nehr, -Russell Nelson, Luciano Nocera, Travis Oliphant, Piet van Oostrum, -Richard Oudkerk, Paul Pharr, Andres Polit, Conrado Porto Lopes Gouvêa, -Eric Raymond, Victor Reijs, Bertil Reinhammar, Nicholas Riley, Don -Rozenberg, Toby Sargeant, Barry Scott, Les Schaffer, Joel Shprentz, -Klamer Shutte, Gene Skonicki, Niki Spahiev, D. Alan Stewart, Perry -Stoll, Paul Svensson, Ulrik Svensson, Miki Tebeka, Michael van -Tellingen, Ivan Tkatchev, Dan Torop, Adam Twardoch, Rune Uhlin, Dmitry -Vasiliev, Sasha Voynow, Charles Waldman, Collin Winter, Dan Wolfe, -Ka-Ping Yee, and many others (if your name should be on this list, let -me know.) - -*** Changes from release 1.1.6 to 1.1.7 *** - -This section may not be fully complete. For changes since this file -was last updated, see the repository revision history: - - http://bitbucket.org/effbot/pil-2009-raclette/changesets/ - -(1.1.7 final) - -+ Set GIF loop info property to the number of iterations if a NETSCAPE - loop extension is present, instead of always setting it to 1 (from - Valentino Volonghi). - -(1.1.7c1 released) - -+ Improved PNG compression (from Alexey Borzenkov). - -+ Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) - -+ Added various TGA improvements from Alexey Borzenkov, including - support for specifying image orientation. - -+ Bumped block threshold to 16 megabytes, made size estimation a bit - more accurate. This speeds up allocation of large images. - -+ Fixed rounding error in ImagingDrawWideLine. - - "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every - version I've seen, which leads to different width lines depending on - the order of the points in the line. This is especially bad at some - angles where a 'width=2' line can completely disappear. - -+ Added support for RGBA mode to the SGI module (based on code by - Karsten Hiddemann). - -+ Handle repeated IPTC tags (adapted from a patch by Eric Bruning). - - Eric writes: According to the specification, some IPTC tags can be - repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last - instance of that tag. Below is a patch to store all tags. If there are - multiple tag instances, they are stored in a (python) list. Single tag - instances remain as strings. - -+ Fixed potential crash in ImageFilter for small target images - (reported by Zac Burns and Daniel Fetchinson). - -+ Use BMP instead of JPEG as temporary show format on Mac OS X. - -+ Fixed putpixel/new for I;16 with colors > 255. - -+ Added integer power support to ImagingMath. - -+ Added limited support for I;16L mode (explicit little endian). - -+ Moved WMF support into Image.core; enable WMF rendering by default - if renderer is available. - -+ Mark the ARG plugin as obsolete. - -+ Added version query mechanism to ImageCms and ImageFont, for - debugging. - -+ Added (experimental) ImageCms function for fetching the ICC profile - for the current display (currently Windows only). - - Added HWND/HDC support to ImageCms.get_display_profile(). - -+ Added WMF renderer (Windows only). - -+ Added ImagePointHandler and ImageTransformHandler mixins; made - ImageCmsTransform work with im.point. - -+ Fixed potential endless loop in the XVThumbnail reader (from Nikolai - Ugelvik). - -+ Added Kevin Cazabon's pyCMS package. - - The C code has been moved to _imagingcms.c, the Python interface - module is installed as PIL.ImageCMS. - - Added support for in-memory ICC profiles. - - Unified buildTransform and buildTransformFromOpenProfiles. - - The profile can now be either a filename, a profile object, or a - file-like object containing an in-memory profile. - - Additional fixes from Florian Böch: - - Very nice - it just needs LCMS flags support so we can use black - point compensation and softproofing :) See attached patches. They - also fix a naming issue which could cause confusion - display - profile (ImageCms wording) actually means proof profile (lcms - wording), so I changed variable names and docstrings where - applicable. Patches are tested under Python 2.6. - -+ Improved support for layer names in PSD files (from Sylvain Baubeau) - - Sylvain writes: I needed to be able to retrieve the names of the - layers in a PSD files. But PsdImagePlugin.py didn't do the job so I - wrote this very small patch. - -+ Improved RGBA support for ImageTk for 8.4 and newer (from Con - Radchenko). - - This replaces the slow run-length based encoding model with true - compositing at the Tk level. - -+ Added support for 16- and 32-bit images to McIdas loader. - - Based on file samples and stand-alone reader code provided by Craig - Swank. - -+ Added ImagePalette support to putpalette. - -+ Fixed problem with incremental parsing of PNG files. - -+ Make selftest.py report non-zero status on failure (from Mark - Sienkiewicz) - -+ Add big endian save support and multipage infrastructure to the TIFF - writer (from Sebastian Haase). - -+ Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt - Schwehr). - -+ Added zTXT support (from Andrew Kuchling via Lowell Alleman). - -+ Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). - -+ Added sample ICC profiles (from Kevin Cazabon) - -+ Fixed array interface for I, F, and RGBA/RGBX images. - -+ Added Chroma subsampling support for JPEG (from Justin Huff). - - Justin writes: Attached is a patch (against PIL 1.1.6) to provide - control over the chroma subsampling done by the JPEG encoder. This - is often useful for reducing compression artifacts around edges of - clipart and text. - -+ Added USM/Gaussian Blur code from Kevin Cazabon. - -+ Fixed bug w. uninitialized image data when cropping outside the - source image. - -+ Use ImageShow to implement the Image.show method. - - Most notably, this picks the 'display' utility when available. It - also allows application code to register new display utilities via - the ImageShow registry. - -+ Release the GIL in the PNG compressor (from Michael van Tellingen). - -+ Revised JPEG CMYK handling. - - Always assume Adobe behaviour, both when reading and writing (based on - a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and - additional debugging by Michael van Tellingen). - -+ Support for preserving ICC profiles (by Florian Böch via Tim Hatch). - - Florian writes: - - It's a beta, so still needs some testing, but should allow you to: - - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. - Existing code doesn't need to be changed. - - access embedded profiles in JPEG, PNG, PSD, TIFF. - - It also includes patches for TIFF to retain IPTC, Photoshop and XMP - metadata when saving as TIFF again, read/write TIFF resolution - information correctly, and to correct inverted CMYK JPEG files. - -+ Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). - -+ Fixed OverflowError when reading upside-down BMP images. - -+ Added resolution save option for PDF files. - - Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py - based on 1.1.6 as included in Ubuntu, that supports a "resolution" - save option. Not great, but it makes the PDF saving more useful by - allowing PDFs that are not exactly 72dpi. - -+ Look for Tcl/Tk include files in version-specific include directory - (from Encolpe Degoute). - -+ Fixed grayscale rounding error in ImageColor.getcolor (from Tim - Hatch). - -+ Fixed calculation of mean value in ImageEnhance.Contrast (reported - by "roop" and Scott David Daniels). - -+ Fixed truetype positioning when first character has a negative left - bearing (from Ned Batchelder): - - Ned writes: In PIL 1.1.6, ImageDraw.text will position the string - incorrectly if the first character has a negative left bearing. To - see the problem, show a string like "///" in an italic font. The - first slash will be clipped at the left, and the string will be - mis-positioned. - -+ Fixed resolution unit bug in tiff reader/writer (based on code by - Florian Höch, Gary Bloom, and others). - -+ Added simple transparency support for RGB images (reported by - Sebastian Spaeth). - -+ Added support for Unicode filenames in ImageFont.truetype (from Donn - Ingle). - -+ Fixed potential crash in ImageFont.getname method (from Donn Ingle). - -+ Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). - -*** Changes from release 1.1.5 to 1.1.6 *** - -(1.1.6 released) - -+ Fixed some 64-bit compatibility warnings for Python 2.5. - -+ Added threading support for the Sane driver (from Abel Deuring). - -(1.1.6b2 released) - -+ Added experimental "floodfill" function to the ImageDraw module - (based on code by Eric Raymond). - -+ The default arguments for "frombuffer" doesn't match "fromstring" - and the documentation; this is a bug, and will most likely be fixed - in a future version. In this release, PIL prints a warning message - instead. To silence the warning, change any calls of the form - "frombuffer(mode, size, data)" to - - frombuffer(mode, size, data, "raw", mode, 0, 1) - -+ Added "fromarray" function, which takes an object implementing the - NumPy array interface and creates a PIL Image from it. (from Travis - Oliphant). - -+ Added NumPy array interface support (__array_interface__) to the - Image class (based on code by Travis Oliphant). - - This allows you to easily convert between PIL image memories and - NumPy arrays: - - import numpy, Image - - im = Image.open('lena.jpg') - - a = numpy.asarray(im) # a is readonly - - im = Image.fromarray(a) - -+ Fixed CMYK polarity for JPEG images, by treating all images as - "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon - for samples, debugging, and patches). - -(1.1.6b1 released) - -+ Added 'expand' option to the Image 'rotate' method. If true, the - output image is made large enough to hold the entire rotated image. - -+ Changed the ImageDraw 'line' method to always draw the last pixel in - a polyline, independent of line angle. - -+ Fixed bearing calculation and clipping in the ImageFont truetype - renderer; this could lead to clipped text, or crashes in the low- - level _imagingft module. (based on input from Adam Twardoch and - others). - -+ Added ImageQt wrapper module, for converting PIL Image objects to - QImage objects in an efficient way. - -+ Fixed 'getmodebands' to return the number of bands also for "PA" - and "LA" modes. Added 'getmodebandnames' helper that return the - band names. - -(1.1.6a2 released) - -+ Added float/double support to the TIFF loader (from Russell - Nelson). - -+ Fixed broken use of realloc() in path.c (from Jan Matejek) - -+ Added save support for Spider images (from William Baxter). - -+ Fixed broken 'paste' and 'resize' operations in pildriver - (from Bill Janssen). - -+ Added support for duplex scanning to the Sane interface (Abel - Deuring). - -(1.1.6a1 released) - -+ Fixed a memory leak in "convert(mode)", when converting from - L to P. - -+ Added pixel access object. The "load" method now returns a - access object that can be used to directly get and set pixel - values, using ordinary [x, y] notation: - - pixel = im.load() - v = pixel[x, y] - pixel[x, y] = v - - If you're accessing more than a few pixels, this is a lot - faster than using getpixel/putpixel. - -+ Fixed building on Cygwin (from Miki Tebeka). - -+ Fixed "point(callable)" on unloaded images (reported by Håkan - Karlsson). - -+ Fixed size bug in ImageWin.ImageWindow constructor (from Victor - Reijs) - -+ Fixed ImageMath float() and int() operations for Python 2.4 - (reported by Don Rozenberg). - -+ Fixed "RuntimeError: encoder error -8 in tostring" problem for - wide "RGB", "I", and "F" images. - -+ Fixed line width calculation. - -(1.1.6a0 released) - -+ Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). - -+ Fixed off-by-0.5 errors in the ANTIALIAS code (based on input - from Douglas Bagnall). - -+ Added buffer interface support to the Path constructor. If - a buffer is provided, it is assumed to contain a flat array - of float coordinates (e.g. array.array('f', seq)). - -+ Added new ImageMath module. - -+ Fixed ImageOps.equalize when used with a small number of distinct - values (reported by David Kirtley). - -+ Fixed potential integer division in PSDraw.image (from Eric Etheridge). - -*** Changes from release 1.1 to 1.1.5 *** - -(1.1.5c2 and 1.1.5 final released) - -+ Added experimental PERSPECTIVE transform method (from Jeff Breiden- - bach). - -(1.1.5c1 released) - -+ Make sure "thumbnail" never generates zero-wide or zero-high images - (reported by Gene Skonicki) - -+ Fixed a "getcolors" bug that could result in a zero count for some - colors (reported by Richard Oudkerk). - -+ Changed default "convert" palette to avoid "rounding errors" when - round-tripping white source pixels (reported by Henryk Gerlach and - Jeff Epler). - -(1.1.5b3 released) - -+ Don't crash in "quantize" method if the number of colors requested - is larger than 256. This release raises a ValueError exception; - future versions may return a mode "RGB" image instead (reported - by Richard Oudkerk). - -+ Added WBMP read/write support (based on code by Duncan Booth). - -(1.1.5b2 released) - -+ Added DPI read/write support to the PNG codec. The decoder sets - the info["dpi"] attribute for PNG files with appropriate resolution - settings. The encoder uses the "dpi" option (based on code by Niki - Spahiev). - -+ Added limited support for "point" mappings from mode "I" to mode "L". - Only 16-bit values are supported (other values are clipped), the lookup - table must contain exactly 65536 entries, and the mode argument must be - set to "L". - -+ Added support for Mac OS X icns files (based on code by Bob Ippolito). - -+ Added "ModeFilter" support to the ImageFilter module. - -+ Added support for Spider images (from William Baxter). See the - comments in PIL/SpiderImagePlugin.py for more information on this - format. - -(1.1.5b1 released) - -+ Added new Sane release (from Ralph Heinkel). See the Sane/README - and Sane/CHANGES files for more information. - -+ Added experimental PngInfo chunk container to the PngImageFile - module. This can be used to add arbitrary chunks to a PNG file. - Create a PngInfo instance, use "add" or "add_text" to add chunks, - and pass the instance as the "pnginfo" option when saving the - file. - -+ Added "getpalette" method. This returns the palette as a list, - or None if the image has no palette. To modify the palette, use - "getpalette" to fetch the current palette, modify the list, and - put it back using "putpalette". - -+ Added optional flattening to the ImagePath "tolist" method. - tolist() or tolist(0) returns a list of 2-tuples, as before. - tolist(1) returns a flattened list instead. - -(1.1.5a5 released) - -+ Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". - -+ Added "getcolors()" method. This is similar to the existing histo- - gram method, but looks at color values instead of individual layers, - and returns an unsorted list of (count, color) tuples. - - By default, the method returns None if finds more than 256 colors. - If you need to look for more colors, you can pass in a limit (this - is used to allocate internal tables, so you probably don't want to - pass in too large values). - -+ Build improvements: Fixed building under AIX, improved detection of - FreeType2 and Mac OS X framework libraries, and more. Many thanks - to everyone who helped test the new "setup.py" script! - -(1.1.5a4 released) - -+ The "save" method now looks for a file format driver before - creating the file. - -+ Don't use antialiased truetype fonts when drawing in mode "P", "I", - and "F" images. - -+ Rewrote the "setup.py" file. The new version scans for available - support libraries, and configures both the libImaging core library - and the bindings in one step. - - To use specific versions of the libraries, edit the ROOT variables - in the setup.py file. - -+ Removed threaded "show" viewer; use the old "show" implementation - instead (Windows). - -+ Added deprecation warnings to Image.offset, ImageDraw.setink, and - ImageDraw.setfill. - -+ Added width option to ImageDraw.line(). The current implementation - works best for straight lines; it does not support line joins, so - polylines won't look good. - -+ ImageDraw.Draw is now a factory function instead of a class. If - you need to create custom draw classes, inherit from the ImageDraw - class. All other code should use the factory function. - -+ Fixed loading of certain PCX files (problem reported by Greg - Hamilton, who also provided samples). - -+ Changed _imagingft.c to require FreeType 2.1 or newer. The - module can still be built with earlier versions; see comments - in _imagingft.c for details. - -(1.1.5a3 released) - -+ Added 'getim' method, which returns a PyCObject wrapping an - Imaging pointer. The description string is set to IMAGING_MAGIC. - See Imaging.h for pointer and string declarations. - -+ Fixed reading of TIFF JPEG images (problem reported by Ulrik - Svensson). - -+ Made ImageColor work under Python 1.5.2 - -+ Fixed division by zero "equalize" on very small images (from - Douglas Bagnall). - -(1.1.5a2 released) - -+ The "paste" method now supports the alternative "paste(im, mask)" - syntax (in this case, the box defaults to im's bounding box). - -+ The "ImageFile.Parser" class now works also for PNG files with - more than one IDAT block. - -+ Added DPI read/write to the TIFF codec, and fixed writing of - rational values. The decoder sets the info["dpi"] attribute - for TIFF files with appropriate resolution settings. The - encoder uses the "dpi" option. - -+ Disable interlacing for small (or narrow) GIF images, to - work around what appears to be a hard-to-find bug in PIL's - GIF encoder. - -+ Fixed writing of mode "P" PDF images. Made mode "1" PDF - images smaller. - -+ Made the XBM reader a bit more robust; the file may now start - with a few whitespace characters. - -+ Added support for enhanced metafiles to the WMF driver. The - separate PILWMF kit lets you render both placeable WMF files - and EMF files as raster images. See - - http://effbot.org/downloads#pilwmf - -(1.1.5a1 released) - -+ Replaced broken WMF driver with a WMF stub plugin (see below). - -+ Fixed writing of mode "1", "L", and "CMYK" PDF images (based on - input from Nicholas Riley and others). - -+ Fixed adaptive palette conversion for zero-width or zero-height - images (from Chris Cogdon) - -+ Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) - -+ Added support for StubImageFile plugins, including stub plugins - for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify - a given file format, but relies on application code to open and - save files in that format. - -+ Added optional "encoding" argument to the ImageFont.truetype - factory. This argument can be used to specify non-Unicode character - maps for fonts that support that. For example, to draw text using - the Microsoft Symbol font, use: - - font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") - draw.text((0, 0), unichr(0xF000 + 0xAA)) - - (note that the symbol font uses characters in the 0xF000-0xF0FF - range) - - 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. - -+ Made "putalpha" a bit more robust; you can now attach an alpha - layer to a plain "L" or "RGB" image, and you can also specify - constant alphas instead of alpha layers (using integers or colour - names). - -+ Added experimental "LA" mode support. - - An "LA" image is an "L" image with an attached transparency layer. - Note that support for "LA" is not complete; some operations may - fail or produce unexpected results. - -+ Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" - classes to the ImageFilter module. - -+ Improved support for applications using multiple threads; PIL - now releases the global interpreter lock for many CPU-intensive - operations (based on work by Kevin Cazabon). - -+ Ignore Unicode characters in the PCF loader (from Andres Polit) - -+ Fixed typo in OleFileIO.loadfat, which could affect loading of - FlashPix and Image Composer images (Daniel Haertle) - -+ Fixed building on platforms that have Freetype but don't have - Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) - -+ Added EXIF GPSInfo read support for JPEG files. To extract - GPSInfo information, open the file, extract the exif dictionary, - and check for the key 0x8825 (GPSInfo). If present, it contains - a dictionary mapping GPS keys to GPS values. For a list of keys, - see the EXIF specification. - - The "ExifTags" module contains a GPSTAGS dictionary mapping GPS - tags to tag names. - -+ Added DPI read support to the PCX and DCX codecs (info["dpi"]). - -+ The "show" methods now uses a built-in image viewer on Windows. - This viewer creates an instance of the ImageWindow class (see - below) and keeps it running in a separate thread. NOTE: This - was disabled in 1.1.5a4. - -+ Added experimental "Window" and "ImageWindow" classes to the - ImageWin module. These classes allow you to create a WCK-style - toplevel window, and use it to display raster data. - -+ Fixed some Python 1.5.2 issues (to build under 1.5.2, use the - Makefile.pre.in/Setup.in approach) - -+ Added support for the TIFF FillOrder tag. PIL can read mode "1", - "L", "P" and "RGB" images with non-standard FillOrder (based on - input from Jeff Breidenbach). - -(1.1.4 final released) - -+ Fixed ImageTk build problem on Unix. - -(1.1.4b2 released) - -+ Improved building on Mac OS X (from Jack Jansen). - -+ Improved building on Windows with MinGW (from Klamer Shutte). - -+ If no font is specified, ImageDraw now uses the embedded default - font. Use the "load" or "truetype" methods to load a real font. - -+ Added embedded default font to the ImageFont module (currently - an 8-pixel Courier font, taken from the X window distribution). - -(1.1.4b1 released) - -+ Added experimental EXIF support for JPEG files. To extract EXIF - information from a JPEG file, open the file as usual, and call the - "_getexif" method. If successful, this method returns a dictionary - mapping EXIF TIFF tags to values. If the file does not contain EXIF - data, the "_getexif" method returns None. - - The "ExifTags" module contains a dictionary mapping tags to tag - names. - - This interface will most likely change in future versions. - -+ Fixed a bug when using the "transparency" option with the GIF - writer. - -+ Added limited support for "bitfield compression" in BMP files - and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This - also fixes a problem with ImageGrab module when copying screen- - dumps from the clipboard on 15/16/32-bit displays. - -+ Added experimental WAL (Quake 2 textures) loader. To use this - loader, import WalImageFile and call the "open" method in that - module. - -(1.1.4a4 released) - -+ Added updated SANE driver (Andrew Kuchling, Abel Deuring) - -+ Use Python's "mmap" module on non-Windows platforms to read some - uncompressed formats using memory mapping. Also added a "frombuffer" - function that allows you to access the contents of an existing string - or buffer object as if it were an image object. - -+ Fixed a memory leak that could appear when processing mode "P" - images (from Pier Paolo Glave) - -+ Ignore Unicode characters in the BDF loader (from Graham Dumpleton) - -(1.1.4a3 released; windows only) - -+ Added experimental RGBA-on-RGB drawing support. To use RGBA - colours on an RGB image, pass "RGBA" as the second string to - the ImageDraw.Draw constructor. - -+ Added support for non-ASCII strings (Latin-1) and Unicode - to the truetype font renderer. - -+ The ImageWin "Dib" object can now be constructed directly from - an image object. - -+ The ImageWin module now allows you use window handles as well - as device contexts. To use a window handle, wrap the handle in - an ImageWin.HWND object, and pass in this object instead of the - device context. - -(1.1.4a2 released) - -+ Improved support for 16-bit unsigned integer images (mode "I;16"). - This includes TIFF reader support, and support for "getextrema" - and "point" (from Klamer Shutte). - -+ Made the BdfFontFile reader a bit more robust (from Kevin Cazabon - and Dmitry Vasiliev) - -+ Changed TIFF writer to always write Compression tag, even when - using the default compression (from Greg Couch). - -+ Added "show" support for Mac OS X (from Dan Wolfe). - -+ Added clipboard support to the "ImageGrab" module (Windows only). - The "grabclipboard" function returns an Image object, a list of - filenames (not in 1.1.4), or None if neither was found. - -(1.1.4a1 released) - -+ Improved support for drawing RGB data in palette images. You can - now use RGB tuples or colour names (see below) when drawing in a - mode "P" image. The drawing layer automatically assigns color - indexes, as long as you don't use more than 256 unique colours. - -+ Moved self test from MiniTest/test.py to ./selftest.py. - -+ Added support for CSS3-style color strings to most places that - accept colour codes/tuples. This includes the "ImageDraw" module, - the Image "new" function, and the Image "paste" method. - - Colour strings can use one of the following formats: "#f00", - "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", - or "red" (most X11-style colour names are supported). See the - documentation for the "ImageColor" module for more information. - -+ Fixed DCX decoder (based on input from Larry Bates) - -+ Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA - newsphoto properties from JPEG, TIFF, or IPTC files. - -+ Support for TrueType/OpenType fonts has been added to - the standard distribution. You need the freetype 2.0 - library. - -+ Made the PCX reader a bit more robust when reading 2-bit - and 4-bit PCX images with odd image sizes. - -+ Added "Kernel" class to the ImageFilter module. This class - allows you to filter images with user-defined 3x3 and 5x5 - convolution kernels. - -+ Added "putdata" support for mode "I", "F" and "RGB". - -+ The GIF writer now supports the transparency option (from - Denis Benoit). - -+ A HTML version of the module documentation is now shipped - with the source code distribution. You'll find the files in - the Doc subdirectory. - -+ Added support for Palm pixmaps (from Bill Janssen). This - change was listed for 1.1.3, but the "PalmImagePlugin" driver - didn't make it into the distribution. - -+ Improved decoder error messages. - -(1.1.3 final released) - -+ Made setup.py look for old versions of zlib. For some back- - ground, see: http://www.gzip.org/zlib/advisory-2002-03-11.txt - -(1.1.3c2 released) - -+ Added setup.py file (tested on Unix and Windows). You still - need to build libImaging/imaging.lib in the traditional way, - but the setup.py script takes care of the rest. - - The old Setup.in/Makefile.pre.in build method is still - supported. - -+ Fixed segmentation violation in ANTIALIAS filter (an internal - buffer wasn't properly allocated). - -(1.1.3c1 released) - -+ Added ANTIALIAS downsampling filter for high-quality "resize" - and "thumbnail" operations. Also added filter option to the - "thumbnail" operation; the default value is NEAREST, but this - will most likely change in future versions. - -+ Fixed plugin loader to be more robust if the __file__ - variable isn't set. - -+ Added seek/tell support (for layers) to the PhotoShop - loader. Layer 0 is the main image. - -+ Added new (but experimental) "ImageOps" module, which provides - shortcuts for commonly used operations on entire images. - -+ Don't mess up when loading PNG images if the decoder leaves - data in the output buffer. This could cause internal errors - on some PNG images, with some versions of ZLIB. (Bug report - and patch provided by Bernhard Herzog.) - -+ Don't mess up on Unicode filenames. - -+ Don't mess up when drawing on big endian platforms. - -+ Made the TIFF loader a bit more robust; it can now read some - more slightly broken TIFF files (based on input from Ted Wright, - Bob Klimek, and D. Alan Stewart) - -+ Added OS/2 EMX build files (from Andrew MacIntyre) - -+ Change "ImageFont" to reject image files if they don't have the - right mode. Older versions could leak memory for "P" images. - (Bug reported by Markus Gritsch). - -+ Renamed some internal functions to avoid potential build - problem on Mac OS X. - -+ Added DL_EXPORT where relevant (for Cygwin, based on input - from Robert Yodlowski) - -+ (re)moved bogus __init__ call in BdfFontFile (bug spotted - by Fred Clare) - -+ Added "ImageGrab" support (Windows only) - -+ Added support for XBM hotspots (based on code contributed by - Bernhard Herzog). - -+ Added write support for more TIFF tags, namely the Artist, - Copyright, DateTime, ResolutionUnit, Software, XResolution and - YResolution tags (from Greg Couch) - -+ Added TransposedFont wrapper to ImageFont module - -+ Added "optimize" flag to GIF encoder. If optimize is present - and non-zero, PIL will work harder to create a small file. - -+ Raise "EOFError" (not IndexError) when reading beyond the - end of a TIFF sequence. - -+ Support rewind ("seek(0)") for GIF and TIFF sequences. - -+ Load grayscale GIF images as mode "L" - -+ Added DPI read/write support to the JPEG codec. The decoder - sets the info["dpi"] attribute for JPEG files with JFIF dpi - settings. The encoder uses the "dpi" option: - - im = Image.open("file.jpg") - dpi = im.info["dpi"] # raises KeyError if DPI not known - im.save("out.jpg", dpi=dpi) - - Note that PIL doesn't always preserve the "info" attribute - for normal image operations. - -(1.1.2c1 and 1.1.2 final released) - -+ Adapted to Python 2.1. Among other things, all uses of the - "regex" module has been repleased with "re". - -+ Fixed attribute error when reading large PNG files (this bug - was introduced in maintenance code released after the 1.1.1 - release) - -+ Ignore non-string objects in sys.path - -+ Fixed Image.transform(EXTENT) for negative xoffsets - -+ Fixed loading of image plugins if PIL is installed as a package. - (The plugin loader now always looks in the directory where the - Image.py module itself is found, even if that directory isn't on - the standard search path) - -+ The Png plugin has been added to the list of preloaded standard - formats - -+ Fixed bitmap/text drawing in fill mode. - -+ Fixed "getextrema" to work also for multiband images. - -+ Added transparency support for L and P images to the PNG codec. - -+ Improved support for read-only images. The "load" method now - sets the "readonly" attribute for memory-mapped images. Operations - that modifies an image in place (such as "paste" and drawing operations) - creates an in-memory copy of the image, if necessary. (before this - change, any attempt to modify a memory-mapped image resulted in a - core dump...) - -+ Added special cases for lists everywhere PIL expects a sequence. - This should speed up things like "putdata" and drawing operations. - -+ The Image.offset method is deprecated. Use the ImageChops.offset - function instead. - -+ Changed ImageChops operators to copy palette and info dictionary - from the first image argument. - -(1.1.1 released) - -+ Additional fixes for Python 1.6/2.0, including TIFF "save" bug. - -+ Changed "init" to properly load plugins when PIL is used as a - package. - -+ Fixed broken "show" method (on Unix) - -*** Changes from release 1.0 to 1.1 *** - -+ Adapted to Python 1.6 ("append" and other method changes) - -+ Fixed Image.paste when pasting with solid colour and matte - layers ("L" or "RGBA" masks) (bug reported by Robert Kern) - -+ To make it easier to distribute prebuilt versions of PIL, - the tkinit binding stuff has been moved to a separate - extension module, named "_imagingtk". - -*** Changes from release 0.3b2 to 1.0 final *** - -+ If there's no 16-bit integer (like on a Cray T3E), set - INT16 to the smallest integer available. Most of the - library works just fine anyway (from Bill Crutchfield) - -+ Tweaks to make drawing work on big-endian platforms. - -(1.0c2 released) - -+ If PIL is built with the WITH_TKINTER flag, ImageTk can - automatically hook into a standard Tkinter build. You - no longer need to build your own Tkinter to use the - ImageTk module. - - The old way still works, though. For more information, - see Tk/install.txt. - -+ Some tweaks to ImageTk to support multiple Tk interpreters - (from Greg Couch). - -+ ImageFont "load_path" now scans directory mentioned in .pth - files (from Richard Jones). - -(1.0c1 released) - -+ The TIFF plugin has been rewritten. The new plugin fully - supports all major PIL image modes (including F and I). - -+ The ImageFile module now includes a Parser class, which can - be used to incrementally decode an image file (while down- - loading it from the net, for example). See the handbook for - details. - -+ "show" now converts non-standard modes to "L" or "RGB" (as - appropriate), rather than writing weird things to disk for - "xv" to choke upon. (bug reported by Les Schaffer). - -(1.0b2 released) - -+ Major speedups for rotate, transform(EXTENT), and transform(AFFINE) - when using nearest neighbour resampling. - -+ Modified ImageDraw to be compatible with the Arrow graphics - interface. See the handbook for details. - -+ PIL now automatically loads file codecs when used as a package - (from The Dragon De Monsyne). Also included an __init__.py file - in the standard distribution. - -+ The GIF encoder has been modified to produce much smaller files. - - PIL now uses a run-length encoding method to encode GIF files. - On a random selection of GIF images grabbed from the web, this - version makes the images about twice as large as the original - LZW files, where the earlier version made them over 5 times - larger. YMMV, of course. - -+ Added PCX write support (works with "1", "P", "L", and "RGB") - -+ Added "bitmap" and "textsize" methods to ImageDraw. - -+ Improved font rendering code. Fixed a bug or two, and moved - most of the time critical stuff to C. - -+ Removed "bdf2pil.py". Use "pilfont.py" instead! - -+ Improved 16-bit support (still experimental, though). - - The following methods now support "I;16" and "I;16B" images: - "getpixel", "copy", "convert" (to and from mode "I"), "resize", - "rotate", and "transform" with nearest neighbour filters, and - "save" using the IM format. The "new" and "open" functions - also work as expected. On Windows, 16-bit files are memory - mapped. - - NOTE: ALL other operations are still UNDEFINED on 16-bit images. - -+ The "paste" method now supports constant sources. - - Just pass a colour value (a number or a tuple, depending on - the target image mode) instead of the source image. - - This was in fact implemented in an inefficient way in - earlier versions (the "paste" method generated a temporary - source image if you passed it a colour instead of an image). - In this version, this is handled on the C level instead. - -+ Added experimental "RGBa" mode support. - - An "RGBa" image is an RGBA image where the colour components - have have been premultipled with the alpha value. PIL allows - you to convert an RGBA image to an RGBa image, and to paste - RGBa images on top of RGB images. Since this saves a bunch - of multiplications and shifts, it is typically about twice - as fast an ordinary RGBA paste. - -+ Eliminated extra conversion step when pasting "RGBA" or "RGBa" - images on top of "RGB" images. - -+ Fixed Image.BICUBIC resampling for "RGB" images. - -+ Fixed PCX image file handler to properly read 8-bit PCX - files (bug introduced in 1.0b1, reported by Bernhard - Herzog) - -+ Fixed PSDraw "image" method to restore the coordinate - system. - -+ Fixed "blend" problem when applied to images that was - not already loaded (reported by Edward C. Jones) - -+ Fixed -f option to "pilconvert.py" (from Anthony Baxter) - -(1.0b1 released) - -+ Added Toby J. Sargeant's quantization package. To enable - quantization, use the "palette" option to "convert": - - imOut = im.convert("P", palette=Image.ADAPTIVE) - - This can be used with "L", "P", and "RGB" images. In this - version, dithering cannot be used with adaptive palettes. - - Note: ADAPTIVE currently maps to median cut quantization - with 256 colours. The quantization package also contains - a maximum coverage quantizer, which will be supported by - future versions of PIL. - -+ Added Eric S. Raymond's "pildriver" image calculator to the - distribution. See the docstring for more information. - -+ The "offset" method no longer dumps core if given positive - offsets (from Charles Waldman). - -+ Fixed a resource leak that could cause ImageWin to run out of - GDI resources (from Roger Burnham). - -+ Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired - by code contributed by Richard Jones). - -+ Added experimental 16-bit support, via modes "I;16" (little endian - data) and "I;16B" (big endian). Only a few methods properly support - such images (see above). - -+ Added XV thumbnail file handler (from Gene Cash). - -+ Fixed BMP image file handler to handle palette images with small - palettes (from Rob Hooft). - -+ Fixed Sun raster file handler for palette images (from Charles - Waldman). - -+ Improved various internal error messages. - -+ Fixed Path constructor to handle arbitrary sequence objects. This - also affects the ImageDraw class (from Richard Jones). - -+ Fixed a bug in JpegDecode that caused PIL to report "decoder error - -2" for some progressive JPEG files (reported by Magnus Källström, - who also provided samples). - -+ Fixed a bug in JpegImagePlugin that caused PIL to hang when loading - JPEG files using 16-bit quantization tables. - -+ The Image "transform" method now supports Image.QUAD transforms. - The data argument is an 8-tuple giving the upper left, lower - left, lower right, and upper right corner of the source quadri- - lateral. Also added Image.MESH transform which takes a list - of quadrilaterals. - -+ The Image "resize", "rotate", and "transform" methods now support - Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. - Filters can be used with all transform methods. - -+ The ImageDraw "rectangle" method now includes both the right - and the bottom edges when drawing filled rectangles. - -+ The TGA decoder now works properly for runlength encoded images - which have more than one byte per pixel. - -+ "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") - -+ Some file drivers didn't handle the optional "modify" argument - to the load method. This resulted in exceptions when you used - "paste" (and other methods that modify an image in place) on a - newly opened file. - -*** Changes from release 0.2 (b5) to 0.3 (b2) *** - -(0.3b2 released) - -The test suite includes 825 individual tests. - -+ An Image "getbands" method has been added. It returns a tuple - containing the individual band names for this image. To figure - out how many bands an image has, use "len(im.getbands())". - -+ An Image "putpixel" method has been added. - -+ The Image "point" method can now be used to convert "L" images - to any other format, via a lookup table. That table should - contain 256 values for each band in the output image. - -+ Some file drivers (including FLI/FLC, GIF, and IM) accidently - overwrote the offset method with an internal attribute. All - drivers have been updated to use private attributes where - possible. - -+ The Image "histogram" method now works for "I" and "F" images. - For these modes, PIL divides the range between the min and - max values used in the image into 256 bins. You can also - pass in your own min and max values via the "extrema" option: - - h = im.histogram(extrema=(0, 255)) - -+ An Image "getextrema" method has been added. It returns the - min and max values used in the image. In this release, this - works for single band images only. - -+ Changed the PNG driver to load and save mode "I" images as - 16-bit images. When saving, values outside the range 0..65535 - are clipped. - -+ Fixed ImageFont.py to work with the new "pilfont" compiler. - -+ Added JPEG "save" and "draft" support for mode "YCbCr" images. - Note that if you save an "YCbCr" image as a JPEG file and read - it back, it is read as an RGB file. To get around this, you - can use the "draft" method: - - im = Image.open("color.jpg") - im.draft("YCbCr", im.size) - -+ Read "RGBA" TGA images. Also fixed the orientation bug; all - images should now come out the right way. - -+ Changed mode name (and internal representation) from "YCrCb" - to "YCbCr" (!) - *** WARNING: MAY BREAK EXISTING CODE *** - -(0.3b1 released) - -The test suite includes 750 individual tests. - -+ The "pilfont" package is now included in the standard PIL - distribution. The pilfont utility can be used to convert - X BDF and PCF raster font files to a format understood by - the ImageFont module. - -+ GIF files are now interlaced by default. To write a - non-interlaced file, pass interlace=0 to the "save" - method. - -+ The default string format has changed for the "fromstring" - and "tostring" methods. - *** WARNING: MAY BREAK EXISTING CODE *** - - NOTE: If no extra arguments are given, the first line in - the string buffer is the top line of the image, instead of - the bottom line. For RGB images, the string now contains - 3 bytes per pixel instead of 4. These changes were made - to make the methods compatible with the "fromstring" - factory function. - - To get the old behaviour, use the following syntax: - - data = im.tostring("raw", "RGBX", 0, -1) - im.fromstring(data, "raw", "RGBX", 0, -1) - -+ "new" no longer gives a MemoryError if the width or height - is zero (this only happened on platforms where malloc(0) - or calloc(0) returns NULL). - -+ "new" now adds a default palette object to "P" images. - -+ You can now convert directly between all modes supported by - PIL. When converting colour images to "P", PIL defaults to - a "web" palette and dithering. When converting greyscale - images to "1", PIL uses a thresholding and dithering. - -+ Added a "dither" option to "convert". By default, "convert" - uses floyd-steinberg error diffusion for "P" and "1" targets, - so this option is only used to *disable* dithering. Allowed - values are NONE (no dithering) or FLOYDSTEINBERG (default). - - imOut = im.convert("P", dither=Image.NONE) - -+ Added a full set of "I" decoders. You can use "fromstring" - (and file decoders) to read any standard integer type as an - "I" image. - -+ Added some support for "YCbCr" images (creation, conversion - from/to "L" and "RGB", IM YCC load/save) - -+ "getpixel" now works properly with fractional coordinates. - -+ ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", - "RGBX", "CMYK", and "YCbCr" images. - -+ ImImagePlugin no longer attaches palettes to "RGB" images. - -+ Various minor fixes. - -(0.3a4 released) - -+ Added experimental IPTC/NAA support. - -+ Eliminated AttributeError exceptions after "crop" (from - Skip Montanaro) - -+ Reads some uncompressed formats via memory mapping (this - is currently supported on Win32 only) - -+ Fixed some last minute glitches in the last alpha release - (Types instead of types in Image.py, version numbers, etc.) - -+ Eliminated some more bogus compiler warnings. - -+ Various fixes to make PIL compile and run smoother on Macs - (from Jack Jansen). - -+ Fixed "fromstring" and "tostring" for mode "I" images. - -(0.3a3 released) - -The test suite includes 530 individual tests. - -+ Eliminated unexpected side-effect in "paste" with matte. "paste" - now works properly also if compiled with "gcc". - -+ Adapted to Python 1.5 (build issues only) - -+ Fixed the ImageDraw "point" method to draw also the last - point (!). - -+ Added "I" and "RGBX" support to Image.new. - -+ The plugin path is now properly prepended to the module search - path when a plugin module is imported. - -+ Added "draw" method to the ImageWin.Dib class. This is used by - Topaz to print images on Windows printers. - -+ "convert" now supports conversions from "P" to "1" and "F". - -+ "paste" can now take a colour instead of an image as the first argument. - The colour must match the colour argument given to the new function, and - match the mode of the target image. - -+ Fixed "paste" to allow a mask also for mode "F" images. - -+ The BMP driver now saves mode "1" images. When loading images, the mode - is set to "L" for 8-bit files with greyscale palettes, and to "P" for - other 8-bit files. - -+ The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). - -+ The JPEG and GIF drivers now saves "1" images. For JPEG, the image - is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the - image will be loaded as a "P" image. - -+ Fixed a potential buffer overrun in the GIF encoder. - -(0.3a2 released) - -The test suite includes 400 individual tests. - -+ Improvements to the test suite revealed a number of minor bugs, which - are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont - are still weak spots in this release. - -+ Added "putpalette" method to the Image class. You can use this - to add or modify the palette for "P" and "L" images. If a palette - is added to an "L" image, it is automatically converted to a "P" - image. - -+ Fixed ImageDraw to properly handle 32-bit image memories - ("RGB", "RGBA", "CMYK", "F") - -+ Fixed "fromstring" and "tostring" not to mess up the mode attribute - in default mode. - -+ Changed ImPlatform.h to work on CRAY's (don't have one at home, so I - haven't tried it). The previous version assumed that either "short" - or "int" were 16-bit wide. PIL still won't compile on platforms where - neither "short", "int" nor "long" are 32-bit wide. - -+ Added file= and data= keyword arguments to PhotoImage and BitmapImage. - This allows you to use them as drop-in replacements for the corre- - sponding Tkinter classes. - -+ Removed bogus references to the crack coder (ImagingCrack). - -(0.3a1 released) - -+ Make sure image is loaded in "tostring". - -+ Added floating point packer (native 32-bit floats only). - -*** Changes from release 0.1b1 to 0.2 (b5) *** - -+ Modified "fromstring" and "tostring" methods to use file codecs. - Also added "fromstring" factory method to create an image directly - from data in a string. - -+ Added support for 32-bit floating point images (mode "F"). You - can convert between "L" and "F" images, and apply a subset of the - available image processing methods on the "F" image. You can also - read virtually any data format into a floating point image memory; - see the section on "Decoding Floating Point Data" in the handbook - for more information. - -(0.2b5 released; on windows only) - -+ Fixed the tobitmap() method to work properly for small bitmaps. - -+ Added RMS and standard deviation to the ImageStat.Stat class. Also - modified the constructor to take an optional feature mask, and also - to accept either an image or a list containing the histogram data. - -+ The BitmapImage code in ImageTk can now use a special bitmap - decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" - file for details. If not installed, bitmaps are transferred to Tk as - XBM strings. - -+ The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") - instead of a special image type. This gives somewhat better performance, - and also allows PIL to support transparency. - *** WARNING: TKAPPINIT MUST BE MODIFIED *** - -+ ImageTk now honours the alpha layer in RGBA images. Only fully - transparent pixels are made transparent (that is, the alpha layer - is treated as a mask). To treat the alpha laters as a matte, you - must paste the image on the background before handing it over to - ImageTk. - -+ Added McIdas reader (supports 8-bit images only). - -+ PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As - long as you only load and save these formats, you don't have to - wait for a full scan for drivers. To force scanning, call the - Image.init() function. - -+ The "seek" and "tell" methods are now always available, also for - single-frame images. - -+ Added optional mask argument to histogram method. The mask may - be an "1" or "L" image with the same size as the original image. - Only pixels where the mask is non-zero are included in the - histogram. - -+ The "paste" method now allows you to specify only the lower left - corner (a 2-tuple), instead of the full region (a 4-tuple). - -+ Reverted to old plugin scanning model; now scans all directory - names in the path when looking for plugins. - -+ Added PIXAR raster support. Only uncompressed ("dumped") RGB - images can currently be read (based on information provided - by Greg Coats). - -+ Added FlashPix (FPX) read support. Reads all pixel formats, but - only the highest resolution is read, and the viewing transform is - currently ignored. - -+ Made PNG encoding somewhat more efficient in "optimize" mode; a - bug in 0.2b4 didn't enable all predictor filters when optimized - storage were requested. - -+ Added Microsoft Image Composer (MIC) read support. When opened, - the first sprite in the file is loaded. You can use the seek method - to load additional sprites from the file. - -+ Properly reads "P" and "CMYK" PSD images. - -+ "pilconvert" no longer optimizes by default; use the -o option to - make the file as small as possible (at the expense of speed); use - the -q option to set the quality when compressing to JPEG. - -+ Fixed "crop" not to drop the palette for "P" images. - -+ Added and verified FLC support. - -+ Paste with "L" or "RGBA" alpha is now several times faster on most - platforms. - -+ Changed Image.new() to initialize the image to black, as described - in the handbook. To get an uninitialized image, use None as the - colour. - -+ Fixed the PDF encoder to produce a valid header; Acrobat no longer - complains when you load PDF images created by PIL. - -+ PIL only scans fully-qualified directory names in the path when - looking for plugins. - *** WARNING: MAY BREAK EXISTING CODE *** - -+ Faster implementation of "save" used when filename is given, - or when file object has "fileno" and "flush" methods. - -+ Don't crash in "crop" if region extends outside the source image. - -+ Eliminated a massive memory leak in the "save" function. - -+ The GIF decoder doesn't crash if the code size is set to an illegal - value. This could happen since another bug didn't handle local - palettes properly if they didn't have the same size as the - global palette (not very common). - -+ Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. - -+ Fixed palette and padding problems in BMP driver. Now properly - writes "1", "L", "P" and "RGB" images. - -+ Fixed getpixel()/getdata() to return correct pixel values. - -+ Added PSD (PhotoShop) read support. Reads both uncompressed - and compressed images of most types. - -+ Added GIF write support (writes "uncompressed" GIF files only, - due to unresolvable licensing issues). The "gifmaker.py" script - can be used to create GIF animations. - -+ Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" - images. - -+ Added FLI read support. This driver has only been tested - on a few FLI samples. - -+ Reads 2-bit and 4-bit PCX images. - -+ Added MSP read and write support. Both version 1 and 2 can be - read, but only version 1 (uncompressed) files are written. - -+ Fixed a bug in the FLI/FLC identification code that caused the - driver to raise an exception when parsing valid FLI/FLC files. - -+ Improved performance when loading file format plugins, and when - opening files. - -+ Added GIF animation support, via the "seek" and "tell" methods. - You can use "player.py" to play an animated GIF file. - -+ Removed MNG support, since the spec is changing faster than I - can change the code. I've added support for the experimental - ARG format instead. Contact me for more information on this - format. - -+ Added keyword options to the "save" method. The following options - are currently supported: - - format option description - -------------------------------------------------------- - JPEG optimize minimize output file at the - expense of compression speed. - - JPEG progressive enable progressive output. the - option value is ignored. - - JPEG quality set compression quality (1-100). - the default value is 75. - - JPEG smooth smooth dithered images. value - is strengh (1-100). default is - off (0). - - PNG optimize minimize output file at the - expense of compression speed. - - Expect more options in future releases. Also note that - file writers silently ignore unknown options. - -+ Plugged memory leaks in the PNG and TIFF decoders. - -+ Added PNG write support. - -+ (internal) RGB unpackers and converters now set the pad byte - to 255 (full opacity). - -+ Properly handles the "transparency" property for GIF, PNG - and XPM files. - -+ Added a "putalpha" method, allowing you to attach a "1" or "L" - image as the alpha layer to an "RGBA" image. - -+ Various improvements to the sample scripts: - - "pilconvert" Carries out some extra tricks in order to make - the resulting file as small as possible. - - "explode" (NEW) Split an image sequence into individual frames. - - "gifmaker" (NEW) Convert a sequence file into a GIF animation. - Note that the GIF encoder create "uncompressed" GIF - files, so animations created by this script are - rather large (typically 2-5 times the compressed - sizes). - - "image2py" (NEW) Convert a single image to a python module. See - comments in this script for details. - - "player" If multiple images are given on the command line, - they are interpreted as frames in a sequence. The - script assumes that they all have the same size. - Also note that this script now can play FLI/FLC - and GIF animations. - - This player can also execute embedded Python - animation applets (ARG format only). - - "viewer" Transparent images ("P" with transparency property, - and "RGBA") are superimposed on the standard Tk back- - ground. - -+ Fixed colour argument to "new". For multilayer images, pass a - tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, - Magenta, Yellow, Black). - -+ Added XPM (X pixmap) read support. - -(0.2b3 released) - -+ Added MNG (multi-image network graphics) read support. "Ming" - is a proposed animation standard, based on the PNG file format. - - You can use the "player" sample script to display some flavours - of this format. The MNG standard is still under development, - as is this driver. More information, including sample files, - can be found at - -+ Added a "verify" method to images loaded from file. This method - scans the file for errors, without actually decoding the image - data, and raises a suitable exception if it finds any problems. - Currently implemented for PNG and MNG files only. - -+ Added support for interlaced GIF images. - -+ Added PNG read support -- if linked with the ZLIB compression library, - PIL reads all kinds of PNG images, except interlaced files. - -+ Improved PNG identification support -- doesn't mess up on unknown - chunks, identifies all possible PNG modes, and verifies checksum - on PNG header chunks. - -+ Added an experimental reader for placable Windows Meta Files (WMF). - This reader is still very incomplete, but it illustrates how PIL's - drawing capabilities can be used to render vector and metafile - formats. - -+ Added restricted drivers for images from Image Tools (greyscale - only) and LabEye/IFUNC (common interchange modes only). - -+ Some minor improvements to the sample scripts provided in the - "Scripts" directory. - -+ The test images have been moved to the "Images" directory. - -(0.2b2 released) -(0.2b1 released; Windows only) - -+ Fixed filling of complex polygons. The ImageDraw "line" and - "polygon" methods also accept Path objects. - -+ The ImageTk "PhotoImage" object can now be constructed directly - from an image. You can also pass the object itself to Tkinter, - instead of using the "image" attribute. Finally, using "paste" - on a displayed image automatically updates the display. - -+ The ImageTk "BitmapImage" object allows you to create transparent - overlays from 1-bit images. You can pass the object itself to - Tkinter. The constructor takes the same arguments as the Tkinter - BitmapImage class; use the "foreground" option to set the colour - of the overlay. - -+ Added a "putdata" method to the Image class. This can be used to - load a 1-layer image with data from a sequence object or a string. - An optional floating point scale and offset can be used to adjust - the data to fit into the 8-bit pixel range. Also see the "getdata" - method. - -+ Added the EXTENT method to the Image "transform" method. This can - be used to quickly crop, stretch, shrink, or mirror a subregion - from another image. - -+ Adapted to Python 1.4. - -+ Added a project makefile for Visual C++ 4.x. This allows you to - easily build a dynamically linked version of PIL for Windows 95 - and NT. - -+ A Tk "booster" patch for Windows is available. It gives dramatic - performance improvements for some displays. Has been tested with - Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk - subdirectory for details. - -+ You can now save 1-bit images in the XBM format. In addition, the - Image class now provides a "tobitmap" method which returns a string - containing an XBM representation of the image. Quite handy to use - with Tk. - -+ More conversions, including "RGB" to "1" and more. - -(0.2a1 released) - -+ Where earlier versions accepted lists, this version accepts arbitrary - Python sequences (including strings, in some cases). A few resource - leaks were plugged in the process. - -+ The Image "paste" method now allows the box to extend outside - the target image. The size of the box, the image to be pasted, - and the optional mask must still match. - -+ The ImageDraw module now supports filled polygons, outlined and - filled ellipses, and text. Font support is rudimentary, though. - -+ The Image "point" method now takes an optional mode argument, - allowing you to convert the image while translating it. Currently, - this can only be used to convert "L" or "P" images to "1" images - (creating thresholded images or "matte" masks). - -+ An Image "getpixel" method has been added. For single band images, - it returns the pixel value at a given position as an integer. - For n-band images, it returns an n-tuple of integers. - -+ An Image "getdata" method has been added. It returns a sequence - object representing the image as a 1-dimensional array. Only len() - and [] can be used with this sequence. This method returns a - reference to the existing image data, so changes in the image - will be immediately reflected in the sequence object. - -+ Fixed alignment problems in the Windows BMP writer. - -+ If converting an "RGB" image to "RGB" or "L", you can give a second - argument containing a colour conversion matrix. - -+ An Image "getbbox" method has been added. It returns the bounding - box of data in an image, considering the value 0 as background. - -+ An Image "offset" method has been added. It returns a new image - where the contents of the image have been offset the given distance - in X and/or Y direction. Data wraps between edges. - -+ Saves PDF images. The driver creates a binary PDF 1.1 files, using - JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding - (same as for PostScript) for other formats. - -+ The "paste" method now accepts "1" masks. Zero means transparent, - any other pixel value means opaque. This is faster than using an - "L" transparency mask. - -+ Properly writes EPS files (and properly prints images to postscript - printers as well). - -+ Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR - files. Cursor animations are not supported. - -+ Fixed alignment problems in the Sun raster loader. - -+ Added "draft" and "thumbnail" methods. The draft method is used - to optimize loading of JPEG and PCD files, the thumbnail method is - used to create a thumbnail representation of an image. - -+ Added Windows display support, via the ImageWin class (see the - handbook for details). - -+ Added raster conversion for EPS files. This requires GNU or Aladdin - Ghostscript, and probably works on UNIX only. - -+ Reads PhotoCD (PCD) images. The base resolution (768x512) can be - read from a PhotoCD file. - -+ Eliminated some compiler warnings. Bindings now compile cleanly in C++ - mode. Note that the Imaging library itself must be compiled in C mode. - -+ Added "bdf2pil.py", which converts BDF fonts into images with associated - metrics. This is definitely work in progress. For info, see description - in script for details. - -+ Fixed a bug in the "ImageEnhance.py" module. - -+ Fixed a bug in the netpbm save hack in "GifImagePlugin.py" - -+ Fixed 90 and 270 degree rotation of rectangular images. - -+ Properly reads 8-bit TIFF palette-color images. - -+ Reads plane separated RGB and CMYK TIFF images. - -+ Added driver debug mode. This is enabled by setting Image.DEBUG - to a non-zero value. Try the -D option to "pilfile.py" and see what - happens. - -+ Don't crash on "atend" constructs in PostScript files. - -+ Only the Image module imports _imaging directly. Other modules - should refer to the binding module as "Image.core". - -*** Changes from release 0.0 to 0.1 (b1) *** - -+ A handbook is available (distributed separately). - -+ The coordinate system is changed so that (0,0) is now located - in the upper left corner. This is in compliancy with ISO 12087 - and 90% of all other image processing and graphics libraries. - -+ Modes "1" (bilevel) and "P" (palette) have been introduced. Note - that bilevel images are stored with one byte per pixel. - -+ The Image "crop" and "paste" methods now accepts None as the - box argument, to refer to the full image (self, that is). - -+ The Image "crop" method now works properly. - -+ The Image "point" method is now available. You can use either a - lookup table or a function taking one argument. - -+ The Image join function has been renamed to "merge". - -+ An Image "composite" function has been added. It is identical - to copy() followed by paste(mask). - -+ An Image "eval" function has been added. It is currently identical - to point(function); that is, only a single image can be processed. - -+ A set of channel operations has been added. See the "ImageChops" - module, test_chops.py, and the handbook for details. - -+ Added the "pilconvert" utility, which converts image files. Note - that the number of output formats are still quite restricted. - -+ Added the "pilfile" utility, which quickly identifies image files - (without loading them, in most cases). - -+ Added the "pilprint" utility, which prints image files to Postscript - printers. - -+ Added a rudimentary version of the "pilview" utility, which is - simple image viewer based on Tk. Only File/Exit and Image/Next - works properly. - -+ An interface to Tk has been added. See "Lib/ImageTk.py" and README - for details. - -+ An interface to Jack Jansen's Img library has been added (thanks to - Jack). This allows you to read images through the Img extensions file - format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. - -+ Postscript printing is provided through the PSDraw module. See the - handbook for details. diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 000000000..756935fe7 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,2200 @@ +Changelog (Pillow) +================== + +2.4.0 (2014-04-01) +------------------ + +- Add arch support for pcc64, s390, s390x, armv7l, aarch64 + [manisandro] + +- Add arch support for ppc + [wiredfool] + +- Correctly quote file names for WindowsViewer command + [cgohlke] + +- Prefer homebrew freetype over X11 freetype (but still allow both) + [dmckeone] + +2.3.0 (2014-01-01) +------------------ + +- Stop leaking filename parameter passed to getfont + [jpharvey] + +- Report availability of LIBTIFF during setup and selftest + [cgohlke] + +- Fix msvc build error C1189: "No Target Architecture" + [cgohlke] + +- Fix memory leak in font_getsize + [wiredfool] + +- Correctly prioritize include and library paths + [ohanar] + +- Image.point fixes for numpy.array and docs + [wiredfool] + +- Save the transparency header by default for PNGs + [wiredfool] + +- Support for PNG tRNS header when converting from RGB->RGBA + [wiredfool] + +- PyQT5 Support + [wiredfool] + +- Updates for saving color tiffs w/compression using libtiff + [wiredfool] + +- 2gigapix image fixes and redux + [wiredfool] + +- Save arbitrary tags in Tiff image files + [wiredfool] + +- Quote filenames and title before using on command line + [tmccombs] + +- Fixed Viewer.show to return properly + [tmccombs] + +- Documentation fixes + [wiredfool] + +- Fixed memory leak saving images as webp when webpmux is available + [cezarsa] + +- Fix compiling with FreeType 2.5.1 + [stromnov] + +- Adds directories for NetBSD. + [deepy] + +- Support RGBA TIFF with missing ExtraSamples tag + [cgohlke] + +- Lossless WEBP Support + [wiredfool] + +- Take compression as an option in the save call for tiffs + [wiredfool] + +- Add support for saving lossless WebP. Just pass 'lossless=True' to save() + [liftoff] + +- LCMS support upgraded from version 1 to version 2, fixes #343 + [wiredfool] + +- Added more raw decoder 16 bit pixel formats + [svanheulen] + +- Document remaining Image* modules listed in PIL handbook + [irksep] + +- Document ImageEnhance, ImageFile, ImageFilter, ImageFont, ImageGrab, ImageMath, and ImageOps + [irksep] + +- Port and update docs for Image, ImageChops, ImageColor, and ImageDraw + [irksep] + +- Move or copy content from README.rst to docs/ + [irksep] + +- Respect CFLAGS/LDFLAGS when searching for headers/libs + [iElectric] + +- Port PIL Handbook tutorial and appendices + [irksep] + +- Alpha Premultiplication support for transform and resize + [wiredfool] + +- Fixes to make Pypy 2.1.0 work on Ubuntu 12.04/64 + [wiredfool] + +2.2.2 (2013-12-11) +------------------ + +- Fix #427: compiling with FreeType 2.5.1 + [stromnov] + +2.2.1 (2013-10-02) +------------------ + +- Fix #356: Error installing Pillow 2.2.0 on Mac OS X (due to hard dep on brew) + [wiredfool] + +2.2.0 (2013-10-02) +------------------ + +- Fix #254: Bug in image transformations resulting from uninitialized memory + [nikmolnar] + +- Fix for encoding of b_whitespace, similar to closed issue #272 + [mhogg] + +- Fix #273: Add numpy array interface support for 16 and 32 bit integer modes + [cgohlke] + +- Partial fix for #290: Add preliminary support for TIFF tags. + [wiredfool] + +- Fix #251 and #326: circumvent classification of pngtest_bad.png as malware + [cgohlke] + +- Add typedef uint64_t for MSVC. + [cgohlke] + +- Fix #329: setup.py: better support for C_INCLUDE_PATH, LD_RUN_PATH, etc. + [nu774] + +- Fix #328: _imagingcms.c: include windef.h to fix build issue on MSVC + [nu774] + +- Automatically discover homebrew include/ and lib/ paths on OSX + [donspaulding] + +- Fix bytes which should be bytearray + [manisandro] + +- Add respective paths for C_INCLUDE_PATH, LD_RUN_PATH (rpath) to build + if specified as environment variables. + [seanupton] + +- Fix #312 + gif optimize improvement + [d-schmidt] + +- Be more tolerant of tag read failures + [ericbuehl] + +- Fix #318: Catch truncated zTXt errors. + [vytisb] + +- Fix IOError when saving progressive JPEGs. + [e98cuenc] + +- Add RGBA support to ImageColor + [yoavweiss] + +- Fix #304: test for `str`, not `"utf-8"`. + [mjpieters] + +- Fix missing import os in _util.py. + [mnowotka] + +- Added missing exif tags. + [freyes] + +- Fail on all import errors, fixes #298. + [macfreek, wiredfool] + +- Fixed Windows fallback (wasn't using correct file in Windows fonts). + [lmollea] + +- Moved ImageFile and ImageFileIO comments to docstrings. + [freyes] + +- Restore compatibility with ISO C. + [cgohlke] + +- Use correct format character for C int type. + [cgohlke] + +- Allocate enough memory to hold pointers in encode.c. + [cgohlke] + +- Fix #279, fillorder double shuffling bug when FillOrder ==2 and decoding using libtiff. + [wiredfool] + +- Moved Image module comments to docstrings. + [freyes] + +- Add 16-bit TIFF support, fixes #274. + [wiredfool] + +- Ignore high ascii characters in string.whitespace, fixes #272. + [wiredfool] + +- Added clean/build to tox to make it behave like travis. + [freyes] + +- Adding support for metadata in webp images. + [heynemann] + +2.1.0 (2013-07-02) +------------------ + +- Add /usr/bin/env python shebangs to all scripts in /Scripts. + +- Add several TIFF decoders and encoders. + +- Added support for alpha transparent webp images. + +- Adding Python 3 support for StringIO. + +- Adding Python3 basestring compatibility without changing basestring. + +- Fix webp encode errors on win-amd64. + +- Better fix for ZeroDivisionError in ImageOps.fit for image.size height is 1. + +- Better support for ICO images. + +- Changed PY_VERSION_HEX, fixes #166. + +- Changes to put everything under the PIL namespace. + [wiredfool] + +- Changing StringIO to BytesIO. + +- Cleanup whitespace. + [Arfrever] + +- Don't skip 'import site' on initialization when running tests for inplace builds. + [cgohlke] + +- Enable warnings for test suite. + +- Fix for ZeroDivisionError in ImageOps.fit for image.size == (1,1) + +- Fix for if isinstance(filter, collections.Callable) crash. Python bug #7624 on <2.6.6 + +- Fix #193: remove double typedef declaration. + +- Fix msvc compile errors (#230). + +- Fix rendered characters have been chipped for some TrueType fonts. + +- Fix usage of pilfont.py script. + +- Fresh start for docs, generated by sphinx-apidoc. + +- Introduce --enable-x and fail if it is given and x is not available. + +- Partial work to add a wrapper for WebPGetFeatures to correctly support #204. + +- Significant performance improvement of `alpha_composite` function. + +- Support explicitly disabling features via --disable-* options. + +- Support selftest.py --installed, fixes #263. + +- Transparent WebP Support, #204 + +- Use PyCapsule for py3.1, fixes #237. + +- Workaround for: http://bugs.python.org/16754 in 3.2.x < 3.2.4 and 3.3.0. + +2.0.0 (2013-03-15) +------------------ + +- Add Python 3 support. (Pillow >= 2.0.0 supports Python 2.6, 2.7, 3.2, 3.3. Pillow < 2.0.0 supports Python 2.4, 2.5, 2.6, 2.7.) + [fluggo] + +- Add PyPy support (experimental, please see: https://github.com/python-imaging/Pillow/issues/67) + +- Add WebP support. + [lqs] + +- Add Tiff G3/G4 support (experimental) + [wiredfool] + +- Backport PIL's PNG/Zip improvements. + [olt] + +- Various 64 bit and Windows fixes. + [cgohlke] + +- Add testing suite. + [cgohlke, fluggo] + +- Added support for PNG images with transparency palette. + [d-schmidt] + +- Many other bug fixes and enhancements by many other people (see commit log and/or docs/CONTRIBUTORS.txt). + +- Special thanks to Christoph Gohlke and Eric Soroos for rallying around the effort to get a release out for PyCon 2013. + +1.7.8 (2012-11-01) +------------------ + +- Removed doctests.py that made tests of other packages fail. + [thomasdesvenain] + +- Fix opening psd files with RGBA layers when A mode is not of type 65535 but 3. + Fixes #3 + [thomasdesvenain] + + +1.7.7 (2012-04-04) +------------------ + +- UNDEF more types before including windows headers + [mattip] + +1.7.6 (2012-01-20) +------------------ + +- Bug fix: freetype not found on Mac OS X with case-sensitive filesystem + [gjo] + +- Bug fix: Backport fix to split() after open() (regression introduced in PIL 1.1.7). + [sfllaw] + +1.7.5 (2011-09-07) +------------------ + +- Fix for sys.platform = "linux3" + [blueyed] + +- Package cleanup and additional documentation + [aclark] + +1.7.4 (2011-07-21) +------------------ + +- Fix brown bag release + [aclark] + +1.7.3 (2011-07-20) +------------------ + +- Fix : resize need int values, append int conversion in thumbnail method + [harobed] + +1.7.2 (2011-06-02) +------------------ + +- Bug fix: Python 2.4 compat + [aclark] + +1.7.1 (2011-05-31) +------------------ + +- More multi-arch support + [SteveM, regebro, barry, aclark] + +1.7.0 (2011-05-27) +------------------ + +- Add support for multi-arch library directory /usr/lib/x86_64-linux-gnu + [aclark] + +1.6 (12/01/2010) +---------------- + +- Bug fix: /usr/x11/include should be added to include_dirs not library_dirs + [elro] + +- Doc fixes + +1.5 (11/28/2010) +---------------- + +- Module and package fixes + +1.4 (11/28/2010) +---------------- + +- Doc fixes + +1.3 (11/28/2010) +---------------- + +- Add support for /lib64 and /usr/lib64 library directories on Linux +- Doc fixes + +1.2 (08/02/2010) +---------------- + +- On OS X also check for freetype2 in the X11 path [jezdez] +- Doc fixes [aclark] + +1.1 (07/31/2010) +---------------- + +- Removed setuptools_hg requirement +- Doc fixes + +1.0 (07/30/2010) +---------------- + +- Forked PIL based on Hanno Schlichting's re-packaging + (http://dist.plone.org/thirdparty/PIL-1.1.7.tar.gz) + +- Remove support for importing from the standard namespace + +.. Note:: What follows is the original PIL 1.1.7 CHANGES file contents + +:: + + -*- coding: utf-8 -*- + + The Python Imaging Library + $Id$ + + ACKNOWLEDGEMENTS: PIL wouldn't be what it is without the help of: + David Ascher, Phil Austin, Douglas Bagnall, Larry Bates, Anthony + Baxter, William Baxter, Denis Benoit, Jan Blom, Duncan Booth, Alexey + Borzenkov, Jeff Breidenbach, Roger Burnham, Zac Burns, Gene Cash, + Kevin Cazabon, Fred Clare, Greg Coats, Chris Cogdon, Greg Couch, Bill + Crutchfield, Abel Deuring, Tim Docker, Fred Drake, Graham Dumpleton, + Matthew Ellis, Eric Etheridge, Daniel Fetchinson, Robin Friedrich, + Pier Paolo Glave, Federico Di Gregorio, Markus Gritsch, Daniel + Haertle, Greg Hamilton, Mark Hammond, Bernhard Herzog, Rob Hooft, Bob + Ippolito, Jack Jansen, Bill Janssen, Edward Jones, Richard Jones, + Håkan Karlsson, Robert Kern, David Kirtley, Bob Klimek, Matthias + Klose, Andrew Kuchling, Magnus Källström, Victor Lacina, Ben Last, + Hamish Lawson, Cesare Leonardi, Andrew MacIntyre, Jan Matejek, Naveen + Michaud-Agrawal, Gordon McMillan, Skip Montanaro, Fredrik Nehr, + Russell Nelson, Luciano Nocera, Travis Oliphant, Piet van Oostrum, + Richard Oudkerk, Paul Pharr, Andres Polit, Conrado Porto Lopes Gouvêa, + Eric Raymond, Victor Reijs, Bertil Reinhammar, Nicholas Riley, Don + Rozenberg, Toby Sargeant, Barry Scott, Les Schaffer, Joel Shprentz, + Klamer Shutte, Gene Skonicki, Niki Spahiev, D. Alan Stewart, Perry + Stoll, Paul Svensson, Ulrik Svensson, Miki Tebeka, Michael van + Tellingen, Ivan Tkatchev, Dan Torop, Adam Twardoch, Rune Uhlin, Dmitry + Vasiliev, Sasha Voynow, Charles Waldman, Collin Winter, Dan Wolfe, + Ka-Ping Yee, and many others (if your name should be on this list, let + me know.) + + *** Changes from release 1.1.6 to 1.1.7 *** + + This section may not be fully complete. For changes since this file + was last updated, see the repository revision history: + + http://bitbucket.org/effbot/pil-2009-raclette/changesets/ + + (1.1.7 final) + + + Set GIF loop info property to the number of iterations if a NETSCAPE + loop extension is present, instead of always setting it to 1 (from + Valentino Volonghi). + + (1.1.7c1 released) + + + Improved PNG compression (from Alexey Borzenkov). + + + Read interlaced PNG files (from Conrado Porto Lopes Gouvêa) + + + Added various TGA improvements from Alexey Borzenkov, including + support for specifying image orientation. + + + Bumped block threshold to 16 megabytes, made size estimation a bit + more accurate. This speeds up allocation of large images. + + + Fixed rounding error in ImagingDrawWideLine. + + "gormish" writes: ImagingDrawWideLine() in Draw.c has a bug in every + version I've seen, which leads to different width lines depending on + the order of the points in the line. This is especially bad at some + angles where a 'width=2' line can completely disappear. + + + Added support for RGBA mode to the SGI module (based on code by + Karsten Hiddemann). + + + Handle repeated IPTC tags (adapted from a patch by Eric Bruning). + + Eric writes: According to the specification, some IPTC tags can be + repeated, e.g., tag 2:25 (keywords). PIL 1.1.6 only retained the last + instance of that tag. Below is a patch to store all tags. If there are + multiple tag instances, they are stored in a (python) list. Single tag + instances remain as strings. + + + Fixed potential crash in ImageFilter for small target images + (reported by Zac Burns and Daniel Fetchinson). + + + Use BMP instead of JPEG as temporary show format on Mac OS X. + + + Fixed putpixel/new for I;16 with colors > 255. + + + Added integer power support to ImagingMath. + + + Added limited support for I;16L mode (explicit little endian). + + + Moved WMF support into Image.core; enable WMF rendering by default + if renderer is available. + + + Mark the ARG plugin as obsolete. + + + Added version query mechanism to ImageCms and ImageFont, for + debugging. + + + Added (experimental) ImageCms function for fetching the ICC profile + for the current display (currently Windows only). + + Added HWND/HDC support to ImageCms.get_display_profile(). + + + Added WMF renderer (Windows only). + + + Added ImagePointHandler and ImageTransformHandler mixins; made + ImageCmsTransform work with im.point. + + + Fixed potential endless loop in the XVThumbnail reader (from Nikolai + Ugelvik). + + + Added Kevin Cazabon's pyCMS package. + + The C code has been moved to _imagingcms.c, the Python interface + module is installed as PIL.ImageCMS. + + Added support for in-memory ICC profiles. + + Unified buildTransform and buildTransformFromOpenProfiles. + + The profile can now be either a filename, a profile object, or a + file-like object containing an in-memory profile. + + Additional fixes from Florian Böch: + + Very nice - it just needs LCMS flags support so we can use black + point compensation and softproofing :) See attached patches. They + also fix a naming issue which could cause confusion - display + profile (ImageCms wording) actually means proof profile (lcms + wording), so I changed variable names and docstrings where + applicable. Patches are tested under Python 2.6. + + + Improved support for layer names in PSD files (from Sylvain Baubeau) + + Sylvain writes: I needed to be able to retrieve the names of the + layers in a PSD files. But PsdImagePlugin.py didn't do the job so I + wrote this very small patch. + + + Improved RGBA support for ImageTk for 8.4 and newer (from Con + Radchenko). + + This replaces the slow run-length based encoding model with true + compositing at the Tk level. + + + Added support for 16- and 32-bit images to McIdas loader. + + Based on file samples and stand-alone reader code provided by Craig + Swank. + + + Added ImagePalette support to putpalette. + + + Fixed problem with incremental parsing of PNG files. + + + Make selftest.py report non-zero status on failure (from Mark + Sienkiewicz) + + + Add big endian save support and multipage infrastructure to the TIFF + writer (from Sebastian Haase). + + + Handle files with GPS IFD but no basic EXIF IFD (reported by Kurt + Schwehr). + + + Added zTXT support (from Andrew Kuchling via Lowell Alleman). + + + Fixed potential infinite loop bug in ImageFont (from Guilherme Polo). + + + Added sample ICC profiles (from Kevin Cazabon) + + + Fixed array interface for I, F, and RGBA/RGBX images. + + + Added Chroma subsampling support for JPEG (from Justin Huff). + + Justin writes: Attached is a patch (against PIL 1.1.6) to provide + control over the chroma subsampling done by the JPEG encoder. This + is often useful for reducing compression artifacts around edges of + clipart and text. + + + Added USM/Gaussian Blur code from Kevin Cazabon. + + + Fixed bug w. uninitialized image data when cropping outside the + source image. + + + Use ImageShow to implement the Image.show method. + + Most notably, this picks the 'display' utility when available. It + also allows application code to register new display utilities via + the ImageShow registry. + + + Release the GIL in the PNG compressor (from Michael van Tellingen). + + + Revised JPEG CMYK handling. + + Always assume Adobe behaviour, both when reading and writing (based on + a patch by Kevin Cazabon, and test data by Tim V. and Charlie Clark, and + additional debugging by Michael van Tellingen). + + + Support for preserving ICC profiles (by Florian Böch via Tim Hatch). + + Florian writes: + + It's a beta, so still needs some testing, but should allow you to: + - retain embedded ICC profiles when saving from/to JPEG, PNG, TIFF. + Existing code doesn't need to be changed. + - access embedded profiles in JPEG, PNG, PSD, TIFF. + + It also includes patches for TIFF to retain IPTC, Photoshop and XMP + metadata when saving as TIFF again, read/write TIFF resolution + information correctly, and to correct inverted CMYK JPEG files. + + + Fixed potential memory leak in median cut quantizer (from Evgeny Salmin). + + + Fixed OverflowError when reading upside-down BMP images. + + + Added resolution save option for PDF files. + + Andreas Kostyrka writes: I've included a patched PdfImagePlugin.py + based on 1.1.6 as included in Ubuntu, that supports a "resolution" + save option. Not great, but it makes the PDF saving more useful by + allowing PDFs that are not exactly 72dpi. + + + Look for Tcl/Tk include files in version-specific include directory + (from Encolpe Degoute). + + + Fixed grayscale rounding error in ImageColor.getcolor (from Tim + Hatch). + + + Fixed calculation of mean value in ImageEnhance.Contrast (reported + by "roop" and Scott David Daniels). + + + Fixed truetype positioning when first character has a negative left + bearing (from Ned Batchelder): + + Ned writes: In PIL 1.1.6, ImageDraw.text will position the string + incorrectly if the first character has a negative left bearing. To + see the problem, show a string like "///" in an italic font. The + first slash will be clipped at the left, and the string will be + mis-positioned. + + + Fixed resolution unit bug in tiff reader/writer (based on code by + Florian Höch, Gary Bloom, and others). + + + Added simple transparency support for RGB images (reported by + Sebastian Spaeth). + + + Added support for Unicode filenames in ImageFont.truetype (from Donn + Ingle). + + + Fixed potential crash in ImageFont.getname method (from Donn Ingle). + + + Fixed encoding issue in PIL/WalImageFile (from Santiago M. Mola). + + *** Changes from release 1.1.5 to 1.1.6 *** + + (1.1.6 released) + + + Fixed some 64-bit compatibility warnings for Python 2.5. + + + Added threading support for the Sane driver (from Abel Deuring). + + (1.1.6b2 released) + + + Added experimental "floodfill" function to the ImageDraw module + (based on code by Eric Raymond). + + + The default arguments for "frombuffer" doesn't match "fromstring" + and the documentation; this is a bug, and will most likely be fixed + in a future version. In this release, PIL prints a warning message + instead. To silence the warning, change any calls of the form + "frombuffer(mode, size, data)" to + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + + Added "fromarray" function, which takes an object implementing the + NumPy array interface and creates a PIL Image from it. (from Travis + Oliphant). + + + Added NumPy array interface support (__array_interface__) to the + Image class (based on code by Travis Oliphant). + + This allows you to easily convert between PIL image memories and + NumPy arrays: + + import numpy, Image + + im = Image.open('lena.jpg') + + a = numpy.asarray(im) # a is readonly + + im = Image.fromarray(a) + + + Fixed CMYK polarity for JPEG images, by treating all images as + "Adobe CMYK" images. (thanks to Cesare Leonardi and Kevin Cazabon + for samples, debugging, and patches). + + (1.1.6b1 released) + + + Added 'expand' option to the Image 'rotate' method. If true, the + output image is made large enough to hold the entire rotated image. + + + Changed the ImageDraw 'line' method to always draw the last pixel in + a polyline, independent of line angle. + + + Fixed bearing calculation and clipping in the ImageFont truetype + renderer; this could lead to clipped text, or crashes in the low- + level _imagingft module. (based on input from Adam Twardoch and + others). + + + Added ImageQt wrapper module, for converting PIL Image objects to + QImage objects in an efficient way. + + + Fixed 'getmodebands' to return the number of bands also for "PA" + and "LA" modes. Added 'getmodebandnames' helper that return the + band names. + + (1.1.6a2 released) + + + Added float/double support to the TIFF loader (from Russell + Nelson). + + + Fixed broken use of realloc() in path.c (from Jan Matejek) + + + Added save support for Spider images (from William Baxter). + + + Fixed broken 'paste' and 'resize' operations in pildriver + (from Bill Janssen). + + + Added support for duplex scanning to the Sane interface (Abel + Deuring). + + (1.1.6a1 released) + + + Fixed a memory leak in "convert(mode)", when converting from + L to P. + + + Added pixel access object. The "load" method now returns a + access object that can be used to directly get and set pixel + values, using ordinary [x, y] notation: + + pixel = im.load() + v = pixel[x, y] + pixel[x, y] = v + + If you're accessing more than a few pixels, this is a lot + faster than using getpixel/putpixel. + + + Fixed building on Cygwin (from Miki Tebeka). + + + Fixed "point(callable)" on unloaded images (reported by Håkan + Karlsson). + + + Fixed size bug in ImageWin.ImageWindow constructor (from Victor + Reijs) + + + Fixed ImageMath float() and int() operations for Python 2.4 + (reported by Don Rozenberg). + + + Fixed "RuntimeError: encoder error -8 in tostring" problem for + wide "RGB", "I", and "F" images. + + + Fixed line width calculation. + + (1.1.6a0 released) + + + Fixed byte order issue in Image.paste(ink) (from Ka-Ping Yee). + + + Fixed off-by-0.5 errors in the ANTIALIAS code (based on input + from Douglas Bagnall). + + + Added buffer interface support to the Path constructor. If + a buffer is provided, it is assumed to contain a flat array + of float coordinates (e.g. array.array('f', seq)). + + + Added new ImageMath module. + + + Fixed ImageOps.equalize when used with a small number of distinct + values (reported by David Kirtley). + + + Fixed potential integer division in PSDraw.image (from Eric Etheridge). + + *** Changes from release 1.1 to 1.1.5 *** + + (1.1.5c2 and 1.1.5 final released) + + + Added experimental PERSPECTIVE transform method (from Jeff Breiden- + bach). + + (1.1.5c1 released) + + + Make sure "thumbnail" never generates zero-wide or zero-high images + (reported by Gene Skonicki) + + + Fixed a "getcolors" bug that could result in a zero count for some + colors (reported by Richard Oudkerk). + + + Changed default "convert" palette to avoid "rounding errors" when + round-tripping white source pixels (reported by Henryk Gerlach and + Jeff Epler). + + (1.1.5b3 released) + + + Don't crash in "quantize" method if the number of colors requested + is larger than 256. This release raises a ValueError exception; + future versions may return a mode "RGB" image instead (reported + by Richard Oudkerk). + + + Added WBMP read/write support (based on code by Duncan Booth). + + (1.1.5b2 released) + + + Added DPI read/write support to the PNG codec. The decoder sets + the info["dpi"] attribute for PNG files with appropriate resolution + settings. The encoder uses the "dpi" option (based on code by Niki + Spahiev). + + + Added limited support for "point" mappings from mode "I" to mode "L". + Only 16-bit values are supported (other values are clipped), the lookup + table must contain exactly 65536 entries, and the mode argument must be + set to "L". + + + Added support for Mac OS X icns files (based on code by Bob Ippolito). + + + Added "ModeFilter" support to the ImageFilter module. + + + Added support for Spider images (from William Baxter). See the + comments in PIL/SpiderImagePlugin.py for more information on this + format. + + (1.1.5b1 released) + + + Added new Sane release (from Ralph Heinkel). See the Sane/README + and Sane/CHANGES files for more information. + + + Added experimental PngInfo chunk container to the PngImageFile + module. This can be used to add arbitrary chunks to a PNG file. + Create a PngInfo instance, use "add" or "add_text" to add chunks, + and pass the instance as the "pnginfo" option when saving the + file. + + + Added "getpalette" method. This returns the palette as a list, + or None if the image has no palette. To modify the palette, use + "getpalette" to fetch the current palette, modify the list, and + put it back using "putpalette". + + + Added optional flattening to the ImagePath "tolist" method. + tolist() or tolist(0) returns a list of 2-tuples, as before. + tolist(1) returns a flattened list instead. + + (1.1.5a5 released) + + + Fixed BILINEAR/BICUBIC/ANTIALIAS filtering for mode "LA". + + + Added "getcolors()" method. This is similar to the existing histo- + gram method, but looks at color values instead of individual layers, + and returns an unsorted list of (count, color) tuples. + + By default, the method returns None if finds more than 256 colors. + If you need to look for more colors, you can pass in a limit (this + is used to allocate internal tables, so you probably don't want to + pass in too large values). + + + Build improvements: Fixed building under AIX, improved detection of + FreeType2 and Mac OS X framework libraries, and more. Many thanks + to everyone who helped test the new "setup.py" script! + + (1.1.5a4 released) + + + The "save" method now looks for a file format driver before + creating the file. + + + Don't use antialiased truetype fonts when drawing in mode "P", "I", + and "F" images. + + + Rewrote the "setup.py" file. The new version scans for available + support libraries, and configures both the libImaging core library + and the bindings in one step. + + To use specific versions of the libraries, edit the ROOT variables + in the setup.py file. + + + Removed threaded "show" viewer; use the old "show" implementation + instead (Windows). + + + Added deprecation warnings to Image.offset, ImageDraw.setink, and + ImageDraw.setfill. + + + Added width option to ImageDraw.line(). The current implementation + works best for straight lines; it does not support line joins, so + polylines won't look good. + + + ImageDraw.Draw is now a factory function instead of a class. If + you need to create custom draw classes, inherit from the ImageDraw + class. All other code should use the factory function. + + + Fixed loading of certain PCX files (problem reported by Greg + Hamilton, who also provided samples). + + + Changed _imagingft.c to require FreeType 2.1 or newer. The + module can still be built with earlier versions; see comments + in _imagingft.c for details. + + (1.1.5a3 released) + + + Added 'getim' method, which returns a PyCObject wrapping an + Imaging pointer. The description string is set to IMAGING_MAGIC. + See Imaging.h for pointer and string declarations. + + + Fixed reading of TIFF JPEG images (problem reported by Ulrik + Svensson). + + + Made ImageColor work under Python 1.5.2 + + + Fixed division by zero "equalize" on very small images (from + Douglas Bagnall). + + (1.1.5a2 released) + + + The "paste" method now supports the alternative "paste(im, mask)" + syntax (in this case, the box defaults to im's bounding box). + + + The "ImageFile.Parser" class now works also for PNG files with + more than one IDAT block. + + + Added DPI read/write to the TIFF codec, and fixed writing of + rational values. The decoder sets the info["dpi"] attribute + for TIFF files with appropriate resolution settings. The + encoder uses the "dpi" option. + + + Disable interlacing for small (or narrow) GIF images, to + work around what appears to be a hard-to-find bug in PIL's + GIF encoder. + + + Fixed writing of mode "P" PDF images. Made mode "1" PDF + images smaller. + + + Made the XBM reader a bit more robust; the file may now start + with a few whitespace characters. + + + Added support for enhanced metafiles to the WMF driver. The + separate PILWMF kit lets you render both placeable WMF files + and EMF files as raster images. See + + http://effbot.org/downloads#pilwmf + + (1.1.5a1 released) + + + Replaced broken WMF driver with a WMF stub plugin (see below). + + + Fixed writing of mode "1", "L", and "CMYK" PDF images (based on + input from Nicholas Riley and others). + + + Fixed adaptive palette conversion for zero-width or zero-height + images (from Chris Cogdon) + + + Fixed reading of PNG images from QuickTime 6 (from Paul Pharr) + + + Added support for StubImageFile plugins, including stub plugins + for BUFR, FITS, GRIB, and HDF5 files. A stub plugin can identify + a given file format, but relies on application code to open and + save files in that format. + + + Added optional "encoding" argument to the ImageFont.truetype + factory. This argument can be used to specify non-Unicode character + maps for fonts that support that. For example, to draw text using + the Microsoft Symbol font, use: + + font = ImageFont.truetype("symbol.ttf", 16, encoding="symb") + draw.text((0, 0), unichr(0xF000 + 0xAA)) + + (note that the symbol font uses characters in the 0xF000-0xF0FF + range) + + 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. + + + Made "putalpha" a bit more robust; you can now attach an alpha + layer to a plain "L" or "RGB" image, and you can also specify + constant alphas instead of alpha layers (using integers or colour + names). + + + Added experimental "LA" mode support. + + An "LA" image is an "L" image with an attached transparency layer. + Note that support for "LA" is not complete; some operations may + fail or produce unexpected results. + + + Added "RankFilter", "MinFilter", "MedianFilter", and "MaxFilter" + classes to the ImageFilter module. + + + Improved support for applications using multiple threads; PIL + now releases the global interpreter lock for many CPU-intensive + operations (based on work by Kevin Cazabon). + + + Ignore Unicode characters in the PCF loader (from Andres Polit) + + + Fixed typo in OleFileIO.loadfat, which could affect loading of + FlashPix and Image Composer images (Daniel Haertle) + + + Fixed building on platforms that have Freetype but don't have + Tcl/Tk (Jack Jansen, Luciano Nocera, Piet van Oostrum and others) + + + Added EXIF GPSInfo read support for JPEG files. To extract + GPSInfo information, open the file, extract the exif dictionary, + and check for the key 0x8825 (GPSInfo). If present, it contains + a dictionary mapping GPS keys to GPS values. For a list of keys, + see the EXIF specification. + + The "ExifTags" module contains a GPSTAGS dictionary mapping GPS + tags to tag names. + + + Added DPI read support to the PCX and DCX codecs (info["dpi"]). + + + The "show" methods now uses a built-in image viewer on Windows. + This viewer creates an instance of the ImageWindow class (see + below) and keeps it running in a separate thread. NOTE: This + was disabled in 1.1.5a4. + + + Added experimental "Window" and "ImageWindow" classes to the + ImageWin module. These classes allow you to create a WCK-style + toplevel window, and use it to display raster data. + + + Fixed some Python 1.5.2 issues (to build under 1.5.2, use the + Makefile.pre.in/Setup.in approach) + + + Added support for the TIFF FillOrder tag. PIL can read mode "1", + "L", "P" and "RGB" images with non-standard FillOrder (based on + input from Jeff Breidenbach). + + (1.1.4 final released) + + + Fixed ImageTk build problem on Unix. + + (1.1.4b2 released) + + + Improved building on Mac OS X (from Jack Jansen). + + + Improved building on Windows with MinGW (from Klamer Shutte). + + + If no font is specified, ImageDraw now uses the embedded default + font. Use the "load" or "truetype" methods to load a real font. + + + Added embedded default font to the ImageFont module (currently + an 8-pixel Courier font, taken from the X window distribution). + + (1.1.4b1 released) + + + Added experimental EXIF support for JPEG files. To extract EXIF + information from a JPEG file, open the file as usual, and call the + "_getexif" method. If successful, this method returns a dictionary + mapping EXIF TIFF tags to values. If the file does not contain EXIF + data, the "_getexif" method returns None. + + The "ExifTags" module contains a dictionary mapping tags to tag + names. + + This interface will most likely change in future versions. + + + Fixed a bug when using the "transparency" option with the GIF + writer. + + + Added limited support for "bitfield compression" in BMP files + and DIB buffers, for 15-bit, 16-bit, and 32-bit images. This + also fixes a problem with ImageGrab module when copying screen- + dumps from the clipboard on 15/16/32-bit displays. + + + Added experimental WAL (Quake 2 textures) loader. To use this + loader, import WalImageFile and call the "open" method in that + module. + + (1.1.4a4 released) + + + Added updated SANE driver (Andrew Kuchling, Abel Deuring) + + + Use Python's "mmap" module on non-Windows platforms to read some + uncompressed formats using memory mapping. Also added a "frombuffer" + function that allows you to access the contents of an existing string + or buffer object as if it were an image object. + + + Fixed a memory leak that could appear when processing mode "P" + images (from Pier Paolo Glave) + + + Ignore Unicode characters in the BDF loader (from Graham Dumpleton) + + (1.1.4a3 released; windows only) + + + Added experimental RGBA-on-RGB drawing support. To use RGBA + colours on an RGB image, pass "RGBA" as the second string to + the ImageDraw.Draw constructor. + + + Added support for non-ASCII strings (Latin-1) and Unicode + to the truetype font renderer. + + + The ImageWin "Dib" object can now be constructed directly from + an image object. + + + The ImageWin module now allows you use window handles as well + as device contexts. To use a window handle, wrap the handle in + an ImageWin.HWND object, and pass in this object instead of the + device context. + + (1.1.4a2 released) + + + Improved support for 16-bit unsigned integer images (mode "I;16"). + This includes TIFF reader support, and support for "getextrema" + and "point" (from Klamer Shutte). + + + Made the BdfFontFile reader a bit more robust (from Kevin Cazabon + and Dmitry Vasiliev) + + + Changed TIFF writer to always write Compression tag, even when + using the default compression (from Greg Couch). + + + Added "show" support for Mac OS X (from Dan Wolfe). + + + Added clipboard support to the "ImageGrab" module (Windows only). + The "grabclipboard" function returns an Image object, a list of + filenames (not in 1.1.4), or None if neither was found. + + (1.1.4a1 released) + + + Improved support for drawing RGB data in palette images. You can + now use RGB tuples or colour names (see below) when drawing in a + mode "P" image. The drawing layer automatically assigns color + indexes, as long as you don't use more than 256 unique colours. + + + Moved self test from MiniTest/test.py to ./selftest.py. + + + Added support for CSS3-style color strings to most places that + accept colour codes/tuples. This includes the "ImageDraw" module, + the Image "new" function, and the Image "paste" method. + + Colour strings can use one of the following formats: "#f00", + "#ff0000", "rgb(255,0,0)", "rgb(100%,0%,0%)", "hsl(0, 100%, 50%)", + or "red" (most X11-style colour names are supported). See the + documentation for the "ImageColor" module for more information. + + + Fixed DCX decoder (based on input from Larry Bates) + + + Added "IptcImagePlugin.getiptcinfo" helper to extract IPTC/NAA + newsphoto properties from JPEG, TIFF, or IPTC files. + + + Support for TrueType/OpenType fonts has been added to + the standard distribution. You need the freetype 2.0 + library. + + + Made the PCX reader a bit more robust when reading 2-bit + and 4-bit PCX images with odd image sizes. + + + Added "Kernel" class to the ImageFilter module. This class + allows you to filter images with user-defined 3x3 and 5x5 + convolution kernels. + + + Added "putdata" support for mode "I", "F" and "RGB". + + + The GIF writer now supports the transparency option (from + Denis Benoit). + + + A HTML version of the module documentation is now shipped + with the source code distribution. You'll find the files in + the Doc subdirectory. + + + Added support for Palm pixmaps (from Bill Janssen). This + change was listed for 1.1.3, but the "PalmImagePlugin" driver + didn't make it into the distribution. + + + Improved decoder error messages. + + (1.1.3 final released) + + + Made setup.py look for old versions of zlib. For some back- + ground, see: http://www.gzip.org/zlib/advisory-2002-03-11.txt + + (1.1.3c2 released) + + + Added setup.py file (tested on Unix and Windows). You still + need to build libImaging/imaging.lib in the traditional way, + but the setup.py script takes care of the rest. + + The old Setup.in/Makefile.pre.in build method is still + supported. + + + Fixed segmentation violation in ANTIALIAS filter (an internal + buffer wasn't properly allocated). + + (1.1.3c1 released) + + + Added ANTIALIAS downsampling filter for high-quality "resize" + and "thumbnail" operations. Also added filter option to the + "thumbnail" operation; the default value is NEAREST, but this + will most likely change in future versions. + + + Fixed plugin loader to be more robust if the __file__ + variable isn't set. + + + Added seek/tell support (for layers) to the PhotoShop + loader. Layer 0 is the main image. + + + Added new (but experimental) "ImageOps" module, which provides + shortcuts for commonly used operations on entire images. + + + Don't mess up when loading PNG images if the decoder leaves + data in the output buffer. This could cause internal errors + on some PNG images, with some versions of ZLIB. (Bug report + and patch provided by Bernhard Herzog.) + + + Don't mess up on Unicode filenames. + + + Don't mess up when drawing on big endian platforms. + + + Made the TIFF loader a bit more robust; it can now read some + more slightly broken TIFF files (based on input from Ted Wright, + Bob Klimek, and D. Alan Stewart) + + + Added OS/2 EMX build files (from Andrew MacIntyre) + + + Change "ImageFont" to reject image files if they don't have the + right mode. Older versions could leak memory for "P" images. + (Bug reported by Markus Gritsch). + + + Renamed some internal functions to avoid potential build + problem on Mac OS X. + + + Added DL_EXPORT where relevant (for Cygwin, based on input + from Robert Yodlowski) + + + (re)moved bogus __init__ call in BdfFontFile (bug spotted + by Fred Clare) + + + Added "ImageGrab" support (Windows only) + + + Added support for XBM hotspots (based on code contributed by + Bernhard Herzog). + + + Added write support for more TIFF tags, namely the Artist, + Copyright, DateTime, ResolutionUnit, Software, XResolution and + YResolution tags (from Greg Couch) + + + Added TransposedFont wrapper to ImageFont module + + + Added "optimize" flag to GIF encoder. If optimize is present + and non-zero, PIL will work harder to create a small file. + + + Raise "EOFError" (not IndexError) when reading beyond the + end of a TIFF sequence. + + + Support rewind ("seek(0)") for GIF and TIFF sequences. + + + Load grayscale GIF images as mode "L" + + + Added DPI read/write support to the JPEG codec. The decoder + sets the info["dpi"] attribute for JPEG files with JFIF dpi + settings. The encoder uses the "dpi" option: + + im = Image.open("file.jpg") + dpi = im.info["dpi"] # raises KeyError if DPI not known + im.save("out.jpg", dpi=dpi) + + Note that PIL doesn't always preserve the "info" attribute + for normal image operations. + + (1.1.2c1 and 1.1.2 final released) + + + Adapted to Python 2.1. Among other things, all uses of the + "regex" module has been repleased with "re". + + + Fixed attribute error when reading large PNG files (this bug + was introduced in maintenance code released after the 1.1.1 + release) + + + Ignore non-string objects in sys.path + + + Fixed Image.transform(EXTENT) for negative xoffsets + + + Fixed loading of image plugins if PIL is installed as a package. + (The plugin loader now always looks in the directory where the + Image.py module itself is found, even if that directory isn't on + the standard search path) + + + The Png plugin has been added to the list of preloaded standard + formats + + + Fixed bitmap/text drawing in fill mode. + + + Fixed "getextrema" to work also for multiband images. + + + Added transparency support for L and P images to the PNG codec. + + + Improved support for read-only images. The "load" method now + sets the "readonly" attribute for memory-mapped images. Operations + that modifies an image in place (such as "paste" and drawing operations) + creates an in-memory copy of the image, if necessary. (before this + change, any attempt to modify a memory-mapped image resulted in a + core dump...) + + + Added special cases for lists everywhere PIL expects a sequence. + This should speed up things like "putdata" and drawing operations. + + + The Image.offset method is deprecated. Use the ImageChops.offset + function instead. + + + Changed ImageChops operators to copy palette and info dictionary + from the first image argument. + + (1.1.1 released) + + + Additional fixes for Python 1.6/2.0, including TIFF "save" bug. + + + Changed "init" to properly load plugins when PIL is used as a + package. + + + Fixed broken "show" method (on Unix) + + *** Changes from release 1.0 to 1.1 *** + + + Adapted to Python 1.6 ("append" and other method changes) + + + Fixed Image.paste when pasting with solid colour and matte + layers ("L" or "RGBA" masks) (bug reported by Robert Kern) + + + To make it easier to distribute prebuilt versions of PIL, + the tkinit binding stuff has been moved to a separate + extension module, named "_imagingtk". + + *** Changes from release 0.3b2 to 1.0 final *** + + + If there's no 16-bit integer (like on a Cray T3E), set + INT16 to the smallest integer available. Most of the + library works just fine anyway (from Bill Crutchfield) + + + Tweaks to make drawing work on big-endian platforms. + + (1.0c2 released) + + + If PIL is built with the WITH_TKINTER flag, ImageTk can + automatically hook into a standard Tkinter build. You + no longer need to build your own Tkinter to use the + ImageTk module. + + The old way still works, though. For more information, + see Tk/install.txt. + + + Some tweaks to ImageTk to support multiple Tk interpreters + (from Greg Couch). + + + ImageFont "load_path" now scans directory mentioned in .pth + files (from Richard Jones). + + (1.0c1 released) + + + The TIFF plugin has been rewritten. The new plugin fully + supports all major PIL image modes (including F and I). + + + The ImageFile module now includes a Parser class, which can + be used to incrementally decode an image file (while down- + loading it from the net, for example). See the handbook for + details. + + + "show" now converts non-standard modes to "L" or "RGB" (as + appropriate), rather than writing weird things to disk for + "xv" to choke upon. (bug reported by Les Schaffer). + + (1.0b2 released) + + + Major speedups for rotate, transform(EXTENT), and transform(AFFINE) + when using nearest neighbour resampling. + + + Modified ImageDraw to be compatible with the Arrow graphics + interface. See the handbook for details. + + + PIL now automatically loads file codecs when used as a package + (from The Dragon De Monsyne). Also included an __init__.py file + in the standard distribution. + + + The GIF encoder has been modified to produce much smaller files. + + PIL now uses a run-length encoding method to encode GIF files. + On a random selection of GIF images grabbed from the web, this + version makes the images about twice as large as the original + LZW files, where the earlier version made them over 5 times + larger. YMMV, of course. + + + Added PCX write support (works with "1", "P", "L", and "RGB") + + + Added "bitmap" and "textsize" methods to ImageDraw. + + + Improved font rendering code. Fixed a bug or two, and moved + most of the time critical stuff to C. + + + Removed "bdf2pil.py". Use "pilfont.py" instead! + + + Improved 16-bit support (still experimental, though). + + The following methods now support "I;16" and "I;16B" images: + "getpixel", "copy", "convert" (to and from mode "I"), "resize", + "rotate", and "transform" with nearest neighbour filters, and + "save" using the IM format. The "new" and "open" functions + also work as expected. On Windows, 16-bit files are memory + mapped. + + NOTE: ALL other operations are still UNDEFINED on 16-bit images. + + + The "paste" method now supports constant sources. + + Just pass a colour value (a number or a tuple, depending on + the target image mode) instead of the source image. + + This was in fact implemented in an inefficient way in + earlier versions (the "paste" method generated a temporary + source image if you passed it a colour instead of an image). + In this version, this is handled on the C level instead. + + + Added experimental "RGBa" mode support. + + An "RGBa" image is an RGBA image where the colour components + have have been premultipled with the alpha value. PIL allows + you to convert an RGBA image to an RGBa image, and to paste + RGBa images on top of RGB images. Since this saves a bunch + of multiplications and shifts, it is typically about twice + as fast an ordinary RGBA paste. + + + Eliminated extra conversion step when pasting "RGBA" or "RGBa" + images on top of "RGB" images. + + + Fixed Image.BICUBIC resampling for "RGB" images. + + + Fixed PCX image file handler to properly read 8-bit PCX + files (bug introduced in 1.0b1, reported by Bernhard + Herzog) + + + Fixed PSDraw "image" method to restore the coordinate + system. + + + Fixed "blend" problem when applied to images that was + not already loaded (reported by Edward C. Jones) + + + Fixed -f option to "pilconvert.py" (from Anthony Baxter) + + (1.0b1 released) + + + Added Toby J. Sargeant's quantization package. To enable + quantization, use the "palette" option to "convert": + + imOut = im.convert("P", palette=Image.ADAPTIVE) + + This can be used with "L", "P", and "RGB" images. In this + version, dithering cannot be used with adaptive palettes. + + Note: ADAPTIVE currently maps to median cut quantization + with 256 colours. The quantization package also contains + a maximum coverage quantizer, which will be supported by + future versions of PIL. + + + Added Eric S. Raymond's "pildriver" image calculator to the + distribution. See the docstring for more information. + + + The "offset" method no longer dumps core if given positive + offsets (from Charles Waldman). + + + Fixed a resource leak that could cause ImageWin to run out of + GDI resources (from Roger Burnham). + + + Added "arc", "chord", and "pieslice" methods to ImageDraw (inspired + by code contributed by Richard Jones). + + + Added experimental 16-bit support, via modes "I;16" (little endian + data) and "I;16B" (big endian). Only a few methods properly support + such images (see above). + + + Added XV thumbnail file handler (from Gene Cash). + + + Fixed BMP image file handler to handle palette images with small + palettes (from Rob Hooft). + + + Fixed Sun raster file handler for palette images (from Charles + Waldman). + + + Improved various internal error messages. + + + Fixed Path constructor to handle arbitrary sequence objects. This + also affects the ImageDraw class (from Richard Jones). + + + Fixed a bug in JpegDecode that caused PIL to report "decoder error + -2" for some progressive JPEG files (reported by Magnus Källström, + who also provided samples). + + + Fixed a bug in JpegImagePlugin that caused PIL to hang when loading + JPEG files using 16-bit quantization tables. + + + The Image "transform" method now supports Image.QUAD transforms. + The data argument is an 8-tuple giving the upper left, lower + left, lower right, and upper right corner of the source quadri- + lateral. Also added Image.MESH transform which takes a list + of quadrilaterals. + + + The Image "resize", "rotate", and "transform" methods now support + Image.BILINEAR (2x2) and Image.BICUBIC (4x4) resampling filters. + Filters can be used with all transform methods. + + + The ImageDraw "rectangle" method now includes both the right + and the bottom edges when drawing filled rectangles. + + + The TGA decoder now works properly for runlength encoded images + which have more than one byte per pixel. + + + "getbands" on an YCbCr image now returns ("Y", "Cb", "Cr") + + + Some file drivers didn't handle the optional "modify" argument + to the load method. This resulted in exceptions when you used + "paste" (and other methods that modify an image in place) on a + newly opened file. + + *** Changes from release 0.2 (b5) to 0.3 (b2) *** + + (0.3b2 released) + + The test suite includes 825 individual tests. + + + An Image "getbands" method has been added. It returns a tuple + containing the individual band names for this image. To figure + out how many bands an image has, use "len(im.getbands())". + + + An Image "putpixel" method has been added. + + + The Image "point" method can now be used to convert "L" images + to any other format, via a lookup table. That table should + contain 256 values for each band in the output image. + + + Some file drivers (including FLI/FLC, GIF, and IM) accidently + overwrote the offset method with an internal attribute. All + drivers have been updated to use private attributes where + possible. + + + The Image "histogram" method now works for "I" and "F" images. + For these modes, PIL divides the range between the min and + max values used in the image into 256 bins. You can also + pass in your own min and max values via the "extrema" option: + + h = im.histogram(extrema=(0, 255)) + + + An Image "getextrema" method has been added. It returns the + min and max values used in the image. In this release, this + works for single band images only. + + + Changed the PNG driver to load and save mode "I" images as + 16-bit images. When saving, values outside the range 0..65535 + are clipped. + + + Fixed ImageFont.py to work with the new "pilfont" compiler. + + + Added JPEG "save" and "draft" support for mode "YCbCr" images. + Note that if you save an "YCbCr" image as a JPEG file and read + it back, it is read as an RGB file. To get around this, you + can use the "draft" method: + + im = Image.open("color.jpg") + im.draft("YCbCr", im.size) + + + Read "RGBA" TGA images. Also fixed the orientation bug; all + images should now come out the right way. + + + Changed mode name (and internal representation) from "YCrCb" + to "YCbCr" (!) + *** WARNING: MAY BREAK EXISTING CODE *** + + (0.3b1 released) + + The test suite includes 750 individual tests. + + + The "pilfont" package is now included in the standard PIL + distribution. The pilfont utility can be used to convert + X BDF and PCF raster font files to a format understood by + the ImageFont module. + + + GIF files are now interlaced by default. To write a + non-interlaced file, pass interlace=0 to the "save" + method. + + + The default string format has changed for the "fromstring" + and "tostring" methods. + *** WARNING: MAY BREAK EXISTING CODE *** + + NOTE: If no extra arguments are given, the first line in + the string buffer is the top line of the image, instead of + the bottom line. For RGB images, the string now contains + 3 bytes per pixel instead of 4. These changes were made + to make the methods compatible with the "fromstring" + factory function. + + To get the old behaviour, use the following syntax: + + data = im.tostring("raw", "RGBX", 0, -1) + im.fromstring(data, "raw", "RGBX", 0, -1) + + + "new" no longer gives a MemoryError if the width or height + is zero (this only happened on platforms where malloc(0) + or calloc(0) returns NULL). + + + "new" now adds a default palette object to "P" images. + + + You can now convert directly between all modes supported by + PIL. When converting colour images to "P", PIL defaults to + a "web" palette and dithering. When converting greyscale + images to "1", PIL uses a thresholding and dithering. + + + Added a "dither" option to "convert". By default, "convert" + uses floyd-steinberg error diffusion for "P" and "1" targets, + so this option is only used to *disable* dithering. Allowed + values are NONE (no dithering) or FLOYDSTEINBERG (default). + + imOut = im.convert("P", dither=Image.NONE) + + + Added a full set of "I" decoders. You can use "fromstring" + (and file decoders) to read any standard integer type as an + "I" image. + + + Added some support for "YCbCr" images (creation, conversion + from/to "L" and "RGB", IM YCC load/save) + + + "getpixel" now works properly with fractional coordinates. + + + ImageDraw "setink" now works with "I", "F", "RGB", "RGBA", + "RGBX", "CMYK", and "YCbCr" images. + + + ImImagePlugin no longer attaches palettes to "RGB" images. + + + Various minor fixes. + + (0.3a4 released) + + + Added experimental IPTC/NAA support. + + + Eliminated AttributeError exceptions after "crop" (from + Skip Montanaro) + + + Reads some uncompressed formats via memory mapping (this + is currently supported on Win32 only) + + + Fixed some last minute glitches in the last alpha release + (Types instead of types in Image.py, version numbers, etc.) + + + Eliminated some more bogus compiler warnings. + + + Various fixes to make PIL compile and run smoother on Macs + (from Jack Jansen). + + + Fixed "fromstring" and "tostring" for mode "I" images. + + (0.3a3 released) + + The test suite includes 530 individual tests. + + + Eliminated unexpected side-effect in "paste" with matte. "paste" + now works properly also if compiled with "gcc". + + + Adapted to Python 1.5 (build issues only) + + + Fixed the ImageDraw "point" method to draw also the last + point (!). + + + Added "I" and "RGBX" support to Image.new. + + + The plugin path is now properly prepended to the module search + path when a plugin module is imported. + + + Added "draw" method to the ImageWin.Dib class. This is used by + Topaz to print images on Windows printers. + + + "convert" now supports conversions from "P" to "1" and "F". + + + "paste" can now take a colour instead of an image as the first argument. + The colour must match the colour argument given to the new function, and + match the mode of the target image. + + + Fixed "paste" to allow a mask also for mode "F" images. + + + The BMP driver now saves mode "1" images. When loading images, the mode + is set to "L" for 8-bit files with greyscale palettes, and to "P" for + other 8-bit files. + + + The IM driver now reads and saves "1" images (file modes "0 1" or "L 1"). + + + The JPEG and GIF drivers now saves "1" images. For JPEG, the image + is saved as 8-bit greyscale (it will load as mode "L"). For GIF, the + image will be loaded as a "P" image. + + + Fixed a potential buffer overrun in the GIF encoder. + + (0.3a2 released) + + The test suite includes 400 individual tests. + + + Improvements to the test suite revealed a number of minor bugs, which + are all fixed. Note that crop/paste, 32-bit ImageDraw, and ImageFont + are still weak spots in this release. + + + Added "putpalette" method to the Image class. You can use this + to add or modify the palette for "P" and "L" images. If a palette + is added to an "L" image, it is automatically converted to a "P" + image. + + + Fixed ImageDraw to properly handle 32-bit image memories + ("RGB", "RGBA", "CMYK", "F") + + + Fixed "fromstring" and "tostring" not to mess up the mode attribute + in default mode. + + + Changed ImPlatform.h to work on CRAY's (don't have one at home, so I + haven't tried it). The previous version assumed that either "short" + or "int" were 16-bit wide. PIL still won't compile on platforms where + neither "short", "int" nor "long" are 32-bit wide. + + + Added file= and data= keyword arguments to PhotoImage and BitmapImage. + This allows you to use them as drop-in replacements for the corre- + sponding Tkinter classes. + + + Removed bogus references to the crack coder (ImagingCrack). + + (0.3a1 released) + + + Make sure image is loaded in "tostring". + + + Added floating point packer (native 32-bit floats only). + + *** Changes from release 0.1b1 to 0.2 (b5) *** + + + Modified "fromstring" and "tostring" methods to use file codecs. + Also added "fromstring" factory method to create an image directly + from data in a string. + + + Added support for 32-bit floating point images (mode "F"). You + can convert between "L" and "F" images, and apply a subset of the + available image processing methods on the "F" image. You can also + read virtually any data format into a floating point image memory; + see the section on "Decoding Floating Point Data" in the handbook + for more information. + + (0.2b5 released; on windows only) + + + Fixed the tobitmap() method to work properly for small bitmaps. + + + Added RMS and standard deviation to the ImageStat.Stat class. Also + modified the constructor to take an optional feature mask, and also + to accept either an image or a list containing the histogram data. + + + The BitmapImage code in ImageTk can now use a special bitmap + decoder, which has to be patched into Tk. See the "Tk/pilbitmap.txt" + file for details. If not installed, bitmaps are transferred to Tk as + XBM strings. + + + The PhotoImage code in ImageTk now uses a Tcl command ("PyImagingPaste") + instead of a special image type. This gives somewhat better performance, + and also allows PIL to support transparency. + *** WARNING: TKAPPINIT MUST BE MODIFIED *** + + + ImageTk now honours the alpha layer in RGBA images. Only fully + transparent pixels are made transparent (that is, the alpha layer + is treated as a mask). To treat the alpha laters as a matte, you + must paste the image on the background before handing it over to + ImageTk. + + + Added McIdas reader (supports 8-bit images only). + + + PIL now preloads drivers for BMP, GIF, JPEG, PPM, and TIFF. As + long as you only load and save these formats, you don't have to + wait for a full scan for drivers. To force scanning, call the + Image.init() function. + + + The "seek" and "tell" methods are now always available, also for + single-frame images. + + + Added optional mask argument to histogram method. The mask may + be an "1" or "L" image with the same size as the original image. + Only pixels where the mask is non-zero are included in the + histogram. + + + The "paste" method now allows you to specify only the lower left + corner (a 2-tuple), instead of the full region (a 4-tuple). + + + Reverted to old plugin scanning model; now scans all directory + names in the path when looking for plugins. + + + Added PIXAR raster support. Only uncompressed ("dumped") RGB + images can currently be read (based on information provided + by Greg Coats). + + + Added FlashPix (FPX) read support. Reads all pixel formats, but + only the highest resolution is read, and the viewing transform is + currently ignored. + + + Made PNG encoding somewhat more efficient in "optimize" mode; a + bug in 0.2b4 didn't enable all predictor filters when optimized + storage were requested. + + + Added Microsoft Image Composer (MIC) read support. When opened, + the first sprite in the file is loaded. You can use the seek method + to load additional sprites from the file. + + + Properly reads "P" and "CMYK" PSD images. + + + "pilconvert" no longer optimizes by default; use the -o option to + make the file as small as possible (at the expense of speed); use + the -q option to set the quality when compressing to JPEG. + + + Fixed "crop" not to drop the palette for "P" images. + + + Added and verified FLC support. + + + Paste with "L" or "RGBA" alpha is now several times faster on most + platforms. + + + Changed Image.new() to initialize the image to black, as described + in the handbook. To get an uninitialized image, use None as the + colour. + + + Fixed the PDF encoder to produce a valid header; Acrobat no longer + complains when you load PDF images created by PIL. + + + PIL only scans fully-qualified directory names in the path when + looking for plugins. + *** WARNING: MAY BREAK EXISTING CODE *** + + + Faster implementation of "save" used when filename is given, + or when file object has "fileno" and "flush" methods. + + + Don't crash in "crop" if region extends outside the source image. + + + Eliminated a massive memory leak in the "save" function. + + + The GIF decoder doesn't crash if the code size is set to an illegal + value. This could happen since another bug didn't handle local + palettes properly if they didn't have the same size as the + global palette (not very common). + + + Added predictor support (TIFF 6.0 section 14) to the TIFF decoder. + + + Fixed palette and padding problems in BMP driver. Now properly + writes "1", "L", "P" and "RGB" images. + + + Fixed getpixel()/getdata() to return correct pixel values. + + + Added PSD (PhotoShop) read support. Reads both uncompressed + and compressed images of most types. + + + Added GIF write support (writes "uncompressed" GIF files only, + due to unresolvable licensing issues). The "gifmaker.py" script + can be used to create GIF animations. + + + Reads 8-bit "L" and "P" TGA images. Also reads 16-bit "RGB" + images. + + + Added FLI read support. This driver has only been tested + on a few FLI samples. + + + Reads 2-bit and 4-bit PCX images. + + + Added MSP read and write support. Both version 1 and 2 can be + read, but only version 1 (uncompressed) files are written. + + + Fixed a bug in the FLI/FLC identification code that caused the + driver to raise an exception when parsing valid FLI/FLC files. + + + Improved performance when loading file format plugins, and when + opening files. + + + Added GIF animation support, via the "seek" and "tell" methods. + You can use "player.py" to play an animated GIF file. + + + Removed MNG support, since the spec is changing faster than I + can change the code. I've added support for the experimental + ARG format instead. Contact me for more information on this + format. + + + Added keyword options to the "save" method. The following options + are currently supported: + + format option description + -------------------------------------------------------- + JPEG optimize minimize output file at the + expense of compression speed. + + JPEG progressive enable progressive output. the + option value is ignored. + + JPEG quality set compression quality (1-100). + the default value is 75. + + JPEG smooth smooth dithered images. value + is strengh (1-100). default is + off (0). + + PNG optimize minimize output file at the + expense of compression speed. + + Expect more options in future releases. Also note that + file writers silently ignore unknown options. + + + Plugged memory leaks in the PNG and TIFF decoders. + + + Added PNG write support. + + + (internal) RGB unpackers and converters now set the pad byte + to 255 (full opacity). + + + Properly handles the "transparency" property for GIF, PNG + and XPM files. + + + Added a "putalpha" method, allowing you to attach a "1" or "L" + image as the alpha layer to an "RGBA" image. + + + Various improvements to the sample scripts: + + "pilconvert" Carries out some extra tricks in order to make + the resulting file as small as possible. + + "explode" (NEW) Split an image sequence into individual frames. + + "gifmaker" (NEW) Convert a sequence file into a GIF animation. + Note that the GIF encoder create "uncompressed" GIF + files, so animations created by this script are + rather large (typically 2-5 times the compressed + sizes). + + "image2py" (NEW) Convert a single image to a python module. See + comments in this script for details. + + "player" If multiple images are given on the command line, + they are interpreted as frames in a sequence. The + script assumes that they all have the same size. + Also note that this script now can play FLI/FLC + and GIF animations. + + This player can also execute embedded Python + animation applets (ARG format only). + + "viewer" Transparent images ("P" with transparency property, + and "RGBA") are superimposed on the standard Tk back- + ground. + + + Fixed colour argument to "new". For multilayer images, pass a + tuple: (Red, Green, Blue), (Red, Green, Blue, Alpha), or (Cyan, + Magenta, Yellow, Black). + + + Added XPM (X pixmap) read support. + + (0.2b3 released) + + + Added MNG (multi-image network graphics) read support. "Ming" + is a proposed animation standard, based on the PNG file format. + + You can use the "player" sample script to display some flavours + of this format. The MNG standard is still under development, + as is this driver. More information, including sample files, + can be found at + + + Added a "verify" method to images loaded from file. This method + scans the file for errors, without actually decoding the image + data, and raises a suitable exception if it finds any problems. + Currently implemented for PNG and MNG files only. + + + Added support for interlaced GIF images. + + + Added PNG read support -- if linked with the ZLIB compression library, + PIL reads all kinds of PNG images, except interlaced files. + + + Improved PNG identification support -- doesn't mess up on unknown + chunks, identifies all possible PNG modes, and verifies checksum + on PNG header chunks. + + + Added an experimental reader for placable Windows Meta Files (WMF). + This reader is still very incomplete, but it illustrates how PIL's + drawing capabilities can be used to render vector and metafile + formats. + + + Added restricted drivers for images from Image Tools (greyscale + only) and LabEye/IFUNC (common interchange modes only). + + + Some minor improvements to the sample scripts provided in the + "Scripts" directory. + + + The test images have been moved to the "Images" directory. + + (0.2b2 released) + (0.2b1 released; Windows only) + + + Fixed filling of complex polygons. The ImageDraw "line" and + "polygon" methods also accept Path objects. + + + The ImageTk "PhotoImage" object can now be constructed directly + from an image. You can also pass the object itself to Tkinter, + instead of using the "image" attribute. Finally, using "paste" + on a displayed image automatically updates the display. + + + The ImageTk "BitmapImage" object allows you to create transparent + overlays from 1-bit images. You can pass the object itself to + Tkinter. The constructor takes the same arguments as the Tkinter + BitmapImage class; use the "foreground" option to set the colour + of the overlay. + + + Added a "putdata" method to the Image class. This can be used to + load a 1-layer image with data from a sequence object or a string. + An optional floating point scale and offset can be used to adjust + the data to fit into the 8-bit pixel range. Also see the "getdata" + method. + + + Added the EXTENT method to the Image "transform" method. This can + be used to quickly crop, stretch, shrink, or mirror a subregion + from another image. + + + Adapted to Python 1.4. + + + Added a project makefile for Visual C++ 4.x. This allows you to + easily build a dynamically linked version of PIL for Windows 95 + and NT. + + + A Tk "booster" patch for Windows is available. It gives dramatic + performance improvements for some displays. Has been tested with + Tk 4.2 only, but is likely to work with Tk 4.1 as well. See the Tk + subdirectory for details. + + + You can now save 1-bit images in the XBM format. In addition, the + Image class now provides a "tobitmap" method which returns a string + containing an XBM representation of the image. Quite handy to use + with Tk. + + + More conversions, including "RGB" to "1" and more. + + (0.2a1 released) + + + Where earlier versions accepted lists, this version accepts arbitrary + Python sequences (including strings, in some cases). A few resource + leaks were plugged in the process. + + + The Image "paste" method now allows the box to extend outside + the target image. The size of the box, the image to be pasted, + and the optional mask must still match. + + + The ImageDraw module now supports filled polygons, outlined and + filled ellipses, and text. Font support is rudimentary, though. + + + The Image "point" method now takes an optional mode argument, + allowing you to convert the image while translating it. Currently, + this can only be used to convert "L" or "P" images to "1" images + (creating thresholded images or "matte" masks). + + + An Image "getpixel" method has been added. For single band images, + it returns the pixel value at a given position as an integer. + For n-band images, it returns an n-tuple of integers. + + + An Image "getdata" method has been added. It returns a sequence + object representing the image as a 1-dimensional array. Only len() + and [] can be used with this sequence. This method returns a + reference to the existing image data, so changes in the image + will be immediately reflected in the sequence object. + + + Fixed alignment problems in the Windows BMP writer. + + + If converting an "RGB" image to "RGB" or "L", you can give a second + argument containing a colour conversion matrix. + + + An Image "getbbox" method has been added. It returns the bounding + box of data in an image, considering the value 0 as background. + + + An Image "offset" method has been added. It returns a new image + where the contents of the image have been offset the given distance + in X and/or Y direction. Data wraps between edges. + + + Saves PDF images. The driver creates a binary PDF 1.1 files, using + JPEG compression for "L", "RGB", and "CMYK" images, and hex encoding + (same as for PostScript) for other formats. + + + The "paste" method now accepts "1" masks. Zero means transparent, + any other pixel value means opaque. This is faster than using an + "L" transparency mask. + + + Properly writes EPS files (and properly prints images to postscript + printers as well). + + + Reads 4-bit BMP files, as well as 4 and 8-bit Windows ICO and CUR + files. Cursor animations are not supported. + + + Fixed alignment problems in the Sun raster loader. + + + Added "draft" and "thumbnail" methods. The draft method is used + to optimize loading of JPEG and PCD files, the thumbnail method is + used to create a thumbnail representation of an image. + + + Added Windows display support, via the ImageWin class (see the + handbook for details). + + + Added raster conversion for EPS files. This requires GNU or Aladdin + Ghostscript, and probably works on UNIX only. + + + Reads PhotoCD (PCD) images. The base resolution (768x512) can be + read from a PhotoCD file. + + + Eliminated some compiler warnings. Bindings now compile cleanly in C++ + mode. Note that the Imaging library itself must be compiled in C mode. + + + Added "bdf2pil.py", which converts BDF fonts into images with associated + metrics. This is definitely work in progress. For info, see description + in script for details. + + + Fixed a bug in the "ImageEnhance.py" module. + + + Fixed a bug in the netpbm save hack in "GifImagePlugin.py" + + + Fixed 90 and 270 degree rotation of rectangular images. + + + Properly reads 8-bit TIFF palette-color images. + + + Reads plane separated RGB and CMYK TIFF images. + + + Added driver debug mode. This is enabled by setting Image.DEBUG + to a non-zero value. Try the -D option to "pilfile.py" and see what + happens. + + + Don't crash on "atend" constructs in PostScript files. + + + Only the Image module imports _imaging directly. Other modules + should refer to the binding module as "Image.core". + + *** Changes from release 0.0 to 0.1 (b1) *** + + + A handbook is available (distributed separately). + + + The coordinate system is changed so that (0,0) is now located + in the upper left corner. This is in compliancy with ISO 12087 + and 90% of all other image processing and graphics libraries. + + + Modes "1" (bilevel) and "P" (palette) have been introduced. Note + that bilevel images are stored with one byte per pixel. + + + The Image "crop" and "paste" methods now accepts None as the + box argument, to refer to the full image (self, that is). + + + The Image "crop" method now works properly. + + + The Image "point" method is now available. You can use either a + lookup table or a function taking one argument. + + + The Image join function has been renamed to "merge". + + + An Image "composite" function has been added. It is identical + to copy() followed by paste(mask). + + + An Image "eval" function has been added. It is currently identical + to point(function); that is, only a single image can be processed. + + + A set of channel operations has been added. See the "ImageChops" + module, test_chops.py, and the handbook for details. + + + Added the "pilconvert" utility, which converts image files. Note + that the number of output formats are still quite restricted. + + + Added the "pilfile" utility, which quickly identifies image files + (without loading them, in most cases). + + + Added the "pilprint" utility, which prints image files to Postscript + printers. + + + Added a rudimentary version of the "pilview" utility, which is + simple image viewer based on Tk. Only File/Exit and Image/Next + works properly. + + + An interface to Tk has been added. See "Lib/ImageTk.py" and README + for details. + + + An interface to Jack Jansen's Img library has been added (thanks to + Jack). This allows you to read images through the Img extensions file + format handlers. See the file "Lib/ImgExtImagePlugin.py" for details. + + + Postscript printing is provided through the PSDraw module. See the + handbook for details. diff --git a/CONTENTS b/CONTENTS deleted file mode 100644 index 8846fb945..000000000 --- a/CONTENTS +++ /dev/null @@ -1,299 +0,0 @@ -Imaging/README - -Imaging/CHANGES - -Imaging/CONTENTS -Imaging/MANIFEST - -Imaging/BUILDME - -Imaging/setup.py - -Imaging/selftest.py -Imaging/doctest.py - -Imaging/libImaging/Imaging.h -Imaging/libImaging/ImDib.h -Imaging/libImaging/ImPlatform.h - -Imaging/libImaging/Quant.h -Imaging/libImaging/QuantHash.h -Imaging/libImaging/QuantHeap.h -Imaging/libImaging/QuantDefines.h -Imaging/libImaging/QuantTypes.h - -Imaging/libImaging/Access.c -Imaging/libImaging/Antialias.c -Imaging/libImaging/Bands.c -Imaging/libImaging/Blend.c -Imaging/libImaging/Chops.c -Imaging/libImaging/Convert.c -Imaging/libImaging/ConvertYCbCr.c -Imaging/libImaging/Copy.c -Imaging/libImaging/Crc32.c -Imaging/libImaging/Crop.c -Imaging/libImaging/Dib.c -Imaging/libImaging/Draw.c -Imaging/libImaging/Effects.c -Imaging/libImaging/Except.c -Imaging/libImaging/File.c -Imaging/libImaging/Fill.c -Imaging/libImaging/Filter.c -Imaging/libImaging/Geometry.c -Imaging/libImaging/GetBBox.c -Imaging/libImaging/Histo.c -Imaging/libImaging/Matrix.c -Imaging/libImaging/ModeFilter.c -Imaging/libImaging/Negative.c -Imaging/libImaging/Offset.c -Imaging/libImaging/Pack.c -Imaging/libImaging/Palette.c -Imaging/libImaging/Paste.c -Imaging/libImaging/Point.c -Imaging/libImaging/Quant.c -Imaging/libImaging/QuantHash.c -Imaging/libImaging/QuantHeap.c -Imaging/libImaging/RankFilter.c -Imaging/libImaging/Storage.c -Imaging/libImaging/Unpack.c -Imaging/libImaging/UnpackYCC.c -Imaging/libImaging/UnsharpMask.c - -Imaging/libImaging/Bit.h -Imaging/libImaging/Gif.h -Imaging/libImaging/Jpeg.h -Imaging/libImaging/Lzw.h -Imaging/libImaging/Raw.h -Imaging/libImaging/Zip.h -Imaging/libImaging/BitDecode.c -Imaging/libImaging/EpsEncode.c -Imaging/libImaging/FliDecode.c -Imaging/libImaging/GifDecode.c -Imaging/libImaging/GifEncode.c -Imaging/libImaging/HexDecode.c -Imaging/libImaging/JpegDecode.c -Imaging/libImaging/JpegEncode.c -Imaging/libImaging/LzwDecode.c -Imaging/libImaging/MspDecode.c -Imaging/libImaging/PackDecode.c -Imaging/libImaging/PcdDecode.c -Imaging/libImaging/PcxEncode.c -Imaging/libImaging/PcxDecode.c -Imaging/libImaging/RawDecode.c -Imaging/libImaging/RawEncode.c -Imaging/libImaging/SunRleDecode.c -Imaging/libImaging/TgaRleDecode.c -Imaging/libImaging/XbmDecode.c -Imaging/libImaging/XbmEncode.c -Imaging/libImaging/ZipDecode.c -Imaging/libImaging/ZipEncode.c - -Imaging/_imaging.c -Imaging/decode.c -Imaging/encode.c -Imaging/display.c -Imaging/map.c -Imaging/outline.c -Imaging/path.c - -Imaging/_imagingtk.c -Imaging/_imagingft.c -Imaging/_imagingcms.c -Imaging/_imagingmath.c - -Imaging/PIL.pth -Imaging/PIL/__init__.py -Imaging/PIL/ArgImagePlugin.py -Imaging/PIL/BdfFontFile.py -Imaging/PIL/BmpImagePlugin.py -Imaging/PIL/BufrStubImagePlugin.py -Imaging/PIL/ContainerIO.py -Imaging/PIL/CurImagePlugin.py -Imaging/PIL/DcxImagePlugin.py -Imaging/PIL/EpsImagePlugin.py -Imaging/PIL/ExifTags.py -Imaging/PIL/FitsStubImagePlugin.py -Imaging/PIL/FliImagePlugin.py -Imaging/PIL/FontFile.py -Imaging/PIL/FpxImagePlugin.py -Imaging/PIL/GbrImagePlugin.py -Imaging/PIL/GdImageFile.py -Imaging/PIL/GifImagePlugin.py -Imaging/PIL/GimpGradientFile.py -Imaging/PIL/GimpPaletteFile.py -Imaging/PIL/GribStubImagePlugin.py -Imaging/PIL/Hdf5StubImagePlugin.py -Imaging/PIL/IcoImagePlugin.py -Imaging/PIL/IcnsImagePlugin.py -Imaging/PIL/Image.py -Imaging/PIL/ImageChops.py -Imaging/PIL/ImageCms.py -Imaging/PIL/ImageColor.py -Imaging/PIL/ImageDraw.py -Imaging/PIL/ImageDraw2.py -Imaging/PIL/ImageEnhance.py -Imaging/PIL/ImageFile.py -Imaging/PIL/ImageFileIO.py -Imaging/PIL/ImageFilter.py -Imaging/PIL/ImageFont.py -Imaging/PIL/ImageGL.py -Imaging/PIL/ImageGrab.py -Imaging/PIL/ImageMath.py -Imaging/PIL/ImageMode.py -Imaging/PIL/ImageOps.py -Imaging/PIL/ImagePalette.py -Imaging/PIL/ImagePath.py -Imaging/PIL/ImageQt.py -Imaging/PIL/ImageSequence.py -Imaging/PIL/ImageShow.py -Imaging/PIL/ImageStat.py -Imaging/PIL/ImageTk.py -Imaging/PIL/ImageTransform.py -Imaging/PIL/ImageWin.py -Imaging/PIL/ImImagePlugin.py -Imaging/PIL/ImtImagePlugin.py -Imaging/PIL/IptcImagePlugin.py -Imaging/PIL/JpegImagePlugin.py -Imaging/PIL/McIdasImagePlugin.py -Imaging/PIL/MicImagePlugin.py -Imaging/PIL/MpegImagePlugin.py -Imaging/PIL/MspImagePlugin.py -Imaging/PIL/OleFileIO.py -Imaging/PIL/PaletteFile.py -Imaging/PIL/PalmImagePlugin.py -Imaging/PIL/PcdImagePlugin.py -Imaging/PIL/PcfFontFile.py -Imaging/PIL/PcxImagePlugin.py -Imaging/PIL/PdfImagePlugin.py -Imaging/PIL/PixarImagePlugin.py -Imaging/PIL/PngImagePlugin.py -Imaging/PIL/PpmImagePlugin.py -Imaging/PIL/PsdImagePlugin.py -Imaging/PIL/PSDraw.py -Imaging/PIL/SgiImagePlugin.py -Imaging/PIL/SpiderImagePlugin.py -Imaging/PIL/SunImagePlugin.py -Imaging/PIL/TarIO.py -Imaging/PIL/TgaImagePlugin.py -Imaging/PIL/TiffImagePlugin.py -Imaging/PIL/TiffTags.py -Imaging/PIL/WalImageFile.py -Imaging/PIL/WmfImagePlugin.py -Imaging/PIL/XbmImagePlugin.py -Imaging/PIL/XpmImagePlugin.py -Imaging/PIL/XVThumbImagePlugin.py - -Imaging/Docs/index.html -Imaging/Docs/effbot.css -Imaging/Docs/pythondoc-PIL.ArgImagePlugin.html -Imaging/Docs/pythondoc-PIL.BdfFontFile.html -Imaging/Docs/pythondoc-PIL.BmpImagePlugin.html -Imaging/Docs/pythondoc-PIL.BufrStubImagePlugin.html -Imaging/Docs/pythondoc-PIL.ContainerIO.html -Imaging/Docs/pythondoc-PIL.CurImagePlugin.html -Imaging/Docs/pythondoc-PIL.DcxImagePlugin.html -Imaging/Docs/pythondoc-PIL.EpsImagePlugin.html -Imaging/Docs/pythondoc-PIL.ExifTags.html -Imaging/Docs/pythondoc-PIL.FitsStubImagePlugin.html -Imaging/Docs/pythondoc-PIL.FliImagePlugin.html -Imaging/Docs/pythondoc-PIL.FontFile.html -Imaging/Docs/pythondoc-PIL.FpxImagePlugin.html -Imaging/Docs/pythondoc-PIL.GbrImagePlugin.html -Imaging/Docs/pythondoc-PIL.GdImageFile.html -Imaging/Docs/pythondoc-PIL.GifImagePlugin.html -Imaging/Docs/pythondoc-PIL.GimpGradientFile.html -Imaging/Docs/pythondoc-PIL.GimpPaletteFile.html -Imaging/Docs/pythondoc-PIL.GribStubImagePlugin.html -Imaging/Docs/pythondoc-PIL.Hdf5StubImagePlugin.html -Imaging/Docs/pythondoc-PIL.IcoImagePlugin.html -Imaging/Docs/pythondoc-PIL.IcnsImagePlugin.html -Imaging/Docs/pythondoc-PIL.Image.html -Imaging/Docs/pythondoc-PIL.ImageChops.html -Imaging/Docs/pythondoc-PIL.ImageColor.html -Imaging/Docs/pythondoc-PIL.ImageDraw.html -Imaging/Docs/pythondoc-PIL.ImageEnhance.html -Imaging/Docs/pythondoc-PIL.ImageFile.html -Imaging/Docs/pythondoc-PIL.ImageFileIO.html -Imaging/Docs/pythondoc-PIL.ImageFilter.html -Imaging/Docs/pythondoc-PIL.ImageFont.html -Imaging/Docs/pythondoc-PIL.ImageGL.html -Imaging/Docs/pythondoc-PIL.ImageGrab.html -Imaging/Docs/pythondoc-PIL.ImageOps.html -Imaging/Docs/pythondoc-PIL.ImagePalette.html -Imaging/Docs/pythondoc-PIL.ImagePath.html -Imaging/Docs/pythondoc-PIL.ImageSequence.html -Imaging/Docs/pythondoc-PIL.ImageStat.html -Imaging/Docs/pythondoc-PIL.ImageTk.html -Imaging/Docs/pythondoc-PIL.ImageTransform.html -Imaging/Docs/pythondoc-PIL.ImageWin.html -Imaging/Docs/pythondoc-PIL.ImImagePlugin.html -Imaging/Docs/pythondoc-PIL.ImtImagePlugin.html -Imaging/Docs/pythondoc-PIL.IptcImagePlugin.html -Imaging/Docs/pythondoc-PIL.JpegImagePlugin.html -Imaging/Docs/pythondoc-PIL.McIdasImagePlugin.html -Imaging/Docs/pythondoc-PIL.MicImagePlugin.html -Imaging/Docs/pythondoc-PIL.MpegImagePlugin.html -Imaging/Docs/pythondoc-PIL.MspImagePlugin.html -Imaging/Docs/pythondoc-PIL.OleFileIO.html -Imaging/Docs/pythondoc-PIL.PaletteFile.html -Imaging/Docs/pythondoc-PIL.PalmImagePlugin.html -Imaging/Docs/pythondoc-PIL.PcdImagePlugin.html -Imaging/Docs/pythondoc-PIL.PcfFontFile.html -Imaging/Docs/pythondoc-PIL.PcxImagePlugin.html -Imaging/Docs/pythondoc-PIL.PdfImagePlugin.html -Imaging/Docs/pythondoc-PIL.PixarImagePlugin.html -Imaging/Docs/pythondoc-PIL.PngImagePlugin.html -Imaging/Docs/pythondoc-PIL.PpmImagePlugin.html -Imaging/Docs/pythondoc-PIL.PsdImagePlugin.html -Imaging/Docs/pythondoc-PIL.PSDraw.html -Imaging/Docs/pythondoc-PIL.SgiImagePlugin.html -Imaging/Docs/pythondoc-PIL.SpiderImagePlugin.html -Imaging/Docs/pythondoc-PIL.SunImagePlugin.html -Imaging/Docs/pythondoc-PIL.TarIO.html -Imaging/Docs/pythondoc-PIL.TgaImagePlugin.html -Imaging/Docs/pythondoc-PIL.TiffImagePlugin.html -Imaging/Docs/pythondoc-PIL.TiffTags.html -Imaging/Docs/pythondoc-PIL.WalImageFile.html -Imaging/Docs/pythondoc-PIL.WmfImagePlugin.html -Imaging/Docs/pythondoc-PIL.XbmImagePlugin.html -Imaging/Docs/pythondoc-PIL.XpmImagePlugin.html -Imaging/Docs/pythondoc-PIL.XVThumbImagePlugin.html - -Imaging/Scripts/pilconvert.py -Imaging/Scripts/pildriver.py -Imaging/Scripts/pilfile.py -Imaging/Scripts/pilfont.py -Imaging/Scripts/pilprint.py - -Imaging/Images/lena.gif -Imaging/Images/lena.png -Imaging/Images/lena.ppm -Imaging/Images/lena.jpg - -Imaging/Images/courB08.bdf -Imaging/Images/courB08.pbm -Imaging/Images/courB08.pil - -Imaging/Sane/README -Imaging/Sane/CHANGES -Imaging/Sane/setup.py -Imaging/Sane/sanedoc.txt -Imaging/Sane/_sane.c -Imaging/Sane/sane.py -Imaging/Sane/demo_numarray.py -Imaging/Sane/demo_pil.py - -Imaging/Scripts/README -Imaging/Scripts/enhancer.py -Imaging/Scripts/explode.py -Imaging/Scripts/gifmaker.py -Imaging/Scripts/painter.py -Imaging/Scripts/player.py -Imaging/Scripts/viewer.py -Imaging/Scripts/thresholder.py - -Imaging/Tk/tkImaging.c -Imaging/Tk/install.txt -Imaging/Tk/booster.txt -Imaging/Tk/pilbitmap.txt - diff --git a/Docs/effbot.css b/Docs/effbot.css deleted file mode 100644 index b03ae458c..000000000 --- a/Docs/effbot.css +++ /dev/null @@ -1,118 +0,0 @@ -/* effbot.css */ - -BODY { - font: 100% Georgia, Times, serif; - color: black; - margin: 0px 20px 0px 20px; -} - -#effbot-body { - background: white; - padding: 10px 40px 10px 40px; - max-width: 50em; -} - -#effbot-menu { - display: none; -} - -.adsense { - background: #f8fff8; - border: 1px solid #084; - padding: 10px 4px 4px 4px; -} - -.sidebar { - border: 1px solid #000; - float: right; clear: right; - width: 200px; - background: white; - padding: 10px; - margin: 0px -25px 10px 0px; -} - -/* visual style */ - -P { - line-height: 1.3em; -} - -CODE, PRE { - font: 100% "Courier New", Courier, Monaco, monospace; - color: #042; margin-left: 20px; -} - -H1, H2, H3 { - font-family: Georgia, Times, serif; - color: #084; margin-top: 30px; -} - -H1, H2, { border-top: 1px solid #084; } - -H4, H5, H6 { - font-family: Georgia, Times, serif; - color: #084; margin-top: 15px; -} - -A:link, A:hover { color: #084; } -A:visited { color: #404040; } - -UL LI { list-style-type: square; } - -.title { margin-bottom: 2px; color: #084; } -.info { font-size: 80%; color: #084; margin-top: 0px; } - -.bluebox { color: #084; margin-top: 10px; } - -.highlight { background: #cfc; } -.mark { color: #084; } -.small { font-size: 80%; } -.display { background: #eee; padding: 20px; } - -.note { - background: #efe; - border-top: 1px solid #084; - border-bottom: 1px solid #084; - padding: 2px 20px; -} - -.example { - border-top: medium solid #084; - border-bottom: medium solid #084; - padding: 5px; -} - -.figure { - border-top: medium solid #084; - border-bottom: medium solid #084; - padding: 5px; -} - -.fixme { - background: #eee; - border: 1px solid #084; - padding: 2x 20px; -} - -.simpletable { - border: 1px solid #084; - border-collapse: collapse; -} - -.simpletable TH { - text-align: left; - background: #cfc; - border: 1px solid #084; - margin: 0px; - padding: 1px 5px; -} - -.simpletable TD { - border: 1px solid #084; - margin: 0px; - padding: 5px; -} - -/* xmldiff markup */ -.new { text-decoration: underline; color: red; background: #fff0f0; } -.old { text-decoration: line-through; color: blue; background: #f0f0ff; } diff --git a/Docs/index.html b/Docs/index.html deleted file mode 100644 index fbc76a99d..000000000 --- a/Docs/index.html +++ /dev/null @@ -1,103 +0,0 @@ - - - - - -The Python Imaging Library - - - -

The Python Imaging Library

- -

Online Resources

- -
-
Python Imaging Library (official product page at pythonware.com)
-
Python Imaging Library (development page at effbot.org)
-
- -

Package Contents

- -

The following pages are generated from pythondoc markup in the source files.

- -
-
The PIL.Image Module
-
The PIL.ImageChops Module
-
The PIL.ImageColor Module
-
The PIL.ImageDraw Module
-
The PIL.ImageEnhance Module
-
The PIL.ImageFile Module
-
The PIL.ImageFileIO Module
-
The PIL.ImageFilter Module
-
The PIL.ImageFont Module
-
The PIL.ImageGL Module
-
The PIL.ImageGrab Module
-
The PIL.ImageMath Module
-
The PIL.ImageMode Module
-
The PIL.ImageOps Module
-
The PIL.ImagePalette Module
-
The PIL.ImagePath Module
-
The PIL.ImageQt Module
-
The PIL.ImageSequence Module
-
The PIL.ImageStat Module
-
The PIL.ImageTk Module
-
The PIL.ImageTransform Module
-
The PIL.ImageWin Module
-
 
-
The PIL.ArgImagePlugin Module
-
The PIL.BdfFontFile Module
-
The PIL.BmpImagePlugin Module
-
The PIL.BufrStubImagePlugin Module
-
The PIL.ContainerIO Module
-
The PIL.CurImagePlugin Module
-
The PIL.DcxImagePlugin Module
-
The PIL.EpsImagePlugin Module
-
The PIL.ExifTags Module
-
The PIL.FitsStubImagePlugin Module
-
The PIL.FliImagePlugin Module
-
The PIL.FontFile Module
-
The PIL.FpxImagePlugin Module
-
The PIL.GbrImagePlugin Module
-
The PIL.GdImageFile Module
-
The PIL.GifImagePlugin Module
-
The PIL.GimpGradientFile Module
-
The PIL.GimpPaletteFile Module
-
The PIL.GribStubImagePlugin Module
-
The PIL.Hdf5StubImagePlugin Module
-
The PIL.IcnsImagePlugin Module
-
The PIL.IcoImagePlugin Module
-
The PIL.ImImagePlugin Module
-
The PIL.ImtImagePlugin Module
-
The PIL.IptcImagePlugin Module
-
The PIL.JpegImagePlugin Module
-
The PIL.McIdasImagePlugin Module
-
The PIL.MicImagePlugin Module
-
The PIL.MpegImagePlugin Module
-
The PIL.MspImagePlugin Module
-
The PIL.OleFileIO Module
-
The PIL.PSDraw Module
-
The PIL.PaletteFile Module
-
The PIL.PalmImagePlugin Module
-
The PIL.PcdImagePlugin Module
-
The PIL.PcfFontFile Module
-
The PIL.PcxImagePlugin Module
-
The PIL.PdfImagePlugin Module
-
The PIL.PixarImagePlugin Module
-
The PIL.PngImagePlugin Module
-
The PIL.PpmImagePlugin Module
-
The PIL.PsdImagePlugin Module
-
The PIL.SgiImagePlugin Module
-
The PIL.SunImagePlugin Module
-
The PIL.TarIO Module
-
The PIL.TgaImagePlugin Module
-
The PIL.TiffImagePlugin Module
-
The PIL.TiffTags Module
-
The PIL.WalImageFile Module
-
The PIL.WbmpImagePlugin Module
-
The PIL.WmfImagePlugin Module
-
The PIL.XVThumbImagePlugin Module
-
The PIL.XbmImagePlugin Module
-
The PIL.XpmImagePlugin Module
-
- - diff --git a/Docs/pythondoc-PIL.ArgImagePlugin.html b/Docs/pythondoc-PIL.ArgImagePlugin.html deleted file mode 100644 index 28aa4e11b..000000000 --- a/Docs/pythondoc-PIL.ArgImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.ArgImagePlugin Module - - - -

The PIL.ArgImagePlugin Module

-
-
ArgImageFile (class) [#]
-
-

Image plugin for the experimental Animated Raster Graphics format.

-

For more information about this class, see The ArgImageFile Class.

-
-
-

The ArgImageFile Class

-
-
ArgImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.BdfFontFile.html b/Docs/pythondoc-PIL.BdfFontFile.html deleted file mode 100644 index 1f2008c17..000000000 --- a/Docs/pythondoc-PIL.BdfFontFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.BdfFontFile Module - - - -

The PIL.BdfFontFile Module

-
-
BdfFontFile(fp) (class) [#]
-
-

Font file plugin for the X11 BDF format.

-

For more information about this class, see The BdfFontFile Class.

-
-
-

The BdfFontFile Class

-
-
BdfFontFile(fp) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.BmpImagePlugin.html b/Docs/pythondoc-PIL.BmpImagePlugin.html deleted file mode 100644 index 17fe0d1f1..000000000 --- a/Docs/pythondoc-PIL.BmpImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.BmpImagePlugin Module - - - -

The PIL.BmpImagePlugin Module

-
-
BmpImageFile (class) [#]
-
-

Image plugin for the Windows BMP format.

-

For more information about this class, see The BmpImageFile Class.

-
-
-

The BmpImageFile Class

-
-
BmpImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.BufrStubImagePlugin.html b/Docs/pythondoc-PIL.BufrStubImagePlugin.html deleted file mode 100644 index c17a1199d..000000000 --- a/Docs/pythondoc-PIL.BufrStubImagePlugin.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - -The PIL.BufrStubImagePlugin Module - - - -

The PIL.BufrStubImagePlugin Module

-
-
register_handler(handler) [#]
-
-
-
handler
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ContainerIO.html b/Docs/pythondoc-PIL.ContainerIO.html deleted file mode 100644 index 61e4c9187..000000000 --- a/Docs/pythondoc-PIL.ContainerIO.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - -The PIL.ContainerIO Module - - - -

The PIL.ContainerIO Module

-
-
ContainerIO(file, offset, length) (class) [#]
-
-

A file object that provides read access to a part of an existing -file (for example a TAR file).

-

For more information about this class, see The ContainerIO Class.

-
-
-

The ContainerIO Class

-
-
ContainerIO(file, offset, length) (class) [#]
-
-
-
__init__(file, offset, length) [#]
-
-
-
file
-
-
-
offset
-
-
-
length
-
-
-

-
-
isatty() [#]
-
-
-
read(bytes=0) [#]
-
-
-
bytes
-
-
-
Returns:
-
-
-

-
-
readline() [#]
-
-
-
Returns:
-
-
-

-
-
readlines() [#]
-
-
-
Returns:
-
-
-

-
-
seek(offset, mode=0) [#]
-
-
-
offset
-
-
-
mode
-
-
-

-
-
tell() [#]
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.CurImagePlugin.html b/Docs/pythondoc-PIL.CurImagePlugin.html deleted file mode 100644 index 0dafd34ec..000000000 --- a/Docs/pythondoc-PIL.CurImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.CurImagePlugin Module - - - -

The PIL.CurImagePlugin Module

-
-
CurImageFile (class) [#]
-
-

Image plugin for Windows Cursor files.

-

For more information about this class, see The CurImageFile Class.

-
-
-

The CurImageFile Class

-
-
CurImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.DcxImagePlugin.html b/Docs/pythondoc-PIL.DcxImagePlugin.html deleted file mode 100644 index 335ac4339..000000000 --- a/Docs/pythondoc-PIL.DcxImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.DcxImagePlugin Module - - - -

The PIL.DcxImagePlugin Module

-
-
DcxImageFile (class) [#]
-
-

Image plugin for the Intel DCX format.

-

For more information about this class, see The DcxImageFile Class.

-
-
-

The DcxImageFile Class

-
-
DcxImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.EpsImagePlugin.html b/Docs/pythondoc-PIL.EpsImagePlugin.html deleted file mode 100644 index 262b34632..000000000 --- a/Docs/pythondoc-PIL.EpsImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.EpsImagePlugin Module - - - -

The PIL.EpsImagePlugin Module

-
-
EpsImageFile (class) [#]
-
-

Image plugin for Encapsulated Postscript.

-

For more information about this class, see The EpsImageFile Class.

-
-
-

The EpsImageFile Class

-
-
EpsImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ExifTags.html b/Docs/pythondoc-PIL.ExifTags.html deleted file mode 100644 index 39aab438b..000000000 --- a/Docs/pythondoc-PIL.ExifTags.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - -The PIL.ExifTags Module - - - -

The PIL.ExifTags Module

-

Module Contents

-
-
GPSTAGS (variable) [#]
-
-
-
TAGS (variable) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.FitsStubImagePlugin.html b/Docs/pythondoc-PIL.FitsStubImagePlugin.html deleted file mode 100644 index 1492f74a2..000000000 --- a/Docs/pythondoc-PIL.FitsStubImagePlugin.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - -The PIL.FitsStubImagePlugin Module - - - -

The PIL.FitsStubImagePlugin Module

-
-
register_handler(handler) [#]
-
-
-
handler
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.FliImagePlugin.html b/Docs/pythondoc-PIL.FliImagePlugin.html deleted file mode 100644 index 0d7faa008..000000000 --- a/Docs/pythondoc-PIL.FliImagePlugin.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - -The PIL.FliImagePlugin Module - - - -

The PIL.FliImagePlugin Module

-
-
FliImageFile (class) [#]
-
-

Image plugin for the FLI/FLC animation format.

-

For more information about this class, see The FliImageFile Class.

-
-
-

The FliImageFile Class

-
-
FliImageFile (class) [#]
-
-

Image plugin for the FLI/FLC animation format. Use the seek -method to load individual frames. -

-
- diff --git a/Docs/pythondoc-PIL.FontFile.html b/Docs/pythondoc-PIL.FontFile.html deleted file mode 100644 index 7f9b82faf..000000000 --- a/Docs/pythondoc-PIL.FontFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.FontFile Module - - - -

The PIL.FontFile Module

-
-
FontFile() (class) [#]
-
-

Base class for raster font file handlers.

-

For more information about this class, see The FontFile Class.

-
-
-

The FontFile Class

-
-
FontFile() (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.FpxImagePlugin.html b/Docs/pythondoc-PIL.FpxImagePlugin.html deleted file mode 100644 index f4631b890..000000000 --- a/Docs/pythondoc-PIL.FpxImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.FpxImagePlugin Module - - - -

The PIL.FpxImagePlugin Module

-
-
FpxImageFile (class) [#]
-
-

Image plugin for the FlashPix images.

-

For more information about this class, see The FpxImageFile Class.

-
-
-

The FpxImageFile Class

-
-
FpxImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.GbrImagePlugin.html b/Docs/pythondoc-PIL.GbrImagePlugin.html deleted file mode 100644 index a318ed7c5..000000000 --- a/Docs/pythondoc-PIL.GbrImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.GbrImagePlugin Module - - - -

The PIL.GbrImagePlugin Module

-
-
GbrImageFile (class) [#]
-
-

Image plugin for the GIMP brush format.

-

For more information about this class, see The GbrImageFile Class.

-
-
-

The GbrImageFile Class

-
-
GbrImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.GdImageFile.html b/Docs/pythondoc-PIL.GdImageFile.html deleted file mode 100644 index 554ffb980..000000000 --- a/Docs/pythondoc-PIL.GdImageFile.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - -The PIL.GdImageFile Module - - - -

The PIL.GdImageFile Module

-
-
GdImageFile (class) [#]
-
-

Image plugin for the GD uncompressed format.

-

For more information about this class, see The GdImageFile Class.

-
-
open(fp, mode="r") [#]
-
-
-
filename
-
-
-
mode
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
-

The GdImageFile Class

-
-
GdImageFile (class) [#]
-
-

Image plugin for the GD uncompressed format. Note that this format -is not supported by the standard Image.open function. To use -this plugin, you have to import the GdImageFile module and -use the GdImageFile.open function. -

-
- diff --git a/Docs/pythondoc-PIL.GifImagePlugin.html b/Docs/pythondoc-PIL.GifImagePlugin.html deleted file mode 100644 index 06a0401e4..000000000 --- a/Docs/pythondoc-PIL.GifImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.GifImagePlugin Module - - - -

The PIL.GifImagePlugin Module

-
-
GifImageFile (class) [#]
-
-

Image plugin for GIF images.

-

For more information about this class, see The GifImageFile Class.

-
-
-

The GifImageFile Class

-
-
GifImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.GimpGradientFile.html b/Docs/pythondoc-PIL.GimpGradientFile.html deleted file mode 100644 index d9e3d3231..000000000 --- a/Docs/pythondoc-PIL.GimpGradientFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.GimpGradientFile Module - - - -

The PIL.GimpGradientFile Module

-
-
GimpGradientFile(fp) (class) [#]
-
-

File handler for GIMP's gradient format.

-

For more information about this class, see The GimpGradientFile Class.

-
-
-

The GimpGradientFile Class

-
-
GimpGradientFile(fp) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.GimpPaletteFile.html b/Docs/pythondoc-PIL.GimpPaletteFile.html deleted file mode 100644 index 421fc9c0f..000000000 --- a/Docs/pythondoc-PIL.GimpPaletteFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.GimpPaletteFile Module - - - -

The PIL.GimpPaletteFile Module

-
-
GimpPaletteFile(fp) (class) [#]
-
-

File handler for GIMP's palette format.

-

For more information about this class, see The GimpPaletteFile Class.

-
-
-

The GimpPaletteFile Class

-
-
GimpPaletteFile(fp) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.GribStubImagePlugin.html b/Docs/pythondoc-PIL.GribStubImagePlugin.html deleted file mode 100644 index 735db2b03..000000000 --- a/Docs/pythondoc-PIL.GribStubImagePlugin.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - -The PIL.GribStubImagePlugin Module - - - -

The PIL.GribStubImagePlugin Module

-
-
register_handler(handler) [#]
-
-
-
handler
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.Hdf5StubImagePlugin.html b/Docs/pythondoc-PIL.Hdf5StubImagePlugin.html deleted file mode 100644 index d03591a0f..000000000 --- a/Docs/pythondoc-PIL.Hdf5StubImagePlugin.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - -The PIL.Hdf5StubImagePlugin Module - - - -

The PIL.Hdf5StubImagePlugin Module

-
-
register_handler(handler) [#]
-
-
-
handler
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.IcnsImagePlugin.html b/Docs/pythondoc-PIL.IcnsImagePlugin.html deleted file mode 100644 index 4a0531419..000000000 --- a/Docs/pythondoc-PIL.IcnsImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.IcnsImagePlugin Module - - - -

The PIL.IcnsImagePlugin Module

-
-
IcnsImageFile (class) [#]
-
-

Image plugin for Mac OS icons.

-

For more information about this class, see The IcnsImageFile Class.

-
-
-

The IcnsImageFile Class

-
-
IcnsImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.IcoImagePlugin.html b/Docs/pythondoc-PIL.IcoImagePlugin.html deleted file mode 100644 index 5e2b9230f..000000000 --- a/Docs/pythondoc-PIL.IcoImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.IcoImagePlugin Module - - - -

The PIL.IcoImagePlugin Module

-
-
IcoImageFile (class) [#]
-
-

Image plugin for Windows Icon files.

-

For more information about this class, see The IcoImageFile Class.

-
-
-

The IcoImageFile Class

-
-
IcoImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImImagePlugin.html b/Docs/pythondoc-PIL.ImImagePlugin.html deleted file mode 100644 index 918f74598..000000000 --- a/Docs/pythondoc-PIL.ImImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.ImImagePlugin Module - - - -

The PIL.ImImagePlugin Module

-
-
ImImageFile (class) [#]
-
-

Image plugin for the IFUNC IM file format.

-

For more information about this class, see The ImImageFile Class.

-
-
-

The ImImageFile Class

-
-
ImImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.Image.html b/Docs/pythondoc-PIL.Image.html deleted file mode 100644 index e1a49c01f..000000000 --- a/Docs/pythondoc-PIL.Image.html +++ /dev/null @@ -1,931 +0,0 @@ - - - - -The PIL.Image Module - - - -

The PIL.Image Module

-
-
blend(im1, im2, alpha) [#]
-
-

Creates a new image by interpolating between two input images, using -a constant alpha. - -

-   out = image1 * (1.0 - alpha) + image2 * alpha
-
- -

-
im1
-
-
-
im2
-
-
-
alpha
-
-
-
Returns:
-
-
-

-
-
composite(image1, image2, mask) [#]
-
-
-
image1
-
-
-
image2
-
-
-
mask
-
-
-

-
-
eval(image, function) [#]
-
-
-
image
-
-
-
function
-
-
-
Returns:
-
-
-

-
-
frombuffer(mode, size, data, decoder_name="raw", *args) [#]
-
-

(New in 1.1.4) Creates an image memory from pixel data in a string -or byte buffer. -

-This function is similar to 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; support modes include "L", "RGBX", "RGBA", and -"CMYK". For other modes, this function behaves like a corresponding -call to the fromstring function. -

-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 open to load it. - -

-
mode
-
-
-
size
-
-
-
data
-
-
-
decoder_name
-
-
-
*args
-
-
-
Returns:
-
-
-

-
-
fromstring(mode, size, data, decoder_name="raw", *args) [#]
-
-

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 open to load it. - -

-
mode
-
-
-
size
-
-
-
data
-
-
-
decoder_name
-
-
-
*args
-
-
-
Returns:
-
-
-

-
-
getmodebandnames(mode) [#]
-
-

Gets a list of individual band names. Given a mode, this function -returns a tuple containing the names of individual bands (use -getmodetype to get the mode used to store each individual -band. - -

-
mode
-
-
-
Returns:
-
-
-
Raises KeyError:
-
-

-
-
getmodebands(mode) [#]
-
-
-
mode
-
-
-
Returns:
-
-
-
Raises KeyError:
-
-

-
-
getmodebase(mode) [#]
-
-
-
mode
-
-
-
Returns:
-
-
-
Raises KeyError:
-
-

-
-
getmodetype(mode) [#]
-
-
-
mode
-
-
-
Returns:
-
-
-
Raises KeyError:
-
-

-
-
Image() (class) [#]
-
-

This class represents an image object.

-

For more information about this class, see The Image Class.

-
-
init() [#]
-
-
-
isDirectory(f) [#]
-
-
-
isImageType(t) [#]
-
-
-
isStringType(t) [#]
-
-
-
isTupleType(t) [#]
-
-
-
merge(mode, bands) [#]
-
-
-
mode
-
-
-
bands
-
-
-
Returns:
-
-
-

-
-
new(mode, size, color=0) [#]
-
-
-
mode
-
-
-
size
-
-
-
color
-
-
-
Returns:
-
-
-

-
-
open(file, mode="r") [#]
-
-

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 load method). - -

-
file
-
-A filename (string) or a file object. The file object - must implement read, seek, and tell methods, - and be opened in binary mode. -
-
mode
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
preinit() [#]
-
-
-
register_extension(id, extension) [#]
-
-
-
id
-
-
-
extension
-
-
-

-
-
register_mime(id, mimetype) [#]
-
-
-
id
-
-
-
mimetype
-
-
-

-
-
register_open(id, factory, accept=None) [#]
-
-
-
id
-
-
-
factory
-
-
-
accept
-
-
-

-
-
register_save(id, driver) [#]
-
-
-
id
-
-
-
driver
-
-
-

-
-
-

The Image Class

-
-
Image() (class) [#]
-
-
-
convert(mode, matrix=None) [#]
-
-

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 point method. - -

-
mode
-
-
-
matrix
-
-
-
Returns:
-
-
-

-
-
copy() [#]
-
-
-
Returns:
-
-
-

-
-
crop(box=None) [#]
-
-

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 load method on the cropped -copy. - -

-
The
-
-
-
Returns:
-
-
-

-
-
draft(mode, size) [#]
-
-

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. - -

-
mode
-
-
-
size
-
-
-

-
-
filter(filter) [#]
-
-

Filters this image using the given filter. For a list of -available filters, see the ImageFilter module. - -

-
filter
-
-
-
Returns:
-
-
-

-
-
fromstring(data, decoder_name="raw", *args) [#]
-
-

Loads this image with pixel data from a string. -

-This method is similar to the fromstring function, but -loads data into this image instead of creating a new image -object. -

-
getbands() [#]
-
-

Returns a tuple containing the name of each band in this image. -For example, getbands on an RGB image returns ("R", "G", "B"). - -

-
Returns:
-
-
-

-
-
getbbox() [#]
-
-
-
Returns:
-
-
-

-
-
getcolors(maxcolors=256) [#]
-
-
-
maxcolors
-
-
-
Returns:
-
-
-

-
-
getdata(band=None) [#]
-
-

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()). - -

-
band
-
-
-
Returns:
-
-
-

-
-
getextrema() [#]
-
-
-
Returns:
-
-
-

-
-
getim() [#]
-
-
-
Returns:
-
-
-

-
-
getpalette() [#]
-
-
-
Returns:
-
-
-

-
-
getpixel(xy) [#]
-
-
-
xy
-
-
-
Returns:
-
-
-

-
-
getprojection() [#]
-
-
-
Returns:
-
-
-

-
-
histogram(mask=None) [#]
-
-

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"). - -

-
mask
-
-
-
Returns:
-
-
-

-
-
load() [#]
-
-
-
Returns:
-
-
-

-
-
offset(xoffset, yoffset=None) [#]
-
-

(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. - -

-
xoffset
-
-
-
yoffset
-
-
-
Returns:
-
-
-

-
-
paste(im, box=None, mask=None) [#]
-
-

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 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. - -

-
im
-
-
-
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. -

-
mask
-
-
-
Returns:
-
-
-

-
-
point(lut, mode=None) [#]
-
-
-
lut
-
-
-
mode
-
-
-
Returns:
-
-
-

-
-
putalpha(alpha) [#]
-
-
-
im
-
-
-

-
-
putdata(data, scale=1.0, offset=0.0) [#]
-
-

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. - -

-
data
-
-
-
scale
-
-
-
offset
-
-
-

-
-
putpalette(data) [#]
-
-
-
data
-
-
-

-
-
putpixel(xy, value) [#]
-
-

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 paste or the ImageDraw module -instead. - -

-
xy
-
-
-
value
-
-
-

-
-
resize(size, filter=NEAREST) [#]
-
-
-
size
-
-
-
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. -
-
Returns:
-
-
-

-
-
rotate(angle, filter=NEAREST) [#]
-
-
-
angle
-
-
-
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. -
-
Returns:
-
-
-

-
-
save(file, format=None, **options) [#]
-
-

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. - -

-
file
-
-
-
format
-
-
-
**options
-
-
-
Returns:
-
-
-
Raises KeyError:
-
-
Raises IOError:
-
-

-
-
seek(frame) [#]
-
-

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. - -

-
frame
-
-
-
Raises EOFError:
-
-

-
-
show(title=None) [#]
-
-

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). - -

-
title
-
-
-

-
-
split() [#]
-
-
-
Returns:
-
-
-

-
-
tell() [#]
-
-
-
Returns:
-
-
-

-
-
thumbnail(size, resample=NEAREST) [#]
-
-

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 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 copy of the original image. - -

-
size
-
-
-
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). -
-
Returns:
-
-
-

-
-
tobitmap(name="image") [#]
-
-
-
name
-
-
-
Returns:
-
-
-
Raises ValueError:
-
-

-
-
tostring(encoder_name="raw", *args) [#]
-
-
-
encoder_name
-
-
-
*args
-
-
-
Returns:
-
-
-

-
-
transform(size, method, data, resample=NEAREST) [#]
-
-

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. -

-

-
size
-
-
-
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). -
-
data
-
-
-
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. -
-
Returns:
-
-
-

-
-
transpose(method) [#]
-
-
-
method
-
-One of FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, -ROTATE_90, ROTATE_180, or ROTATE_270. -
-

-
-
verify() [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageChops.html b/Docs/pythondoc-PIL.ImageChops.html deleted file mode 100644 index 2e665b577..000000000 --- a/Docs/pythondoc-PIL.ImageChops.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - -The PIL.ImageChops Module - - - -

The PIL.ImageChops Module

-

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). -

Module Contents

-
-
add(image1, image2, scale=1.0, offset=0) [#]
-
-

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. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
add_modulo(image1, image2) [#]
-
-

Add images without clipping -((image1 + image2) % MAX). -

-Adds two images, without clipping the result. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
blend(image1, image2, alpha) [#]
-
-

Blend images using constant transparency weight. -

-Same as the blend function in the Image module. -

-
composite(image1, image2, mask) [#]
-
-

Create composite using transparency mask. -

-Same as the composite function in the Image module. -

-
constant(image, value) [#]
-
-
-
image
-
-
-
value
-
-
-
Returns:
-
-
-

-
-
darker(image1, image2) [#]
-
-

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. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
difference(image1, image2) [#]
-
-

Calculate absolute difference -(abs(image1 - image2)). -

-Returns the absolute value of the difference between the two images. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
duplicate(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
invert(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
lighter(image1, image2) [#]
-
-

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. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
logical_and(image1, image2) [#]
-
-
-
logical_or(image1, image2) [#]
-
-
-
logical_xor(image1, image2) [#]
-
-
-
multiply(image1, image2) [#]
-
-

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. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
offset(image, xoffset, yoffset=None) [#]
-
-

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. - -

-
image
-
-
-
xoffset
-
-
-
yoffset
-
-
-
Returns:
-
-
-

-
-
screen(image1, image2) [#]
-
-

Superimpose negative images -(MAX - ((MAX - image1) * (MAX - image2) / MAX)). -

-Superimposes two inverted images on top of each other. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
subtract(image1, image2, scale=1.0, offset=0) [#]
-
-

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. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
subtract_modulo(image1, image2) [#]
-
-

Subtract images without clipping -((image1 - image2) % MAX). -

-Subtracts two images, without clipping the result. - -

-
image1
-
-
-
image1
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageColor.html b/Docs/pythondoc-PIL.ImageColor.html deleted file mode 100644 index b409ccff9..000000000 --- a/Docs/pythondoc-PIL.ImageColor.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - -The PIL.ImageColor Module - - - -

The PIL.ImageColor Module

-
-
getrgb(color) [#]
-
-
-
color
-
-
-
Returns:
-
-
-
Raises ValueError:
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageDraw.html b/Docs/pythondoc-PIL.ImageDraw.html deleted file mode 100644 index 1e72665f4..000000000 --- a/Docs/pythondoc-PIL.ImageDraw.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - -The PIL.ImageDraw Module - - - -

The PIL.ImageDraw Module

-
-
Draw(im, mode=None) [#]
-
-
-
im
-
-
-
mode
-
-
-

-
-
getdraw(im=None, hints=None) [#]
-
-
-
im
-
-
-
hints
-
-
-
Returns:
-
-
-

-
-
ImageDraw(im, mode=None) (class) [#]
-
-

A simple 2D drawing interface for PIL images.

-

For more information about this class, see The ImageDraw Class.

-
-
-

The ImageDraw Class

-
-
ImageDraw(im, mode=None) (class) [#]
-
-

A simple 2D drawing interface for PIL images. -

-Application code should use the Draw factory, instead of -directly. -

-
__init__(im, mode=None) [#]
-
-
-
im
-
-
-
mode
-
-
-

-
-
arc(xy, start, end, fill=None) [#]
-
-
-
bitmap(xy, bitmap, fill=None) [#]
-
-
-
chord(xy, start, end, fill=None, outline=None) [#]
-
-
-
ellipse(xy, fill=None, outline=None) [#]
-
-
-
getfont() [#]
-
-
-
line(xy, fill=None, width=0) [#]
-
-
-
pieslice(xy, start, end, fill=None, outline=None) [#]
-
-
-
point(xy, fill=None) [#]
-
-
-
polygon(xy, fill=None, outline=None) [#]
-
-
-
rectangle(xy, fill=None, outline=None) [#]
-
-
-
setfill(onoff) [#]
-
-
-
setfont(font) [#]
-
-
-
setink(ink) [#]
-
-
-
shape(shape, fill=None, outline=None) [#]
-
-
-
text(xy, text, fill=None, font=None, anchor=None) [#]
-
-
-
textsize(text, font=None) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageEnhance.html b/Docs/pythondoc-PIL.ImageEnhance.html deleted file mode 100644 index e7493d098..000000000 --- a/Docs/pythondoc-PIL.ImageEnhance.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - -The PIL.ImageEnhance Module - - - -

The PIL.ImageEnhance Module

-
-
Brightness(image) (class) [#]
-
-

Brightness enhancement object.

-

For more information about this class, see The Brightness Class.

-
-
Color(image) (class) [#]
-
-

Color enhancement object.

-

For more information about this class, see The Color Class.

-
-
Contrast(image) (class) [#]
-
-

Contrast enhancement object.

-

For more information about this class, see The Contrast Class.

-
-
Sharpness(image) (class) [#]
-
-

Sharpness enhancement object.

-

For more information about this class, see The Sharpness Class.

-
-
-

The Brightness Class

-
-
Brightness(image) (class) [#]
-
-

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. -

-
-

The Color Class

-
-
Color(image) (class) [#]
-
-

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. -

-
-

The Contrast Class

-
-
Contrast(image) (class) [#]
-
-

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 an solid grey image, factor 1.0 gives the original image. -

-
-

The Sharpness Class

-
-
Sharpness(image) (class) [#]
-
-

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. -

-
- diff --git a/Docs/pythondoc-PIL.ImageFile.html b/Docs/pythondoc-PIL.ImageFile.html deleted file mode 100644 index 6755b69ae..000000000 --- a/Docs/pythondoc-PIL.ImageFile.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - -The PIL.ImageFile Module - - - -

The PIL.ImageFile Module

-
-
_ParserFile(data) (class) [#]
-
-

(Internal) Support class for the Parser file.

-

For more information about this class, see The _ParserFile Class.

-
-
_safe_read(fp, size) [#]
-
-
-
fp
-
-File handle. Must implement a read method. -
-
size
-
-
-
Returns:
-
-A string containing up to size bytes of data. -
-

-
-
_save(im, fp, tile) [#]
-
-
-
im
-
-
-
fp
-
-
-
tile
-
-
-

-
-
ImageFile(fp=None, filename=None) (class) [#]
-
-

Base class for image file handlers.

-

For more information about this class, see The ImageFile Class.

-
-
Parser (class) [#]
-
-

Incremental image parser.

-

For more information about this class, see The Parser Class.

-
-
StubImageFile (class) [#]
-
-

Base class for stub image loaders.

-

For more information about this class, see The StubImageFile Class.

-
-
-

The _ParserFile Class

-
-
_ParserFile(data) (class) [#]
-
-

(Internal) Support class for the Parser file. -

-
-

The ImageFile Class

-
-
ImageFile(fp=None, filename=None) (class) [#]
-
-
-
-

The Parser Class

-
-
Parser (class) [#]
-
-
-
close() [#]
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
feed(data) [#]
-
-
-
data
-
-
-
Raises IOError:
-
-

-
-
reset() [#]
-
-
-
-

The StubImageFile Class

-
-
StubImageFile (class) [#]
-
-

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. -

-
_load() [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageFileIO.html b/Docs/pythondoc-PIL.ImageFileIO.html deleted file mode 100644 index 78fc226c1..000000000 --- a/Docs/pythondoc-PIL.ImageFileIO.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - -The PIL.ImageFileIO Module - - - -

The PIL.ImageFileIO Module

-
-
ImageFileIO(fp) (class) [#]
-
-

The ImageFileIO module can be used to read an image from a -socket, or any other stream device.

-

For more information about this class, see The ImageFileIO Class.

-
-
-

The ImageFileIO Class

-
-
ImageFileIO(fp) (class) [#]
-
-

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. - -

-
__init__(fp) [#]
-
-

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. - -

-
fp
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageFilter.html b/Docs/pythondoc-PIL.ImageFilter.html deleted file mode 100644 index b67e8223b..000000000 --- a/Docs/pythondoc-PIL.ImageFilter.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - -The PIL.ImageFilter Module - - - -

The PIL.ImageFilter Module

-
-
BLUR (class) [#]
-
-

Blur filter.

-

For more information about this class, see The BLUR Class.

-
-
CONTOUR (class) [#]
-
-

Contour filter.

-

For more information about this class, see The CONTOUR Class.

-
-
DETAIL (class) [#]
-
-

Detail filter.

-

For more information about this class, see The DETAIL Class.

-
-
EDGE_ENHANCE (class) [#]
-
-

Edge enhancement filter.

-

For more information about this class, see The EDGE_ENHANCE Class.

-
-
EDGE_ENHANCE_MORE (class) [#]
-
-

Stronger edge enhancement filter.

-

For more information about this class, see The EDGE_ENHANCE_MORE Class.

-
-
EMBOSS (class) [#]
-
-

Embossing filter.

-

For more information about this class, see The EMBOSS Class.

-
-
FIND_EDGES (class) [#]
-
-

Edge-finding filter.

-

For more information about this class, see The FIND_EDGES Class.

-
-
Kernel(size, kernel, **options) (class) [#]
-
-

Convolution filter kernel.

-

For more information about this class, see The Kernel Class.

-
-
MaxFilter(size=3) (class) [#]
-
-

Max filter.

-

For more information about this class, see The MaxFilter Class.

-
-
MedianFilter(size=3) (class) [#]
-
-

Median filter.

-

For more information about this class, see The MedianFilter Class.

-
-
MinFilter(size=3) (class) [#]
-
-

Min filter.

-

For more information about this class, see The MinFilter Class.

-
-
ModeFilter(size=3) (class) [#]
-
-

Mode filter.

-

For more information about this class, see The ModeFilter Class.

-
-
RankFilter(size, rank) (class) [#]
-
-

Rank filter.

-

For more information about this class, see The RankFilter Class.

-
-
SHARPEN (class) [#]
-
-

Sharpening filter.

-

For more information about this class, see The SHARPEN Class.

-
-
SMOOTH (class) [#]
-
-

Smoothing filter.

-

For more information about this class, see The SMOOTH Class.

-
-
SMOOTH_MORE (class) [#]
-
-

Stronger smoothing filter.

-

For more information about this class, see The SMOOTH_MORE Class.

-
-
-

The BLUR Class

-
-
BLUR (class) [#]
-
-
-
-

The CONTOUR Class

-
-
CONTOUR (class) [#]
-
-
-
-

The DETAIL Class

-
-
DETAIL (class) [#]
-
-
-
-

The EDGE_ENHANCE Class

-
-
EDGE_ENHANCE (class) [#]
-
-
-
-

The EDGE_ENHANCE_MORE Class

-
-
EDGE_ENHANCE_MORE (class) [#]
-
-
-
-

The EMBOSS Class

-
-
EMBOSS (class) [#]
-
-
-
-

The FIND_EDGES Class

-
-
FIND_EDGES (class) [#]
-
-
-
-

The Kernel Class

-
-
Kernel(size, kernel, **options) (class) [#]
-
-
-
__init__(size, kernel, **options) [#]
-
-

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. - -

-
size
-
-
-
kernel
-
-
-
**options
-
-
-
scale=
-
-
-
offset=
-
-
-

-
-
-

The MaxFilter Class

-
-
MaxFilter(size=3) (class) [#]
-
-
-
__init__(size=3) [#]
-
-
-
size
-
-
-

-
-
-

The MedianFilter Class

-
-
MedianFilter(size=3) (class) [#]
-
-
-
__init__(size=3) [#]
-
-
-
size
-
-
-

-
-
-

The MinFilter Class

-
-
MinFilter(size=3) (class) [#]
-
-
-
__init__(size=3) [#]
-
-
-
size
-
-
-

-
-
-

The ModeFilter Class

-
-
ModeFilter(size=3) (class) [#]
-
-
-
__init__(size=3) [#]
-
-
-
size
-
-
-

-
-
-

The RankFilter Class

-
-
RankFilter(size, rank) (class) [#]
-
-
-
__init__(size, rank) [#]
-
-
-
size
-
-
-
rank
-
-
-

-
-
-

The SHARPEN Class

-
-
SHARPEN (class) [#]
-
-
-
-

The SMOOTH Class

-
-
SMOOTH (class) [#]
-
-
-
-

The SMOOTH_MORE Class

-
-
SMOOTH_MORE (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageFont.html b/Docs/pythondoc-PIL.ImageFont.html deleted file mode 100644 index 6f1cd885d..000000000 --- a/Docs/pythondoc-PIL.ImageFont.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - -The PIL.ImageFont Module - - - -

The PIL.ImageFont Module

-
-
FreeTypeFont(file, size, index=0, encoding="") (class) [#]
-
-

Wrapper for FreeType fonts.

-

For more information about this class, see The FreeTypeFont Class.

-
-
ImageFont (class) [#]
-
-

The ImageFont module defines a class with the same name.

-

For more information about this class, see The ImageFont Class.

-
-
load(filename) [#]
-
-
-
filename
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
load_default() [#]
-
-
-
Returns:
-
-
-

-
-
load_path(filename) [#]
-
-
-
filename
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
TransposedFont(font, orientation=None) (class) [#]
-
-

Wrapper that creates a transposed font from any existing font -object.

-
-
font
-
-
-
orientation
-
-
-

-

For more information about this class, see The TransposedFont Class.

-
-
truetype(filename, size, index=0, encoding="") [#]
-
-

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. - -

-
filename
-
-A truetype font file. Under Windows, if the file - is not found in this filename, the loader also looks in Windows - fonts directory -
-
size
-
-
-
index
-
-
-
encoding
-
-
-
Returns:
-
-
-
Raises IOError:
-
-

-
-
-

The FreeTypeFont Class

-
-
FreeTypeFont(file, size, index=0, encoding="") (class) [#]
-
-

Wrapper for FreeType fonts. Application code should use the -truetype factory function to create font objects. -

-
-

The ImageFont Class

-
-
ImageFont (class) [#]
-
-

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 - -

-
-

The TransposedFont Class

-
-
TransposedFont(font, orientation=None) (class) [#]
-
-
-
font
-
-
-
orientation
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageGL.html b/Docs/pythondoc-PIL.ImageGL.html deleted file mode 100644 index 6fee03f8b..000000000 --- a/Docs/pythondoc-PIL.ImageGL.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - -The PIL.ImageGL Module - - - -

The PIL.ImageGL Module

-

Module Contents

-
-
TextureFactory (class) [#]
-
-

Texture factory.

-

For more information about this class, see The TextureFactory Class.

-
-
-

The TextureFactory Class

-
-
TextureFactory (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageGrab.html b/Docs/pythondoc-PIL.ImageGrab.html deleted file mode 100644 index fb1354e05..000000000 --- a/Docs/pythondoc-PIL.ImageGrab.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - -The PIL.ImageGrab Module - - - -

The PIL.ImageGrab Module

-

(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.

- -

Module Contents

-
-
grab(bbox=None) [#]
-
-
-
bbox
-
-
-
Returns:
-
-
-

-
-
grabclipboard() [#]
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageOps.html b/Docs/pythondoc-PIL.ImageOps.html deleted file mode 100644 index 1554571a0..000000000 --- a/Docs/pythondoc-PIL.ImageOps.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - -The PIL.ImageOps Module - - - -

The PIL.ImageOps Module

-

(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. - -

Module Contents

-
-
autocontrast(image, cutoff=0, ignore=None) [#]
-
-

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). - -

-
image
-
-
-
cutoff
-
-
-
ignore
-
-
-
Returns:
-
-
-

-
-
colorize(image, black, white) [#]
-
-

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. - -

-
image
-
-
-
black
-
-
-
white
-
-
-
Returns:
-
-
-

-
-
crop(image, border=0) [#]
-
-
-
image
-
-
-
border
-
-
-
Returns:
-
-
-

-
-
deform(image, deformer, resample=Image.BILINEAR) [#]
-
-
-
image
-
-
-
deformer
-
-
-
resample
-
-
-
Returns:
-
-
-

-
-
equalize(image, mask=None) [#]
-
-
-
image
-
-
-
mask
-
-
-
Returns:
-
-
-

-
-
expand(image, border=0, fill=0) [#]
-
-
-
image
-
-
-
border
-
-
-
fill
-
-
-
Returns:
-
-
-

-
-
fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)) [#]
-
-

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. - -

-
size
-
-
-
method
-
-
-
bleed
-
-
-
centering
-
-
-
Returns:
-
-
-

-
-
flip(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
grayscale(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
invert(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
mirror(image) [#]
-
-
-
image
-
-
-
Returns:
-
-
-

-
-
posterize(image, bits) [#]
-
-
-
image
-
-
-
bits
-
-
-
Returns:
-
-
-

-
-
solarize(image, threshold=128) [#]
-
-
-
image
-
-
-
threshold
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImagePalette.html b/Docs/pythondoc-PIL.ImagePalette.html deleted file mode 100644 index 67e0d114f..000000000 --- a/Docs/pythondoc-PIL.ImagePalette.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.ImagePalette Module - - - -

The PIL.ImagePalette Module

-
-
ImagePalette(mode="RGB", palette=None) (class) [#]
-
-

Colour palette wrapper for palette mapped images.

-

For more information about this class, see The ImagePalette Class.

-
-
-

The ImagePalette Class

-
-
ImagePalette(mode="RGB", palette=None) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImagePath.html b/Docs/pythondoc-PIL.ImagePath.html deleted file mode 100644 index 67743bdfe..000000000 --- a/Docs/pythondoc-PIL.ImagePath.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - -The PIL.ImagePath Module - - - -

The PIL.ImagePath Module

-
-
Path(xy) (class) [#]
-
-

Path wrapper.

-

For more information about this class, see The Path Class.

-
-
-

The Path Class

-
-
Path(xy) (class) [#]
-
-
-
__init__(xy) [#]
-
-
-
xy
-
-
-

-
-
compact(distance=2) [#]
-
-
-
getbbox() [#]
-
-
-
map(function) [#]
-
-
-
tolist(flat=0) [#]
-
-
-
flat
-
-
-
Returns:
-
-
-

-
-
transform(matrix) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImageSequence.html b/Docs/pythondoc-PIL.ImageSequence.html deleted file mode 100644 index 848e7a200..000000000 --- a/Docs/pythondoc-PIL.ImageSequence.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -The PIL.ImageSequence Module - - - -

The PIL.ImageSequence Module

-
-
Iterator(im) (class) [#]
-
-

This class implements an iterator object that can be used to loop -over an image sequence.

-

For more information about this class, see The Iterator Class.

-
-
-

The Iterator Class

-
-
Iterator(im) (class) [#]
-
-
-
__init__(im) [#]
-
-
-
im
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageStat.html b/Docs/pythondoc-PIL.ImageStat.html deleted file mode 100644 index 10a308b33..000000000 --- a/Docs/pythondoc-PIL.ImageStat.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - -The PIL.ImageStat Module - - - -

The PIL.ImageStat Module

-

The ImageStat module calculates global statistics for an -image, or a region of an image. -

Module Contents

-
-
Stat(image, mask=None) (class) [#]
-
-

Calculate statistics for the given image.

-

For more information about this class, see The Stat Class.

-
-
-

The Stat Class

-
-
Stat(image, mask=None) (class) [#]
-
-
-
__init__(image, mask=None) [#]
-
-
-
image
-
-
-
mask
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageTk.html b/Docs/pythondoc-PIL.ImageTk.html deleted file mode 100644 index d5d4e2381..000000000 --- a/Docs/pythondoc-PIL.ImageTk.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - -The PIL.ImageTk Module - - - -

The PIL.ImageTk Module

-

The ImageTk module contains support to create and modify -Tkinter BitmapImage and PhotoImage objects. -

-For examples, see the demo programs in the Scripts -directory. -

Module Contents

-
-
BitmapImage(image=None, **options) (class) [#]
-
-

Create a Tkinter-compatible bitmap image.

-

For more information about this class, see The BitmapImage Class.

-
-
getimage(photo) [#]
-
-
-
PhotoImage(image=None, size=None, **options) (class) [#]
-
-

Creates a Tkinter-compatible photo image.

-

For more information about this class, see The PhotoImage Class.

-
-
-

The BitmapImage Class

-
-
BitmapImage(image=None, **options) (class) [#]
-
-
-
__init__(image=None, **options) [#]
-
-

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. - -

-
image
-
-
-

-
-
__str__() [#]
-
-
-
Returns:
-
-
-

-
-
height() [#]
-
-
-
Returns:
-
-
-

-
-
width() [#]
-
-
-
Returns:
-
-
-

-
-
-

The PhotoImage Class

-
-
PhotoImage(image=None, size=None, **options) (class) [#]
-
-
-
__init__(image=None, size=None, **options) [#]
-
-

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. -

-

-
image
-
-
-
size
-
-
-
file=
-
-
-
data=
-
-
-

-
-
__str__() [#]
-
-
-
Returns:
-
-
-

-
-
height() [#]
-
-
-
Returns:
-
-
-

-
-
paste(im, box=None) [#]
-
-
-
im
-
-
-
box
-
-
-

-
-
width() [#]
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageTransform.html b/Docs/pythondoc-PIL.ImageTransform.html deleted file mode 100644 index 6771fb148..000000000 --- a/Docs/pythondoc-PIL.ImageTransform.html +++ /dev/null @@ -1,140 +0,0 @@ - - - - -The PIL.ImageTransform Module - - - -

The PIL.ImageTransform Module

-
-
AffineTransform (class) [#]
-
-

Define an affine image transform.

-
-
matrix
-
-A 6-tuple (a, b, c, d, e, f) containing - the first two rows from an affine transform matrix. -
-

-

For more information about this class, see The AffineTransform Class.

-
-
ExtentTransform (class) [#]
-
-

Define a transform to extract a subregion from an image.

-
-
bbox
-
-A 4-tuple (x0, y0, x1, y1) which specifies - two points in the input image's coordinate system. -
-

-

For more information about this class, see The ExtentTransform Class.

-
-
MeshTransform (class) [#]
-
-

Define an mesh image transform.

-
-
data
-
-
-

-

For more information about this class, see The MeshTransform Class.

-
-
QuadTransform (class) [#]
-
-

Define an quad image transform.

-
-
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. -
-

-

For more information about this class, see The QuadTransform Class.

-
-
-

The AffineTransform Class

-
-
AffineTransform (class) [#]
-
-

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. - -

-
matrix
-
-A 6-tuple (a, b, c, d, e, f) containing - the first two rows from an affine transform matrix. -
-

-
-
-

The ExtentTransform Class

-
-
ExtentTransform (class) [#]
-
-

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. - -

-
bbox
-
-A 4-tuple (x0, y0, x1, y1) which specifies - two points in the input image's coordinate system. -
-

-
-
-

The MeshTransform Class

-
-
MeshTransform (class) [#]
-
-
-
data
-
-
-

-
-
-

The QuadTransform Class

-
-
QuadTransform (class) [#]
-
-

Define an quad image transform. -

-Maps a quadrilateral (a region defined by four corners) from the -image to a rectangle of the given size. - -

-
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. -
-

-
-
- diff --git a/Docs/pythondoc-PIL.ImageWin.html b/Docs/pythondoc-PIL.ImageWin.html deleted file mode 100644 index e1608d15a..000000000 --- a/Docs/pythondoc-PIL.ImageWin.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - -The PIL.ImageWin Module - - - -

The PIL.ImageWin Module

-
-
Dib(image, size=None) (class) [#]
-
-

Create a Windows bitmap with the given mode and size.

-

For more information about this class, see The Dib Class.

-
-
HDC(dc) (class) [#]
-
-

The ImageWin module contains support to create and display -images under Windows 95/98, NT, 2000 and later.

-

For more information about this class, see The HDC Class.

-
-
ImageWindow(image, title="PIL") (class) [#]
-
-

Create an image window which displays the given image.

-

For more information about this class, see The ImageWindow Class.

-
-
Window(title="PIL", width=None, height=None) (class) [#]
-
-

Create a Window with the given title size.

-

For more information about this class, see The Window Class.

-
-
-

The Dib Class

-
-
Dib(image, size=None) (class) [#]
-
-

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. -

-
__init__(image, size=None) [#]
-
-
-
image
-
-
-
size
-
-
-

-
-
expose(handle) [#]
-
-
-
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. -
-

-
-
fromstring(buffer) [#]
-
-
-
buffer
-
-A string buffer containing display data (usually - data returned from tostring) -
-

-
-
paste(im, box=None) [#]
-
-
-
im
-
-
-
box
-
-
-

-
-
query_palette(handle) [#]
-
-

Installs the palette associated with the image in the -given device context. -

-This method should be called upon QUERYNEWPALETTE -and PALETTECHANGED events from Windows. If this -method returns a non-zero value, one or more display -palette entries were changed, and the image should be -redrawn. - -

-
handle
-
-
-
Returns:
-
-
-

-
-
tostring() [#]
-
-
-
Returns:
-
-
-

-
-
-

The HDC Class

-
-
HDC(dc) (class) [#]
-
-

The ImageWin module contains support to create and display -images under Windows 95/98, NT, 2000 and later. -

-
-

The ImageWindow Class

-
-
ImageWindow(image, title="PIL") (class) [#]
-
-
-
-

The Window Class

-
-
Window(title="PIL", width=None, height=None) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.ImtImagePlugin.html b/Docs/pythondoc-PIL.ImtImagePlugin.html deleted file mode 100644 index 7f2071436..000000000 --- a/Docs/pythondoc-PIL.ImtImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.ImtImagePlugin Module - - - -

The PIL.ImtImagePlugin Module

-
-
ImtImageFile (class) [#]
-
-

Image plugin for IM Tools images.

-

For more information about this class, see The ImtImageFile Class.

-
-
-

The ImtImageFile Class

-
-
ImtImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.IptcImagePlugin.html b/Docs/pythondoc-PIL.IptcImagePlugin.html deleted file mode 100644 index fe503036a..000000000 --- a/Docs/pythondoc-PIL.IptcImagePlugin.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - -The PIL.IptcImagePlugin Module - - - -

The PIL.IptcImagePlugin Module

-
-
getiptcinfo(im) [#]
-
-
-
im
-
-
-
Returns:
-
-
-

-
-
IptcImageFile (class) [#]
-
-

Image plugin for IPTC/NAA datastreams.

-

For more information about this class, see The IptcImageFile Class.

-
-
-

The IptcImageFile Class

-
-
IptcImageFile (class) [#]
-
-

Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields -from TIFF and JPEG files, use the getiptcinfo function. -

-
- diff --git a/Docs/pythondoc-PIL.JpegImagePlugin.html b/Docs/pythondoc-PIL.JpegImagePlugin.html deleted file mode 100644 index fc19e7c46..000000000 --- a/Docs/pythondoc-PIL.JpegImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.JpegImagePlugin Module - - - -

The PIL.JpegImagePlugin Module

-
-
JpegImageFile (class) [#]
-
-

Image plugin for JPEG and JFIF images.

-

For more information about this class, see The JpegImageFile Class.

-
-
-

The JpegImageFile Class

-
-
JpegImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.McIdasImagePlugin.html b/Docs/pythondoc-PIL.McIdasImagePlugin.html deleted file mode 100644 index a59530323..000000000 --- a/Docs/pythondoc-PIL.McIdasImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.McIdasImagePlugin Module - - - -

The PIL.McIdasImagePlugin Module

-
-
McIdasImageFile (class) [#]
-
-

Image plugin for McIdas area images.

-

For more information about this class, see The McIdasImageFile Class.

-
-
-

The McIdasImageFile Class

-
-
McIdasImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.MicImagePlugin.html b/Docs/pythondoc-PIL.MicImagePlugin.html deleted file mode 100644 index fdd746735..000000000 --- a/Docs/pythondoc-PIL.MicImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.MicImagePlugin Module - - - -

The PIL.MicImagePlugin Module

-
-
MicImageFile (class) [#]
-
-

Image plugin for Microsoft's Image Composer file format.

-

For more information about this class, see The MicImageFile Class.

-
-
-

The MicImageFile Class

-
-
MicImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.MpegImagePlugin.html b/Docs/pythondoc-PIL.MpegImagePlugin.html deleted file mode 100644 index b40d0c28e..000000000 --- a/Docs/pythondoc-PIL.MpegImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.MpegImagePlugin Module - - - -

The PIL.MpegImagePlugin Module

-
-
MpegImageFile (class) [#]
-
-

Image plugin for MPEG streams.

-

For more information about this class, see The MpegImageFile Class.

-
-
-

The MpegImageFile Class

-
-
MpegImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.MspImagePlugin.html b/Docs/pythondoc-PIL.MspImagePlugin.html deleted file mode 100644 index c17e99128..000000000 --- a/Docs/pythondoc-PIL.MspImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.MspImagePlugin Module - - - -

The PIL.MspImagePlugin Module

-
-
MspImageFile (class) [#]
-
-

Image plugin for Windows MSP images.

-

For more information about this class, see The MspImageFile Class.

-
-
-

The MspImageFile Class

-
-
MspImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.OleFileIO.html b/Docs/pythondoc-PIL.OleFileIO.html deleted file mode 100644 index f56a6b41d..000000000 --- a/Docs/pythondoc-PIL.OleFileIO.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - -The PIL.OleFileIO Module - - - -

The PIL.OleFileIO Module

-
-
OleFileIO(filename=None) (class) [#]
-
-

This class encapsulates the interface to an OLE 2 structured -storage file.

-

For more information about this class, see The OleFileIO Class.

-
-
-

The OleFileIO Class

-
-
OleFileIO(filename=None) (class) [#]
-
-

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. -

-
getproperties(filename) [#]
-
-
-
listdir() [#]
-
-
-
open(filename) [#]
-
-
-
openstream(filename) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PSDraw.html b/Docs/pythondoc-PIL.PSDraw.html deleted file mode 100644 index f89d796f0..000000000 --- a/Docs/pythondoc-PIL.PSDraw.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PSDraw Module - - - -

The PIL.PSDraw Module

-
-
PSDraw(fp=None) (class) [#]
-
-

Simple Postscript graphics interface.

-

For more information about this class, see The PSDraw Class.

-
-
-

The PSDraw Class

-
-
PSDraw(fp=None) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PaletteFile.html b/Docs/pythondoc-PIL.PaletteFile.html deleted file mode 100644 index 65e4de4e6..000000000 --- a/Docs/pythondoc-PIL.PaletteFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PaletteFile Module - - - -

The PIL.PaletteFile Module

-
-
PaletteFile(fp) (class) [#]
-
-

File handler for Teragon-style palette files.

-

For more information about this class, see The PaletteFile Class.

-
-
-

The PaletteFile Class

-
-
PaletteFile(fp) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PalmImagePlugin.html b/Docs/pythondoc-PIL.PalmImagePlugin.html deleted file mode 100644 index d5a3cb394..000000000 --- a/Docs/pythondoc-PIL.PalmImagePlugin.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - -The PIL.PalmImagePlugin Module - - - -

The PIL.PalmImagePlugin Module

-

Module Contents

-
-
_save(im, fp, filename, check=0) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PcdImagePlugin.html b/Docs/pythondoc-PIL.PcdImagePlugin.html deleted file mode 100644 index feb686038..000000000 --- a/Docs/pythondoc-PIL.PcdImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PcdImagePlugin Module - - - -

The PIL.PcdImagePlugin Module

-
-
PcdImageFile (class) [#]
-
-

Image plugin for PhotoCD images.

-

For more information about this class, see The PcdImageFile Class.

-
-
-

The PcdImageFile Class

-
-
PcdImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PcfFontFile.html b/Docs/pythondoc-PIL.PcfFontFile.html deleted file mode 100644 index 8b1e41be9..000000000 --- a/Docs/pythondoc-PIL.PcfFontFile.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PcfFontFile Module - - - -

The PIL.PcfFontFile Module

-
-
PcfFontFile(fp) (class) [#]
-
-

Font file plugin for the X11 PCF format.

-

For more information about this class, see The PcfFontFile Class.

-
-
-

The PcfFontFile Class

-
-
PcfFontFile(fp) (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PcxImagePlugin.html b/Docs/pythondoc-PIL.PcxImagePlugin.html deleted file mode 100644 index 011349a8d..000000000 --- a/Docs/pythondoc-PIL.PcxImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PcxImagePlugin Module - - - -

The PIL.PcxImagePlugin Module

-
-
PcxImageFile (class) [#]
-
-

Image plugin for Paintbrush images.

-

For more information about this class, see The PcxImageFile Class.

-
-
-

The PcxImageFile Class

-
-
PcxImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PdfImagePlugin.html b/Docs/pythondoc-PIL.PdfImagePlugin.html deleted file mode 100644 index 20e0b3f6e..000000000 --- a/Docs/pythondoc-PIL.PdfImagePlugin.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - -The PIL.PdfImagePlugin Module - - - -

The PIL.PdfImagePlugin Module

-

Module Contents

-
-
_save(im, fp, filename) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PixarImagePlugin.html b/Docs/pythondoc-PIL.PixarImagePlugin.html deleted file mode 100644 index 28b06140c..000000000 --- a/Docs/pythondoc-PIL.PixarImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PixarImagePlugin Module - - - -

The PIL.PixarImagePlugin Module

-
-
PixarImageFile (class) [#]
-
-

Image plugin for PIXAR raster images.

-

For more information about this class, see The PixarImageFile Class.

-
-
-

The PixarImageFile Class

-
-
PixarImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PngImagePlugin.html b/Docs/pythondoc-PIL.PngImagePlugin.html deleted file mode 100644 index ccd8e9325..000000000 --- a/Docs/pythondoc-PIL.PngImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PngImagePlugin Module - - - -

The PIL.PngImagePlugin Module

-
-
PngImageFile (class) [#]
-
-

Image plugin for PNG images.

-

For more information about this class, see The PngImageFile Class.

-
-
-

The PngImageFile Class

-
-
PngImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PpmImagePlugin.html b/Docs/pythondoc-PIL.PpmImagePlugin.html deleted file mode 100644 index 24789682a..000000000 --- a/Docs/pythondoc-PIL.PpmImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PpmImagePlugin Module - - - -

The PIL.PpmImagePlugin Module

-
-
PpmImageFile (class) [#]
-
-

Image plugin for PBM, PGM, and PPM images.

-

For more information about this class, see The PpmImageFile Class.

-
-
-

The PpmImageFile Class

-
-
PpmImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.PsdImagePlugin.html b/Docs/pythondoc-PIL.PsdImagePlugin.html deleted file mode 100644 index 7ed4f8084..000000000 --- a/Docs/pythondoc-PIL.PsdImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.PsdImagePlugin Module - - - -

The PIL.PsdImagePlugin Module

-
-
PsdImageFile (class) [#]
-
-

Image plugin for Photoshop images.

-

For more information about this class, see The PsdImageFile Class.

-
-
-

The PsdImageFile Class

-
-
PsdImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.SgiImagePlugin.html b/Docs/pythondoc-PIL.SgiImagePlugin.html deleted file mode 100644 index 847b35efd..000000000 --- a/Docs/pythondoc-PIL.SgiImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.SgiImagePlugin Module - - - -

The PIL.SgiImagePlugin Module

-
-
SgiImageFile (class) [#]
-
-

Image plugin for SGI images.

-

For more information about this class, see The SgiImageFile Class.

-
-
-

The SgiImageFile Class

-
-
SgiImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.SpiderImagePlugin.html b/Docs/pythondoc-PIL.SpiderImagePlugin.html deleted file mode 100644 index 733f04419..000000000 --- a/Docs/pythondoc-PIL.SpiderImagePlugin.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - -The PIL.SpiderImagePlugin Module - - - -

The PIL.SpiderImagePlugin Module

-

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.

-

Module Contents

-
-
SpiderImageFile (class) [#]
-
-

Image plugin for the SPIDER format.

-

For more information about this class, see The SpiderImageFile Class.

-
-
-

The SpiderImageFile Class

-
-
SpiderImageFile (class) [#]
-
-

Image plugin for the SPIDER format.

-
-
- diff --git a/Docs/pythondoc-PIL.SunImagePlugin.html b/Docs/pythondoc-PIL.SunImagePlugin.html deleted file mode 100644 index fb3cdd987..000000000 --- a/Docs/pythondoc-PIL.SunImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.SunImagePlugin Module - - - -

The PIL.SunImagePlugin Module

-
-
SunImageFile (class) [#]
-
-

Image plugin for Sun raster files.

-

For more information about this class, see The SunImageFile Class.

-
-
-

The SunImageFile Class

-
-
SunImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.TarIO.html b/Docs/pythondoc-PIL.TarIO.html deleted file mode 100644 index 996633b98..000000000 --- a/Docs/pythondoc-PIL.TarIO.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - -The PIL.TarIO Module - - - -

The PIL.TarIO Module

-
-
TarIO(tarfile, file) (class) [#]
-
-

A file object that provides read access to a given member of a TAR -file.

-

For more information about this class, see The TarIO Class.

-
-
-

The TarIO Class

-
-
TarIO(tarfile, file) (class) [#]
-
-
-
__init__(tarfile, file) [#]
-
-
-
tarfile
-
-
-
file
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.TgaImagePlugin.html b/Docs/pythondoc-PIL.TgaImagePlugin.html deleted file mode 100644 index f91381e95..000000000 --- a/Docs/pythondoc-PIL.TgaImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.TgaImagePlugin Module - - - -

The PIL.TgaImagePlugin Module

-
-
TgaImageFile (class) [#]
-
-

Image plugin for Targa files.

-

For more information about this class, see The TgaImageFile Class.

-
-
-

The TgaImageFile Class

-
-
TgaImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.TiffImagePlugin.html b/Docs/pythondoc-PIL.TiffImagePlugin.html deleted file mode 100644 index 56546d260..000000000 --- a/Docs/pythondoc-PIL.TiffImagePlugin.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - -The PIL.TiffImagePlugin Module - - - -

The PIL.TiffImagePlugin Module

-
-
ImageFileDirectory(prefix="II") (class) [#]
-
-

Wrapper for TIFF IFDs.

-

For more information about this class, see The ImageFileDirectory Class.

-
-
TiffImageFile (class) [#]
-
-

Image plugin for TIFF files.

-

For more information about this class, see The TiffImageFile Class.

-
-
-

The ImageFileDirectory Class

-
-
ImageFileDirectory(prefix="II") (class) [#]
-
-
-
-

The TiffImageFile Class

-
-
TiffImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.TiffTags.html b/Docs/pythondoc-PIL.TiffTags.html deleted file mode 100644 index 15d8c98a1..000000000 --- a/Docs/pythondoc-PIL.TiffTags.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - -The PIL.TiffTags Module - - - -

The PIL.TiffTags Module

-

Module Contents

-
-
TAGS (variable) [#]
-
-
-
TYPES (variable) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.WalImageFile.html b/Docs/pythondoc-PIL.WalImageFile.html deleted file mode 100644 index 3b0b54633..000000000 --- a/Docs/pythondoc-PIL.WalImageFile.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - -The PIL.WalImageFile Module - - - -

The PIL.WalImageFile Module

-
-
open(filename) [#]
-
-

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. - -

-
filename
-
-
-
Returns:
-
-
-

-
-
- diff --git a/Docs/pythondoc-PIL.WmfImagePlugin.html b/Docs/pythondoc-PIL.WmfImagePlugin.html deleted file mode 100644 index 51bf10300..000000000 --- a/Docs/pythondoc-PIL.WmfImagePlugin.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - -The PIL.WmfImagePlugin Module - - - -

The PIL.WmfImagePlugin Module

-
-
register_handler(handler) [#]
-
-
-
handler
-
-
-

-
-
WmfStubImageFile (class) [#]
-
-

Image plugin for Windows metafiles.

-

For more information about this class, see The WmfStubImageFile Class.

-
-
-

The WmfStubImageFile Class

-
-
WmfStubImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.XVThumbImagePlugin.html b/Docs/pythondoc-PIL.XVThumbImagePlugin.html deleted file mode 100644 index 30282acab..000000000 --- a/Docs/pythondoc-PIL.XVThumbImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.XVThumbImagePlugin Module - - - -

The PIL.XVThumbImagePlugin Module

-
-
XVThumbImageFile (class) [#]
-
-

Image plugin for XV thumbnail images.

-

For more information about this class, see The XVThumbImageFile Class.

-
-
-

The XVThumbImageFile Class

-
-
XVThumbImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.XbmImagePlugin.html b/Docs/pythondoc-PIL.XbmImagePlugin.html deleted file mode 100644 index 682e05f77..000000000 --- a/Docs/pythondoc-PIL.XbmImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.XbmImagePlugin Module - - - -

The PIL.XbmImagePlugin Module

-
-
XbmImageFile (class) [#]
-
-

Image plugin for X11 bitmaps.

-

For more information about this class, see The XbmImageFile Class.

-
-
-

The XbmImageFile Class

-
-
XbmImageFile (class) [#]
-
-
-
- diff --git a/Docs/pythondoc-PIL.XpmImagePlugin.html b/Docs/pythondoc-PIL.XpmImagePlugin.html deleted file mode 100644 index 218f70477..000000000 --- a/Docs/pythondoc-PIL.XpmImagePlugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - -The PIL.XpmImagePlugin Module - - - -

The PIL.XpmImagePlugin Module

-
-
XpmImageFile (class) [#]
-
-

Image plugin for X11 pixel maps.

-

For more information about this class, see The XpmImageFile Class.

-
-
-

The XpmImageFile Class

-
-
XpmImageFile (class) [#]
-
-
-
- diff --git a/Images/courB08.bdf b/Images/courB08.bdf index 5a48895d1..2be6ab6b9 100644 --- a/Images/courB08.bdf +++ b/Images/courB08.bdf @@ -1,13 +1,13 @@ STARTFONT 2.1 COMMENT $XConsortium: courB08.bdf,v 1.5 94/04/10 21:46:11 gildea Exp $ -COMMENT +COMMENT COMMENT Copyright 1984, 1987 Adobe Systems, Inc. COMMENT Portions Copyright 1988 Digital Equipment Corporation -COMMENT -COMMENT Adobe is a registered trademark of Adobe Systems, Inc. Permission +COMMENT +COMMENT Adobe is a registered trademark of Adobe Systems, Inc. Permission COMMENT to use these trademarks is hereby granted only in association with the COMMENT images described in this file. -COMMENT +COMMENT COMMENT Permission to use, copy, modify, and distribute this software and COMMENT its documentation for any purpose and without fee is hereby granted, COMMENT provided that the above copyright notices appear in all copies and @@ -19,7 +19,7 @@ COMMENT specific, written prior permission. Adobe Systems and Digital COMMENT Equipment Corporation make no representations about the suitability COMMENT of this software for any purpose. It is provided "as is" without COMMENT express or implied warranty. -COMMENT +COMMENT COMMENT ADOBE SYSTEMS AND DIGITAL EQUIPMENT CORPORATION DISCLAIM ALL COMMENT WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED COMMENT WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ADOBE @@ -28,8 +28,8 @@ COMMENT INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER COMMENT RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF COMMENT CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN COMMENT CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -COMMENT -COMMENT +COMMENT +COMMENT FONT -Adobe-Courier-Bold-R-Normal--11-80-100-100-M-60-ISO8859-1 SIZE 8 100 100 FONTBOUNDINGBOX 8 11 -1 -2 diff --git a/Images/flower.webp b/Images/flower.webp new file mode 100644 index 000000000..65d9d52e9 Binary files /dev/null and b/Images/flower.webp differ diff --git a/Images/flower2.webp b/Images/flower2.webp new file mode 100644 index 000000000..0302ea476 Binary files /dev/null and b/Images/flower2.webp differ diff --git a/Images/lena.fli b/Images/lena.fli new file mode 100644 index 000000000..2c7367e22 Binary files /dev/null and b/Images/lena.fli differ diff --git a/Images/lena.ico b/Images/lena.ico new file mode 100644 index 000000000..6fadbe29e Binary files /dev/null and b/Images/lena.ico differ diff --git a/Images/lena.psd b/Images/lena.psd new file mode 100644 index 000000000..c7b19402a Binary files /dev/null and b/Images/lena.psd differ diff --git a/Images/lena.tar b/Images/lena.tar new file mode 100644 index 000000000..3afb1f542 Binary files /dev/null and b/Images/lena.tar differ diff --git a/Images/lena.webp b/Images/lena.webp new file mode 100644 index 000000000..210e1ab0b Binary files /dev/null and b/Images/lena.webp differ diff --git a/Images/lena.xpm b/Images/lena.xpm new file mode 100644 index 000000000..925fd03cb --- /dev/null +++ b/Images/lena.xpm @@ -0,0 +1,175 @@ +/* XPM */ +static char * lena_xpm[] = { +"128 128 44 1", +" c None", +". c #CC9966", +"+ c #663333", +"@ c #996666", +"# c #CC6666", +"$ c #CC9999", +"% c #FFCC99", +"& c #996633", +"* c #FF9966", +"= c #663366", +"- c #FFCCCC", +"; c #CCCC99", +"> c #FF9999", +", c #993333", +"' c #333333", +") c #CCCCCC", +"! c #996699", +"~ c #CC6633", +"{ c #666666", +"] c #999999", +"^ c #666699", +"/ c #330000", +"( c #663300", +"_ c #330033", +": c #999966", +"< c #FFFFCC", +"[ c #333366", +"} c #660033", +"| c #CC99CC", +"1 c #9999CC", +"2 c #660000", +"3 c #FFFFFF", +"4 c #CCCC66", +"5 c #FFCC66", +"6 c #666633", +"7 c #993366", +"8 c #FFFF99", +"9 c #993300", +"0 c #333300", +"a c #FF99CC", +"b c #CCCCFF", +"c c #CC9933", +"d c #CC6699", +"e c #000000", +".....*...*.%*..#&@#&@&#&#&#.&#.#.#.#.#.#..#.*.#...#..#.#.#.#..#..#..#..#..#.#.#@#.>..>...*..*...*....%%%%.&&.@.#.@.#..&#.~.#&#$.", +">%*..*.*..*%.*.&#@&#@#&#&#&#*&.#.#.#.#.*#..#&.~.#.#.#.&.#.#.#.#.#.#&.#..#&.@.@&#&#.>.*.*...*..*..*...%%%%%&&~@#&#.@&##.&#.#.@.>@", +"...>...*.*.>.%*&#,&#&#&@&#.&#.#.#.#.~.#.#.~.>.#.#..~.#*.~.&*.#.*&*.#..#&.#.##.#@&#.$.>.>.*..*..*.....%%;%%@&@.#.@.#..&#..&#.#@+(", +".*...*...%*.*.>&&@@#@&##&#.#&#.~.#.#.#.#.##.#&*.~.#..#.#.#.#.#.#.#.#~.#.#.#&.#&@#&#.>.....*..*..*.*...%%%%%&.&@.#&#.#.#.#$.$&+++", +"..>.*.*.*.%.>.~&#&#&#.@&#&#&.#.#.#..#.~.~.*.~>&>.#.#*.#.#.~.~.#.#.~.#.*&*.#.#.#.#&#..>.>*..>..*.......%%<%%$&#.#&$&#.&#.&>@,++2+", +".*.%...%*.*..*.#&@#@&##&#&#&#&#.&.#*.#.#.#.#*.~*#.~.#~.~*#.#*#.*.#.#.#.#&.#.#.#&@.##.*..%*..*..*>.*.....%%%%&&&.#~.#.#.#.$&+++++", +"..>.*.*.%*.*..*#&,&#@&#&#&#.#.~#.~.&>.~.~#*&#~#.~>#.~$~$&.~..#.#~.~.~.#.#.#.#&$#&#&$.>.>..%*.>....*.....%%%-.@#&.#&#.#.$$,++++++", +"..*.*%.*%.*.>..#&@#&##&#&#&#&.#.#.#.~.#.*.#.*.~.~.#.#.~.##&#.~.#.##.#*&*~.#.#&#.@#&>.>%.*>.%..*.>..*.*..%%%%%&&&.#.&#&#.@++++'=+", +".*...*.*.*.*.*.~#@&#@&#&#&.#~.#&*&*&#.~#.~*#~#.#*#.~.#&>..#.#.#...#.#.#.#.#..#.#&#.#..>...*.*..%*..>...>.%<%-%@.&#.#.$@@++++++=+", +".%.*%.%*.%.*.*.&&,&#&#@&#&#.#&.#&*#.~.*.#..#.*~.#.##..#&..&*&#.##.~.#~.#~.#~#.#.@#@.#..>.>..*.*..>.*.*....%%%%.&&#.#&$,++++=+='+", +"*>.*.*..**..*>.#,@#@#&#&##&#&#*&>&*#.#~.#~.#.#.#*#.&#&.#@#.@..#..@.#..#.#~..#.#&#&#.>.>...*.$..*.*...*.*.>%%%-%&&@&$#@++/++'++=+", +"..%*.%*...*.*.*&,&#&@#&#&&#.#.#.#&.~#.#.#.~.#.#..#$#$.$...$&@.@.#.@.##.#.##.##.#.@#.#..*.>.%*.>...>.#.#....%%<-.&#.@&+++=+=+=+=+", +"*.*.*.*.*%*.*.*&#,@#&##&##&~#&*&*#*&*&*&*@.#.~.#$..$.$.-$%$-%$.@.@.&..#*#.#*&#&##&#.#.>...*....*>.*.*.*.~..%%%%%@@.@++++++=+=++=", +"%..%*..*.*.*..*&&&,@#&##&#.#.#.#.#.#.#.#.#.#.#..@>@.$$$;$%$.$-$%$.#@.#..#.##.##.#@&#$.>.>.>.*>.....*.~.*....%<%<.$+++/+=+=++=/++", +"*.*..~.~.%*.*.*# #&,&#&#&#~.#~.~#.#*&*#..@.#.$.$$.$.$;$;$%$%$-.$.&@#.#~.##.#&##.#.#....*...*.>*.#..#.~....<%%-&+++++'=+[++=++", +".%*.*.&*.*%*.*. @#&#&#.#&#.##.~.#.#..>.#.$&#$@.$$:$$;$;$;$;-;-$.@&#$#.#&###&@.#..#.>..>.>..~..*#*.*.~...%%<-@+++=++=++=++=+", +"*.%*&,&*.*.*.* @#&##&#.~&*&>~.#.>@#&.@#.@$.@$.$.$.$;$;%-;.)%-%$&@.##.#.@.##&#.>.*.#.#.*&*.*.*.#.#..*...-%-+++/+{++_++=+}+", +"*.>.~&~..%*.*. &#&#&#&#.##&.#~#&*&##.@.#&$#.@.$.$;$.%).;-;$;$)%$&@&@#.##,@@.#..>.>..>.>.~.*.#.#>.>.#.*.-$+/+=+=+=++=+=+_+", +".*.&#(#.**.%*. @#&#&##.#&*#.#..##.#&#@.$.$@>$$.$.$%]%;$;;-;)%)%--&@&@&#&#&##.#..*.#.#..>..~.*..#.*...#..++='+++=++=+'+++@", +"%.#&~&~..*c*.* #@&#&#&#.#&*&#*#.&#.#.#@##.&.@.$$.$%$$;$%]%)$;$;-<-%@&@$&##@&#.>.>.#.#&#.*.*.*.>*.~.*..>++++=+=++'=+++=++$", +"*.~&,&#.*%.*.*. &#@#&#.#&#.#.#.#&#&#&@.#..@$#.#.@.$.]%;-;-;$;$;-;)--<$@#&,@&##.*.*.>.#&#&$..*.#.*.#.*$.$&++/+++='=+}+=++@$$", +".#@&,&#.*.*%*.*& @&#&#@&#.#.#.~&*.#&#.##&@@.#.@.#.$.$.$.$%$;$)-3))-;;-;-&@@@#@#&#.>.*.#.@(,$#...*#.**.*..@++++!'+}+=+=+@+@@$.", +".&,&#&&>.*..*.#&#@,&#@&#&#&#&#&#.#.##&#&#.#.#&$.#.$.@.$.$$-$;$$%)%-;-;3;)<-)&@&@&,#.~.*>.>#&,+.@**.*.~>.#.>.+++'+++=+=+=++@@@.$.", +"&#&#&,#..*%*.*.,&,@@#@#&##&#.#&#&#.&~#.##&#&#.#$.#.>.#.$.;.$$;-;-)-);)-)<)%3-++@@&@#.*.*..>@,+@@..#*.*~**.>+++_+='++=+=+=++@.$..", +"&,@,&@&>.*.%.>.#&+&#&@&#&#&#&#.#.#.#&#~.#.#.$&#.#..#.$.$.$$.-;--)%)););););-<-&+,@~@*.**>.#&+2+@>*.*#.*.#>&2+++=++=++_+++@@$$...", +"&#&@,#&>.%**.*.&,@,&@#&@@#&#.#&#.~#.#&#&#&#&#.@.#.$.@.@.$.$;-$-;-;-)-;-);-);-)-$+@&#.*..*.>@++++@.#.**.>*$,+/=+=++'=+'+=+@$$.$..", +"@,#&@&#.*..%.>.#&@@,@##&#&##&#.#.#&#&#&#.##.#.$.$.#..#.@$.--;%)$-)%));)<))<)-<)%@+@.#.**.>.#++++#@.>&#.>.@++++=+'+++=+++@$.@..-.", +"@&@,&,&>..**.*.#&,&#&@&@#&#&#.#&>&#&#.#&#.#.#.#.#.>@.@.$%).-$;-;$-;-%)-)%)-;)%)-$+@&.*..*.#@+++++.@...$.#++}=+=+=+=+++++$$$.$...", +",#@,@&#..*...>&#@+&@,@#&#&#.#&#&%#&.#&##.#.#.#.#...#&$.-.-;$;$-;)-))-;-;-;)-;3;3-@@&$.*.*.>&++++/@.-%%$$+++++++=+'+=+++$.$..$.$.", +"@,&#@,&#%.%*.*.&,@,&#&#&#&#&#.#.>&#&#.#&#&#.#.@.#@@..$%$%$.-;$;-;-;-;--)--;)-;);-$(#..*.>.>@++/++--;-<<)/'+[+=+++=++'+@$&$.$%.$.", +"@,@&,&..*.*%..>&,&@@,@#@#&#&#&#%>&#&#.#.#.#.#.#..&@.$%$%]%).-;$;-;$)%$;-%)-;))<)3-@@...*..#@++=@-)%)%)-<{+[++{+=+='}+@:$.$..$.>.", +"@&#@#,&#.%.*.*.&,@,@&&#&@#&#.#&%>&#&#&#&#.#.#.@>&#.$%$.-.$;$;-;$;$;-)--)-;-)%);)))%@..*.*..@++]-]$<);<)-]'='=++++=@++$.$..$..$.$", +"#&,&#&#.**%.%*#&,&&##&#&#&##&&#%.#&#&#&>&#&.#..#&-*$.$.-$-.)$.-$;$-.;-;.-)%;);-;-)).$...*>&&+$)$))--;)%)$+'+=+=++'}'$$$.$.%$....", +"#&@#,~&*...*..*&@,@&&#&##&#.#&.%*&#&#.#.#.#.&#.&.>%.$.$.;-$%$).-$;-$;--);$);-);)-))-$.>..~@.%;;-;));-;3-{+'+++=+=++@@$$..$...$$.", +"@&#&##&*.*5.%*.#,&@,&#&#&##&#&>%#&#~@#&#@.##.@.>..#.$%$-$.).$;$;%$;-.;-%)%$;-;-;);-;%$....%--$3)-;-));-)++=+[+/++/+@$.$$..$%>..>", +"~@#&,&#*.%*.*.*&@,&#&#&#&#&#@&%.#&#&~.#&#.&#.#..>.$.%$.%$.-;$.$$%$.-;$.$;$;$;-;-)$)-%$.$-%-;%;-;-)-;-3%)'+'++=+=++@$$$.%$..$..$.", +"@&###&#.*.5.%.>&,@@&#&#,&#@~&#%..~#&#&#&#$&#..>..#.>$.$.$%$.-;%.-.$%$%)%$;-)%$);%).]%-%%$.-;-);-)%)%;-3-+'=+_++++@$$.$.$..$.>..$", +"~@&#~#&*..%**.*.,(#,&#&##&#&#&%.>.*&#&#.#.#.#.#..#...$.-.$.$..$.%;$.%$.-;$%)$;$$;$;-;$)%-;-$%)-;-;-)-;-]++'+'=+++@.$$.$.>...$.>.", +"#&#&,&#.*..%.*.#@@&@&#&#&##&#&-%..#.#&#&#.#.$..#.$.>.$.$.-;%$%.-..$;.).$%$-.-$;$;$;$;$;-);-;)-;-)%$%)%-{+_=+=+'+@$$..$.%$.>..$.$", +",@&##,~*.5*%*.*&,@,##&,&#&#@&~%.**.~&#&#&#.#.#..>.$.$%$.$.$.$.$.$%$.$%$%$;$:-$-.$%$;-;-;$)-)%)-;-$:$.)-+'+'=+++@@.$.$.$...$.>.>.", +"&#,&#&#.*..%.*.#,&@&#&#&#&#&#&%..*&*#&#&>$.#$.#..$.#.$.-$.-.-..-..$%$.-$;-$$%]$;-;%)%))););$)%)%-.$@;$$++=++=++@$.$.$..$%.$..$.$", +"@&#&#&#..*.*.*.&@,@#&#&,#&#&&#%.*.*.&#.#.#.#&$.#.>.$.$.$.$$.$$$.$$$%$$%$.$;$$.$$;$%$;)%)-);-;-;-.).$$-'+'+='+'@@.$..-.>.$...$...", +"&#@,#~#*.%5%.%*#+,&@,#&#&##&#.%*.*.*~&#.>@#.>..#&$.#$$$$$.$$@@$$$.$$$$:$$-.$;-;-%;)%)$;);-)-;--$;@.@;-++=+=+++@$.$.$..$..>$..$>.", +",&#&#&&*.*.*.*.~@@,&#&#&#&#&,.%%..*..#.#.#.#.>.#.$#..$&@@@@@$$@$-$$]-$$+.{$%-$;-%$;-;-;-)-;--;-.$@:@-$/++_+=++@$.$..$.$.$..$..$.", +"@&#,##~.*.%.%*>&,@@,&#,&#&#&,.*.*.**.&#.#$#$.#.@*@.$>@$$@@@+$)@!]+@@@++{+$;$:-%]%)%$;-);;--;-%$@&+@%-+'+=++++@$$.$.>.%.$.$..$..$", +"&#&&#&#*..5*..*&,@,@&#&#,#&#&&%%%*.*.#.#&#.#.$.#.$#.@$@@@{!@!@!+=$=@'+++@$$-$%-;)-;).)%-%)%$.@&@&$%)+++'+==++.$.$%$.$$.$.%$.$...", +"#&#@~#..*.*%%*.#@+#@#&#&&#&,&~%%5*.~.~&#.$#$&#..#.$#$@$@!@={=!!@[@[@=e+)$;-;);$%;$-;-);$<$&@6+@&]$-(++++=++++$$.$.$%.$..$....$..", +"&#&#&##*.%..*..~#+&#&,###&#&#(%%%...>&@#.#.#.#.@>.@#$@@+@+=+!{=!!=+!++)@-]%$%)%$;)$;$%)%@]&@&@.$.)+++'++=+=+@$.$%.$.$.-.$.$$..$.", +"@#&##&#.**%.%*.#&,@,@&#&,#&#&#.%5....#@>#$&#.$#.$$@$@=!==+=]!_+=@[+{+$$)%$);$;;-$-;---@@@@.:%.-$-++++=+=+++@$.#.$.%.$.$.$.$.$$.$", +"&&#&#~#...5*%*.&,@,@#@#&#&#&&,&%%%%..@.$.,.#@&$@,$@+}@='=/={+=]!_!/!$]%$;).-$;$;)-$;.@{@@$-$..%%+/'++=+'=++@$$.%..$.%..$%$.$.$.$", +"#&#&#&#**.*..**#@,&@,@#@#&##.#&%%%..#$##&#.&$.$$+@+!@=+!={=+={=@@_]$@-;$%$;$;$;$;-.$(+@${@:%.--+++++=+=++++@..$.$%.$.$%$.$.$.$$.", +"#&###&#.%.%*%...,@,#&#&,#&,&#&#&%%%.&..#$#.>@#@@@@=@=+=!+_'+=+='!!{@)$--;$;$;$%;$)%]&@@@$@$$%.(+++++'+_+=+@$.$%..$%.$..$.%$.$..$", +"#&@&#,&**.%.*%*&,@&@,#@&#&#&#&&9%%.>.#@..@.@.@@@@@@=_=+=@_+=@@!+!]@$).;$.-.$;$;-;-%-.@@@@@:-@++++++=@+'+++@@@.$.%$..$..$.$.$.$.$", +"#&##&###..*%.*.~@,+#@&###~#&#.#&.%%.&.,.#&#$@$@+=+@=+=^@[+++=++!@]@)$%).).)$.).-;$;%.+@@@$@.6+/++++++=+=+@$&$%..$.$.-.$..$..$...", +"&#&#&,&*.%.%*%*#&,@&#,@&@&#&#~&#&-%#.#&#&>.@@@}+=+={!==@!+'+'+@+$1$)$%$.$$.-;-%;-%-%$@++=@]$+/+=+{=+=/+++@$$.$.%.%..$.%$.%$..$.$", +"#&@##&#.*%*.....,@,@@&##,#&#&>&#&%%&#&.@.@>@@+!+='+==+![$!+++@@$]$-$;$;$%$.$;$%-;-%%%$++@+$]+++=+++++'+++$@$.$.$.$$%.$.$.$%.$...", +"&#&#&,&#..%.%*>&,@(##@#&#&#&#.#&#->.&#&>&@++!+=+=+=+[=+=@++/@$$$$]$).$..$;$-%-;%-%;%.$@+}{$$+'+=+={+=+++@@$.$..$...$..%.$.$..$.$", +"&#&##&#*.*%*...#&@,@&#@#&##.#~&#..-&#..#$@@++++^+==+^=@!'=/@$$$-$$$-;-.$.$%$%$%-%--%%#@+}@]$@++=++=+++++$@.$.$..$.$.$.$;..;.$...", +"@&#@#,&...%.%*.#&@,@#&#&#&#~$~#.##%#.@.$@++!_=@={+=+=='=+@{@.$;$)&$%$.$.-$%$%-;%)%%%-%@+++@${+={=++=+++@$$.$..:.$.$:$.$.$$.$.$$.", +"&@&#&,#*.*%*%*.&,@@&@#@##&#&#.&#&$@.@.#@+!++@'+=+'=+=+=@'@$:$.$%-:-$.$.%>%$-.-%%-;-%%>@@+/@$++=+={++'++@@$$.$%$.;..$..$..$.$:..$", +"#&#@#&@....%.%>&#(@#@&#&#&#&##*#@&#.#$@@+@_!_=+{=+={==+/+@@$.$;$$$..$%>.$>%$%-;.-%%)%-$+}+@)@++={+=++++$..$;$...$$..$:@.$:$.$$$.", +"@&#&#~##..*%.*.&@@,&#@#@##~#.&#.>++&$.@+=+=@=+=++++='+^+]@-;-..-%$@&&#.>%>-.$%-;-)%%%$#++++-@+@+=+[+++@$@.$..$;..$.$.$.@.$$.@.$$", +"&#@,@,&*.%..%%>~@(@@&#&#&@&##.*.#+2$@@@+!=^!'={+_+=@!@_+@:-.).;-.$.$.,(#.>$%$%%%$;-%.@@++++%@+=+{+=+++@$$.$....$;.$:.$.$.$.$$@.@", +"@&&#&#@#.*%.*..&#@,@#&#&#~#&#&>.$@(@@@+@++=!+=+'@/=/@+'$@)$%$.-&@$$$.$&,&#.>.$;%%$@@&@#@+++]$++=+=+'++.@.$%$.$..$%.$.$.$:$$$:$@$", +"##&#@9&..*%*%%*.,+#&#&#&#&@&>..>.$@++!=^!!/^+^+=1'+=++@:-.;-$%@@@+++++$.#.>.%%-;-$@+++@++++$@@++=+=++@$@.$.$..$..$$.$.$.$.$.@$:$", +"&#&#&#,>...%.*..&,@,&#&&#&#..$..$+@+=@!@^]=@=+'+_!'+0+$.$;-%-+(+,++@@2+#.#*>.%-;@+++++}+++{$@@+++'+++@..$.$.$..$...$.$.$.$$$$$@$", +"&,@,@(&.**%*.%*&,@,@&#,.,&#-.$.@$++^+^_=!@_'+==/^+_++@:.-.-%+@,++@++--(@~.#.>%%$++@@a+++=++$]@=+==+++.@$.$.$.$.$.>$.$.$$.@.:@@.@", +"#&#&#,,...*%.*..@,&##&#&#@+,$$$(+/@=+=!+^1+[+'+='=+'+@$$.%-&@&,&@+@@$-#@#&#..)%6@+@@$7++++@@$@@++=+++$..$.$.$..$..$.$.$.$.@.$:$.", +"@&#@,&@#.%.%*%.~#,&#&@.,+&@--%+++{!'=!=^+!+==+=+=+'+$..$;-&&$$@@&$.--%.$~#*.>%-@.$#$$,@+++!{$@@+=+'+@:$.$.$.$.$.$..$.$.@$:$.@..;", +"#@#&@,&#.*5.%*..&@#&##.+#$$$@++@@'}'={=!!!'/'=+'=+++@-$%-&@...*$#$@&@$-.#.#.$%;$.$.@,+@}++@@|{++++++@$.$.$..$....$.@.$.@.@.;.%%%", +"#&@,@,&#.*%*%.*.,,&#&#$#$$+@=+'+1$!==^={$1+=++=++/+$]$.-&&*#>.$.$.$.-.#.>.#*$-%-$.##@#@++++@$@+=+=+@.@...$.$.$.$.$.$.@.$.$.$%;.%", +",@#&,+,.>.%*%.*.&#&#.@&$@!+!+==@!{^'==!!!!{+='/='+@@$--#&#...>.>.$.$.$%#*#.*$%-.$.$.#$,@+++@$@@++++@$.-$..$.$.$....$.:@&$%%%%%.%", +"@&+@,&+...%.*%.#,#,@##$#$$|={={^{!+_=]={@$!+++=+++@$$%$&@.#*>.>%$%%$%$..>.#.>%--.*>##@@+}+{+|$+++++@....$..$...$.$.@.@.;%.;%.;5%", +"@,&,@+,#.*%*%.*.&,&#.$@$+=+='=!+!{[='==!^/@!'++/+@]$--(,#.~.*..>.>.%$%>.*#*#$%-%$...>##+++++$]+++++$@.$..$.$$.$.@:$...$.-%.%%.;%", +"@,@+,&+...*%*%.~#&,@@#$+!@^=!'!1'_+==+=+++]@+=+++$$$)++&#&>~>.>%%%$%..$.*.#..---..>.#$#+++++-$++++@.$...$...$.@...@.@.%;%%;%%%%%", +"@+@@,+,@>%..%*..,#&#&@@$!{=!==!{=='+[+{=++$@@'/+$&--@+/@~#*~.>.$>%.%>.>.>*#~$%%-.>.#.#@,++++$$++++@$.$..$.$.$.:$.$.:..%%.%;.;%;%", +",@(+,+&#..*%.%*&,@#@@$$+@!]={^@!{+=='_++++$@$@+/@$-)+/@@#.~**.>..>%$%..*#.~.#$-%...>$#++7+++1$++++@.$.%..$.@.$.@.$&$.;%%4%%%%;%%", +"+,@@+,+&>.%*.*..&@&#$$+!@^===!1]|'+=+}{@+@$$+++]@%$+++/&###~#..>...>.>..#*#..%--.>..>@,+++++$$++++$.$.$...$.$.:$.@...%.%.%4;%%;-", +"@@+,+,+..*.%*%*#,,@@@$@+!{={1{=$=/'+=+@+]@+@/'/$@%@'++@@~*~**>.$*>%..$*.~.#*@.-<..$#.@+++!+/$$@++@$....$...$.$.@.@.$%%454%%%;-%)", +",@+@,++#$%*%.%..,@.#@@@=@!==!=^][+='/=@$@.++++$@:$+/+/@+@#*#*.*$..>.>..##*.#..--...>@,}++@+/$$$+(@@$.$..$.$.$.$.$@..%%%%4%;%;%)%", +"@,@++(,....*%*..#,@@@$+@!{=@^+!!{|+={++=+@+/+@;$-@+'++@,#&*#.#.>*>.%>*#.*#.#.$%-.>.@$+++=++++$$/+.$.$.$..$.$:$...@.%%.4%%%;%%;%;", +",@,@,@+.>.%..%*&&&,@@@@!@=_!='!!]1+={}'+!++++$-$]+/+++@@&#*~*>.>.>.>.$*.#&#&#&>.*..#@+=++++++$$+&$..$.$.$.$.@.$.$..;%%%4;%%;%-%%", +"@,@,+&@...%*%*.>#+#@$@+!+=]=!=]!|!'+!@/++/@@@$6-++=+/+#@~#.#*~>*.#*.*.>.*..#.$%$.>$@+}+=@++++-@+@.$.>.$..@..$:@.@..%%4%%%;%;%;;%", +"@+@@+,@.$*.%.%..&+&#@$+{!@=_{=!!{!!'@^++!@$$@@$+'+++++#@$~#*#.#.>.>>.>.#.>.%%-%$..#@++++!+@+($$@#.$.$..$.$.$..$..$.%%%;%;%%;-;-;", +",@,+@+#..%.*%*.##,@@$@!+!{==={={=1@={@/+]$$$@@@+++=@++@&#.~*~**.#...*..>..>-.%>.>$.++=++@+++/$.@...$.$..$..@.$.@..%%.%5%;%;-%<%;", +"++@+,+@.>.%.*%..&@@#@$+@!+!'==!!^!1+@'+@@$-@@]+++@++++#+##.#.#*.*.>.>.*.>..%$%%$.$@/+=+7@@++($@@$.$...$.@.:$.:.@..%%%;%<%%<;%-;%", +"+,@+@,&$..*%.*.#&,@@@!+!+[=@^@[+^={|++.)$-+@$++{+=+!+++&#.~***#.#*.~.#..*$*>%$.$#@+++++=+@,++@:....$.$..$.$.@.$.@.%%%5;%;;%;%;-<", +"++,++@,.>%.%%%.#&@#@$@=+!'===!{=^!!{@)@-%@@@+/+@}++++++@##.#~.**.#&#.~#.#&.#.,&@>$++++++}@++(@@@.$....$...$..@....%%%%%%%%;-<%;%", +"+++@+@&$..*%*%*.@$,@@@@=+={^'|!{!!{+@$&3$&]$++=+++=+=+,+&#~.#*#.*>*~#.~~>&#.>$.$$}+}'+=+@@+@+&$.$..$..$.@.$:$.]@&%%;%4%;%;%<-;%<", +"@++++@&>..%*.%.#&$+$@@'=+_=@=]=!^!={]$$;@$$++'/=+++@+++@@&#~.~.#.#.*#*#.*>%-*#$>@+++++++}@2@+&$.$..$..$:.$..$..$&%%%%%<%<%;%%;%%", +"++7+,+@..>.%%*..#+@@d+@='='=^@[@^^@!$]$$@-+++++++=++=+@,,@~@~#*~.*>.~.#*#.>.>.>@+_+=+++@+@+@2+$..$..$..$.$:@:$@:@;%;8%;%;%-;%;%;", +"=++++@&#.%.*%>..,@$@@+!+=$[@b{!{=^!]@$$$-+@+}{=+++++@++@,@#&#.#.#.#*#*#.#~##$#$,+=++{++@7@}@++.$..$..$.$.$.$.$.@.%%%%;%<%;%)%%%%", +"+++++#@..*%.%*..@,$@@+!'_=='|^@1!+1@@{$-@++=++++++=+{+@++&#&#&>.#*.>..#.>..>.>@@/=+++++@+@+@++.$...$..$.$.$@:$@:@;%<%%<%%<%%;%;%", +"+=++@@@#..*%.%>.@+@@+@@='@[=$^!]!^+1$+]$+++++=+=++++=+++++,#&##.#.#.>*.>.>$%$.++=+[+{++@+#+#++$.$..$.$.$:$.$$$.@&;<%;%;;%-<%%%%%", +"+++++@..>.%*%..#@,@@=+^+=^+^]='1|]=]$@@+=+=++'+++++=+'+++&+,,&#.#....>.>%$.>$#(++=+++++@@+7@@(-.$.$..@.$.$@:@$@].-%;<%<%;%%)%;%%", +"+=}++@@...%.*%*.@+@+++!+'='_!]={!!]!!{!+'=+=+='@+++++=@+++(+(,#&#.#>..%.>%$.$@+++'={'++@+@+@@+.$..$:$.@$.$.$.@:@:%<%;%%<%;%%%%..", +"+++++#@#.*.%*%.#+@$@=@==+='=]|={!^|{|$1@_+'}{@}@_+'+=+++@+#+,+,(#&&#.#>.%$.$.@++=++++++@!@+#++%$&@.@.$.]$$@$]@$&.;%%4%;%%%-$$+++", +"++++++&$>.>..*..@-+$+'@={=+{!]=={!^!]!{)!'=++=+@+=+++=+=++++&#.#.#.>...$*.$%$.@+'='@{=+@+!+@@($.@.@&$@.@.@.$.@]@%%<%%%%;%;.(++++", +"++++}@,&$%*%*$*.-++===+=+=+_{|]=[!$^@^!+]@'=@++=+++=+'+@++,@#@#$#.#.>.*.$..$%$;-@+'++++!@@@+@($.&@.@.@@@.]@@.@&$;%%4%8%%%.@++++@", +"@@+++++#.>.%.%>.+@]++_{='!{+[@^@|]^!^@^|]!++='+@=+'=++=++++@@#.#.#.#.#..>.$.-%----]/'++++7@+@+.$.$&$&@&@@&@@@@@&%;%%%%%;$&(++@@@", +"@!@@+@+&..**%*..@++='+=@_+^@='|^!!{^@1@^$+1+@+=+++++=++=@++,@#.#.#.>..>...%.-.-;-)--+++@+@@+,@.@.$.@.$@.@@&@&$&@%;%;%;%.&+(@,+@,", +"$]@@@++,.$%..%>.$@++_+|{=]+^+={={==!{!]!{={{=++=+=+'=++++@+@&#$##.#.#.~.>.$.%%$%%)%3;@'+++@++@$.@.$.$.@.@@@&@&@&%4%%%%$(+(@&@,@@", +"@$$$@@2@.*.*%*..$@+=+_+=@_@'!{=$={'!^$!]=$_$+'/@=+=++'=++@+,@.#..#.*.>..*.%%$%.;$%;-%-$+{+@++.$.$.@.@.@$&$@&$.@&%%<;%%@(@@@+&@&@", +"{$]$@@+@$%.%*.>.@)/'={=+='=+=+[{==!{!{1@1+)=+=+{+'+=@+++@@++#.##.#.#..#..>...$%%;%-;%<)@++@++$$.$.:$.$.$@.@@&$&%;%%%;.@(@&#@#@,@", +"+@|$$@(,..>%*%..@@+/=+='=+{='=+={[+=!!]|]!+{$+/=+=++=++@@@#(@#&$*.#.*#.*..%.%.%-%;%;-%--{++++$.$.$@.@$@.$#.@.&$%%%;%%@(@@,@.@&&&", +"'@]$$@++...%.%*.$@+=/!+=+=@=+_{==@^{+^!@|{=|)=++{+=+{+}+@@@(,.#.#.*>.*.>.>..-..%$%-;%<;--+@++.@$..:$..$.@.@.#..%%%%%.&,@&@#@,@#@", +"++]$$$+&.*%*%*.$$+'=+[='_'$'='_@^{!!!{!]|'=@-+/!+=++=+++#@#2@.#.#.#.~>.*..>..%$%;%;%-;-%)++++$..$..@$@.@$.@&..%%%<%%$&@&#@@&@&&@", +"''$]$@++$%.%.%*.$++'=+=+=@!'+=+='=+{!'!1!@={|'+@{=+++++@@@&2&#.#*~.*..#.>.%$..%.%$;%%;-<-)++($.@.$...$@.@.@.$%%%%%%.&&,@&##@#@@@", +"++!$$$(+..*%*...@+_@_+[+[@@_{='=+[={!=@^$^@!{+_+++=++++#&#@(#.#..#*#.*.*.>..%$.-.%-;-%)-;3/0@@...@.$@.$@.@..%%%;%%%$&#&#@&@,@@#&", +"''])$@(+..%.%*..@'+'+[+=@[++=+=+='+=]=!]|]!@^++=++@++++@#.#,~.*#*..#*#.#..>.$%.%$;%-%;-<--@+@.$..$...@.@$&@%<%%%%%%&&&#&#@,@&@@@", +"/+@]$$+($.%*%%*.@@==/='=+]='+'='=+^='!{|=]|$@{++=+++},@#.@(&##..~**..#*.>.>.>.$%.-;%)%--;-)(]..@..@$.$.@.@:%;%4%%%$&#&&#.&#@@@+@", +"''!)$]+,..%..*..$++'+=+[+!@_!+_+='!+=@!1$_'[+=]$@=+++@@#.>,&&*#.#.~*#.*#.>.$.%$.-.-;-;-;-<$+&]..$..@.@:@.@.%%%%<%*.&&#&#@,&@,@@@", +"++{$$$@($.*%*%.>@'+=+'=+'='+'=+'=+^'^^@^@1+=+++@+@@++&#@.#(##.#*.*$*.#.*#*.*$..%.%%%-;-;-;-(@.$..@.$:$$.@...%%8%%.#.&&#&@@#@@@,@", +"_/!]$]@+.>%.%*..$++=++{=++=+=+=+{=+^+^]1+^@'/={++++2+#&#@#+&#.*.#.~.**#*.>.*..$..$;%%-<-%)-:$.@.$..$@.@:@...%.%%%.~#&#&@,@@@+@@@", +"+'+$]$+&&.*%*%.>$@+'+=+'={={+=+=+[+^='!^+^@|'++=++++@&#..#&#.#*.*.#*#.*.#*.>.%.%.%.-;%);-<--:@.@&$.$$].$&.....%%%.&#&@,&@,@#@@+@", +"_+{$$]@+$..%.*..$@}'++='+=+='='+!'_$!'={[@^@=++/++++#@##@2&#.#*#.*.*.~>~.*..>..$..%$%-%-)%-<@&@&@&@.@$@.$....4%%.#&,&@,@&@@+@@@+", +"+/=]$$&&&>%*.%..@@+=+'}=+{=++=+_=@!{!={=={!+'_{+=+++#&$*@(#.#.>.*#.#.*.#.#*...%.%.%.;-;-;-;-.@&+(@@@@@@$...&.%%%.@&@,&@@@@@&@@++", +"+'+]$)@+...*%*..@++'=+'='=+[+='+'_+^$|{!]='=++=+/++@#.##2&#.#.*#*.*.*#.**.*.>*.$.$%$%;-%<--<$6@&@&+({(@+&$..%%%..,+&@,@@,@@@@@++", +"+++$$).(..%*%.%.@=+++=++='={+=+==+=={|!^@=+^/=+/+++#@#&,(#.#.*#.*#.*#*.#.#*...*.%..%$%;-;-%--&@@@@@++++(+%%.%%%.@(@+@&@@@+@@@+++", +"{@{]$]$+&..*%*.&$@_'+'='+=+='=++='=+_{$^!{='=/+=++@#$#&2#.#.>.~*.*.*.#*#*.#.>.>..$%.%-%-;3;-%{&@&@@@@++(,%>.%<.&++,@+@@(@@@@@+@+", +"@@@$$;$&@%%.%%.&$+++=+=+='=@=+[/=+='=!_@!{_++[+++@#@~,(,.#.#.*.~*#.**..*.*.*...*..$%.;-;-%-;-$@&@@&@@++(+.%-.>&(+@+@@@@+@@@@@@++", +"+{@]$]$&&.*.*%%&@=+=+'='+='='=@='!+=+^@^+='=+/+++@@#(2,~#.#.#*#.*.~.#*~.#*#.#*.$*.$%$.%-;)%-<-6@@@@6@@+@&>%%$&+(+@+@@+@+@@@@{+@+", +"@@@$$.]&@.%.%...@++'=+=+=+=++={}+!'!'=@1+=++++}+@(,(#~..~#.*.**~*.**.*>.*.~.*.>..>.%.-;%-;-%--+@&@@@@&@&#.>.&,&+@+@@@@+@@&@@++++", +"+@{@)$$&..*%*%..{++=+{='='=^+!@='!+=]={!{='=++++,(#&&#~#.*##.#*.#.#*.*.~.#*.#.**...$%.$;%)%);-:@@@&@@@&@&..,@(@&+@@@@++@@@@@@+++", +"+'@@$)$&@..%%%.#++_+=+=+!+=@[+=@={!_+={!@++++/+,@&.#*.*~#..~.#****.~.#*.*..*.*#.>>.%$%%$%)%----+6@@@&@@.##&~@#@+@@@+@+@@]@@++{++", +"+++{$-)$..%*.%.@++++'+{='='@={=$=+!'^$_+^+_+++@$&#&~&~*.~#*#*.~.~.*.*.#*.#*#.*.*..>..$;%;-;;-;-+@&@@&$&#&&#&#&@@@@+@+@@.@@@+++++", +"++'@)-.$&$.%%%%&+'=+==+=+=={=@={@[@='={!$@+++,@,.&.#*&~~.*&.#*#*#*~.*.*.*..*>.*>.>.%.%$;%)%--;-@&@&@@.@.#&#.#&@@+@+@@@@@@@++++!@", +"++++-))$...>.%.@++_++{={'=@_+=+={=]+_@[/^@+@+@#@#.~.~*.~*#*#*.~.*.*.**.*.#*.*#.*.>.$..$;$%);--)$6@&@&#.@*&#.#@,@+@@+@@$@@@++++@@"}; diff --git a/Images/transparent.png b/Images/transparent.png new file mode 100644 index 000000000..902ea8272 Binary files /dev/null and b/Images/transparent.png differ diff --git a/Images/transparent.webp b/Images/transparent.webp new file mode 100644 index 000000000..c1e38022e Binary files /dev/null and b/Images/transparent.webp differ diff --git a/MANIFEST.in b/MANIFEST.in index aaed01cdd..3769b645e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,57 @@ -exclude .hgignore -exclude .hgtags -exclude BUILDME.bat -exclude make-manifest.py -exclude SHIP -exclude SHIP.bat -recursive-exclude Tests * +include *.c +include *.h +include *.py +include *.rst +include .gitattributes +include .travis.yml +include Makefile +include tox.ini +recursive-include Images *.bdf +recursive-include Images *.fli +recursive-include Images *.gif +recursive-include Images *.ico +recursive-include Images *.jpg +recursive-include Images *.pbm +recursive-include Images *.pil +recursive-include Images *.png +recursive-include Images *.ppm +recursive-include Images *.psd +recursive-include Images *.tar +recursive-include Images *.webp +recursive-include Images *.xpm +recursive-include Sane *.c +recursive-include Sane *.py +recursive-include Sane *.txt +recursive-include Sane CHANGES +recursive-include Sane README +recursive-include Scripts *.py +recursive-include Scripts README +recursive-include Tests *.bin +recursive-include Tests *.eps +recursive-include Tests *.gnuplot +recursive-include Tests *.icm +recursive-include Tests *.jpg +recursive-include Tests *.pcf +recursive-include Tests *.pcx +recursive-include Tests *.png +recursive-include Tests *.ppm +recursive-include Tests *.py +recursive-include Tests *.tif +recursive-include Tests *.tiff +recursive-include Tests *.ttf +recursive-include Tests *.txt +recursive-include Tk *.c +recursive-include Tk *.txt +recursive-include docs *.bat +recursive-include docs *.gitignore +recursive-include docs *.html +recursive-include docs *.py +recursive-include docs *.rst +recursive-include docs *.txt +recursive-include docs Guardfile +recursive-include docs Makefile +recursive-include docs BUILDME +recursive-include docs COPYING +recursive-include docs LICENSE +recursive-include libImaging *.c +recursive-include libImaging *.h diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..dca36d4ed --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +pre: + bin/python setup.py develop + bin/python selftest.py + bin/python Tests/run.py + check-manifest + pyroma . + viewdoc diff --git a/PIL.egg-info/PKG-INFO b/PIL.egg-info/PKG-INFO deleted file mode 100644 index c27993fb2..000000000 --- a/PIL.egg-info/PKG-INFO +++ /dev/null @@ -1,18 +0,0 @@ -Metadata-Version: 1.0 -Name: PIL -Version: 1.1.7 -Summary: Python Imaging Library -Home-page: http://www.pythonware.com/products/pil -Author: Secret Labs AB (PythonWare) -Author-email: info@pythonware.com -License: Python (MIT style) -Download-URL: http://effbot.org/downloads/PIL-1.1.7.tar.gz -Description: Python Imaging Library -Platform: Python 1.5.2 and later. -Classifier: Development Status :: 6 - Mature -Classifier: Topic :: Multimedia :: Graphics -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture -Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion -Classifier: Topic :: Multimedia :: Graphics :: Viewers diff --git a/PIL.egg-info/SOURCES.txt b/PIL.egg-info/SOURCES.txt deleted file mode 100644 index 275ff0f19..000000000 --- a/PIL.egg-info/SOURCES.txt +++ /dev/null @@ -1,283 +0,0 @@ -BUILDME -CHANGES -CONTENTS -MANIFEST.in -PIL.pth -README -_imaging.c -_imagingcms.c -_imagingft.c -_imagingmath.c -_imagingtk.c -decode.c -display.c -doctest.py -encode.c -map.c -outline.c -path.c -selftest.py -setup.py -Docs/effbot.css -Docs/index.html -Docs/pythondoc-PIL.ArgImagePlugin.html -Docs/pythondoc-PIL.BdfFontFile.html -Docs/pythondoc-PIL.BmpImagePlugin.html -Docs/pythondoc-PIL.BufrStubImagePlugin.html -Docs/pythondoc-PIL.ContainerIO.html -Docs/pythondoc-PIL.CurImagePlugin.html -Docs/pythondoc-PIL.DcxImagePlugin.html -Docs/pythondoc-PIL.EpsImagePlugin.html -Docs/pythondoc-PIL.ExifTags.html -Docs/pythondoc-PIL.FitsStubImagePlugin.html -Docs/pythondoc-PIL.FliImagePlugin.html -Docs/pythondoc-PIL.FontFile.html -Docs/pythondoc-PIL.FpxImagePlugin.html -Docs/pythondoc-PIL.GbrImagePlugin.html -Docs/pythondoc-PIL.GdImageFile.html -Docs/pythondoc-PIL.GifImagePlugin.html -Docs/pythondoc-PIL.GimpGradientFile.html -Docs/pythondoc-PIL.GimpPaletteFile.html -Docs/pythondoc-PIL.GribStubImagePlugin.html -Docs/pythondoc-PIL.Hdf5StubImagePlugin.html -Docs/pythondoc-PIL.IcnsImagePlugin.html -Docs/pythondoc-PIL.IcoImagePlugin.html -Docs/pythondoc-PIL.ImImagePlugin.html -Docs/pythondoc-PIL.Image.html -Docs/pythondoc-PIL.ImageChops.html -Docs/pythondoc-PIL.ImageColor.html -Docs/pythondoc-PIL.ImageDraw.html -Docs/pythondoc-PIL.ImageEnhance.html -Docs/pythondoc-PIL.ImageFile.html -Docs/pythondoc-PIL.ImageFileIO.html -Docs/pythondoc-PIL.ImageFilter.html -Docs/pythondoc-PIL.ImageFont.html -Docs/pythondoc-PIL.ImageGL.html -Docs/pythondoc-PIL.ImageGrab.html -Docs/pythondoc-PIL.ImageOps.html -Docs/pythondoc-PIL.ImagePalette.html -Docs/pythondoc-PIL.ImagePath.html -Docs/pythondoc-PIL.ImageSequence.html -Docs/pythondoc-PIL.ImageStat.html -Docs/pythondoc-PIL.ImageTk.html -Docs/pythondoc-PIL.ImageTransform.html -Docs/pythondoc-PIL.ImageWin.html -Docs/pythondoc-PIL.ImtImagePlugin.html -Docs/pythondoc-PIL.IptcImagePlugin.html -Docs/pythondoc-PIL.JpegImagePlugin.html -Docs/pythondoc-PIL.McIdasImagePlugin.html -Docs/pythondoc-PIL.MicImagePlugin.html -Docs/pythondoc-PIL.MpegImagePlugin.html -Docs/pythondoc-PIL.MspImagePlugin.html -Docs/pythondoc-PIL.OleFileIO.html -Docs/pythondoc-PIL.PSDraw.html -Docs/pythondoc-PIL.PaletteFile.html -Docs/pythondoc-PIL.PalmImagePlugin.html -Docs/pythondoc-PIL.PcdImagePlugin.html -Docs/pythondoc-PIL.PcfFontFile.html -Docs/pythondoc-PIL.PcxImagePlugin.html -Docs/pythondoc-PIL.PdfImagePlugin.html -Docs/pythondoc-PIL.PixarImagePlugin.html -Docs/pythondoc-PIL.PngImagePlugin.html -Docs/pythondoc-PIL.PpmImagePlugin.html -Docs/pythondoc-PIL.PsdImagePlugin.html -Docs/pythondoc-PIL.SgiImagePlugin.html -Docs/pythondoc-PIL.SpiderImagePlugin.html -Docs/pythondoc-PIL.SunImagePlugin.html -Docs/pythondoc-PIL.TarIO.html -Docs/pythondoc-PIL.TgaImagePlugin.html -Docs/pythondoc-PIL.TiffImagePlugin.html -Docs/pythondoc-PIL.TiffTags.html -Docs/pythondoc-PIL.WalImageFile.html -Docs/pythondoc-PIL.WmfImagePlugin.html -Docs/pythondoc-PIL.XVThumbImagePlugin.html -Docs/pythondoc-PIL.XbmImagePlugin.html -Docs/pythondoc-PIL.XpmImagePlugin.html -Images/courB08.bdf -Images/courB08.pbm -Images/courB08.pil -Images/lena.gif -Images/lena.jpg -Images/lena.png -Images/lena.ppm -PIL/ArgImagePlugin.py -PIL/BdfFontFile.py -PIL/BmpImagePlugin.py -PIL/BufrStubImagePlugin.py -PIL/ContainerIO.py -PIL/CurImagePlugin.py -PIL/DcxImagePlugin.py -PIL/EpsImagePlugin.py -PIL/ExifTags.py -PIL/FitsStubImagePlugin.py -PIL/FliImagePlugin.py -PIL/FontFile.py -PIL/FpxImagePlugin.py -PIL/GbrImagePlugin.py -PIL/GdImageFile.py -PIL/GifImagePlugin.py -PIL/GimpGradientFile.py -PIL/GimpPaletteFile.py -PIL/GribStubImagePlugin.py -PIL/Hdf5StubImagePlugin.py -PIL/IcnsImagePlugin.py -PIL/IcoImagePlugin.py -PIL/ImImagePlugin.py -PIL/Image.py -PIL/ImageChops.py -PIL/ImageCms.py -PIL/ImageColor.py -PIL/ImageDraw.py -PIL/ImageDraw2.py -PIL/ImageEnhance.py -PIL/ImageFile.py -PIL/ImageFileIO.py -PIL/ImageFilter.py -PIL/ImageFont.py -PIL/ImageGL.py -PIL/ImageGrab.py -PIL/ImageMath.py -PIL/ImageMode.py -PIL/ImageOps.py -PIL/ImagePalette.py -PIL/ImagePath.py -PIL/ImageQt.py -PIL/ImageSequence.py -PIL/ImageShow.py -PIL/ImageStat.py -PIL/ImageTk.py -PIL/ImageTransform.py -PIL/ImageWin.py -PIL/ImtImagePlugin.py -PIL/IptcImagePlugin.py -PIL/JpegImagePlugin.py -PIL/McIdasImagePlugin.py -PIL/MicImagePlugin.py -PIL/MpegImagePlugin.py -PIL/MspImagePlugin.py -PIL/OleFileIO.py -PIL/PSDraw.py -PIL/PaletteFile.py -PIL/PalmImagePlugin.py -PIL/PcdImagePlugin.py -PIL/PcfFontFile.py -PIL/PcxImagePlugin.py -PIL/PdfImagePlugin.py -PIL/PixarImagePlugin.py -PIL/PngImagePlugin.py -PIL/PpmImagePlugin.py -PIL/PsdImagePlugin.py -PIL/SgiImagePlugin.py -PIL/SpiderImagePlugin.py -PIL/SunImagePlugin.py -PIL/TarIO.py -PIL/TgaImagePlugin.py -PIL/TiffImagePlugin.py -PIL/TiffTags.py -PIL/WalImageFile.py -PIL/WmfImagePlugin.py -PIL/XVThumbImagePlugin.py -PIL/XbmImagePlugin.py -PIL/XpmImagePlugin.py -PIL/__init__.py -PIL.egg-info/PKG-INFO -PIL.egg-info/SOURCES.txt -PIL.egg-info/dependency_links.txt -PIL.egg-info/top_level.txt -Sane/CHANGES -Sane/README -Sane/_sane.c -Sane/demo_numarray.py -Sane/demo_pil.py -Sane/sane.py -Sane/sanedoc.txt -Sane/setup.py -Scripts/README -Scripts/enhancer.py -Scripts/explode.py -Scripts/gifmaker.py -Scripts/painter.py -Scripts/pilconvert.py -Scripts/pildriver.py -Scripts/pilfile.py -Scripts/pilfont.py -Scripts/pilprint.py -Scripts/player.py -Scripts/thresholder.py -Scripts/viewer.py -Tk/booster.txt -Tk/install.txt -Tk/pilbitmap.txt -Tk/tkImaging.c -libImaging/Access.c -libImaging/Antialias.c -libImaging/Bands.c -libImaging/Bit.h -libImaging/BitDecode.c -libImaging/Blend.c -libImaging/Chops.c -libImaging/Convert.c -libImaging/ConvertYCbCr.c -libImaging/Copy.c -libImaging/Crc32.c -libImaging/Crop.c -libImaging/Dib.c -libImaging/Draw.c -libImaging/Effects.c -libImaging/EpsEncode.c -libImaging/Except.c -libImaging/File.c -libImaging/Fill.c -libImaging/Filter.c -libImaging/FliDecode.c -libImaging/Geometry.c -libImaging/GetBBox.c -libImaging/Gif.h -libImaging/GifDecode.c -libImaging/GifEncode.c -libImaging/HexDecode.c -libImaging/Histo.c -libImaging/ImDib.h -libImaging/ImPlatform.h -libImaging/Imaging.h -libImaging/Jpeg.h -libImaging/JpegDecode.c -libImaging/JpegEncode.c -libImaging/Lzw.h -libImaging/LzwDecode.c -libImaging/Matrix.c -libImaging/ModeFilter.c -libImaging/MspDecode.c -libImaging/Negative.c -libImaging/Offset.c -libImaging/Pack.c -libImaging/PackDecode.c -libImaging/Palette.c -libImaging/Paste.c -libImaging/PcdDecode.c -libImaging/PcxDecode.c -libImaging/PcxEncode.c -libImaging/Point.c -libImaging/Quant.c -libImaging/Quant.h -libImaging/QuantDefines.h -libImaging/QuantHash.c -libImaging/QuantHash.h -libImaging/QuantHeap.c -libImaging/QuantHeap.h -libImaging/QuantTypes.h -libImaging/RankFilter.c -libImaging/Raw.h -libImaging/RawDecode.c -libImaging/RawEncode.c -libImaging/Storage.c -libImaging/SunRleDecode.c -libImaging/TgaRleDecode.c -libImaging/Unpack.c -libImaging/UnpackYCC.c -libImaging/UnsharpMask.c -libImaging/XbmDecode.c -libImaging/XbmEncode.c -libImaging/Zip.h -libImaging/ZipDecode.c -libImaging/ZipEncode.c \ No newline at end of file diff --git a/PIL.egg-info/dependency_links.txt b/PIL.egg-info/dependency_links.txt deleted file mode 100644 index 8b1378917..000000000 --- a/PIL.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/PIL.egg-info/top_level.txt b/PIL.egg-info/top_level.txt deleted file mode 100644 index 8eb445d00..000000000 --- a/PIL.egg-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -PIL -_imaging diff --git a/PIL.pth b/PIL.pth deleted file mode 100644 index b338169ce..000000000 --- a/PIL.pth +++ /dev/null @@ -1 +0,0 @@ -PIL diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py index 815fcea33..df5ff2625 100644 --- a/PIL/ArgImagePlugin.py +++ b/PIL/ArgImagePlugin.py @@ -18,13 +18,15 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.4" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette -from PngImagePlugin import i16, i32, ChunkStream, _MODES +from PIL.PngImagePlugin import i8, i16, i32, ChunkStream, _MODES -MAGIC = "\212ARG\r\n\032\n" +MAGIC = b"\212ARG\r\n\032\n" # -------------------------------------------------------------------- # ARG parser @@ -60,18 +62,18 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AHDR chunk" + raise SyntaxError("misplaced AHDR chunk") s = self.fp.read(bytes) self.size = i32(s), i32(s[4:]) try: - self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.mode, self.rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: - raise SyntaxError, "unknown ARG mode" + raise SyntaxError("unknown ARG mode") if Image.DEBUG: - print "AHDR size", self.size - print "AHDR mode", self.mode, self.rawmode + print("AHDR size", self.size) + print("AHDR mode", self.mode, self.rawmode) return s @@ -80,7 +82,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced AFRM chunk" + raise SyntaxError("misplaced AFRM chunk") self.show = 1 self.id = 0 @@ -98,7 +100,7 @@ class ArgStream(ChunkStream): self.repair = None if Image.DEBUG: - print "AFRM", self.id, self.count + print("AFRM", self.id, self.count) return s @@ -107,7 +109,7 @@ class ArgStream(ChunkStream): # assertions if self.count != 0: - raise SyntaxError, "misplaced ADEF chunk" + raise SyntaxError("misplaced ADEF chunk") self.show = 0 self.id = 0 @@ -121,7 +123,7 @@ class ArgStream(ChunkStream): self.count = i16(s[2:4]) if Image.DEBUG: - print "ADEF", self.id, self.count + print("ADEF", self.id, self.count) return s @@ -130,7 +132,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced NAME chunk" + raise SyntaxError("misplaced NAME chunk") name = self.fp.read(bytes) self.names[self.id] = name @@ -141,26 +143,26 @@ class ArgStream(ChunkStream): "AEND -- end of animation" if Image.DEBUG: - print "AEND" + print("AEND") self.eof = 1 - raise EOFError, "end of ARG file" + raise EOFError("end of ARG file") def __getmodesize(self, s, full=1): size = i32(s), i32(s[4:]) try: - mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))] + mode, rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: - raise SyntaxError, "unknown image mode" + raise SyntaxError("unknown image mode") if full: - if ord(s[12]): + if i8(s[12]): pass # interlace not yet supported - if ord(s[11]): - raise SyntaxError, "unknown filter category" + if i8(s[11]): + raise SyntaxError("unknown filter category") return size, mode, rawmode @@ -169,7 +171,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced PAST chunk" + raise SyntaxError("misplaced PAST chunk") if self.repair is not None: # we must repair the target image before we @@ -206,7 +208,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced BLNK chunk" + raise SyntaxError("misplaced BLNK chunk") s = self.fp.read(bytes) size, mode, rawmode = self.__getmodesize(s, 0) @@ -223,7 +225,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced IHDR chunk" + raise SyntaxError("misplaced IHDR chunk") # image header s = self.fp.read(bytes) @@ -234,7 +236,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -243,20 +245,20 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced DHDR chunk" + raise SyntaxError("misplaced DHDR chunk") s = self.fp.read(bytes) size, mode, rawmode = self.__getmodesize(s) # delta header - diff = ord(s[13]) + diff = i8(s[13]) offs = i32(s[14:18]), i32(s[18:22]) bbox = offs + (offs[0]+size[0], offs[1]+size[1]) if Image.DEBUG: - print "DHDR", diff, bbox + print("DHDR", diff, bbox) # FIXME: decode and apply image self.action = ("DHDR", diff, bbox) @@ -267,7 +269,7 @@ class ArgStream(ChunkStream): self.decoder = Image.core.zip_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -276,7 +278,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced JHDR chunk" + raise SyntaxError("misplaced JHDR chunk") # image header s = self.fp.read(bytes) @@ -287,7 +289,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.jpeg_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -296,7 +298,7 @@ class ArgStream(ChunkStream): # assertions if self.count == 0: - raise SyntaxError, "misplaced UHDR chunk" + raise SyntaxError("misplaced UHDR chunk") # image header s = self.fp.read(bytes) @@ -307,7 +309,7 @@ class ArgStream(ChunkStream): self.im = Image.core.new(mode, size) self.decoder = Image.core.raw_decoder(rawmode) self.decoder.setimage(self.im, (0,0) + size) - self.data = "" + self.data = b"" return s @@ -321,7 +323,7 @@ class ArgStream(ChunkStream): if n < 0: # end of image if e < 0: - raise IOError, "decoder error %d" % e + raise IOError("decoder error %d" % e) else: self.data = self.data[n:] @@ -386,7 +388,7 @@ class ArgStream(ChunkStream): "SYNC -- reset decoder" if self.count != 0: - raise SyntaxError, "misplaced sYNC chunk" + raise SyntaxError("misplaced sYNC chunk") s = self.fp.read(bytes) self.__reset() @@ -418,7 +420,7 @@ class ArgImageFile(ImageFile.ImageFile): ) if self.fp.read(8) != MAGIC: - raise SyntaxError, "not an ARG file" + raise SyntaxError("not an ARG file") self.arg = ArgStream(self.fp) @@ -427,7 +429,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if cid != "AHDR": - raise SyntaxError, "expected an AHDR chunk" + raise SyntaxError("expected an AHDR chunk") s = self.arg.call(cid, offset, bytes) @@ -452,11 +454,11 @@ class ArgImageFile(ImageFile.ImageFile): def seek(self, frame): if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") self.fp = self.arg.fp - while 1: + while True: # # process chunks @@ -464,7 +466,7 @@ class ArgImageFile(ImageFile.ImageFile): cid, offset, bytes = self.arg.read() if self.arg.eof: - raise EOFError, "end of animation" + raise EOFError("end of animation") try: s = self.arg.call(cid, offset, bytes) @@ -473,7 +475,7 @@ class ArgImageFile(ImageFile.ImageFile): except "glurk": # AttributeError if Image.DEBUG: - print cid, bytes, "(unknown)" + print(cid, bytes, "(unknown)") s = self.fp.read(bytes) self.arg.crc(cid, s) diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py index 47b5f05c8..3be80d602 100644 --- a/PIL/BdfFontFile.py +++ b/PIL/BdfFontFile.py @@ -17,10 +17,9 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile +from PIL import Image +from PIL import FontFile -import string # -------------------------------------------------------------------- # parse X Bitmap Distribution Format (BDF) @@ -44,39 +43,39 @@ bdf_spacing = { def bdf_char(f): # skip to STARTCHAR - while 1: + while True: s = f.readline() if not s: return None - if s[:9] == "STARTCHAR": + if s[:9] == b"STARTCHAR": break - id = string.strip(s[9:]) + id = s[9:].strip().decode('ascii') # load symbol properties props = {} - while 1: + while True: s = f.readline() - if not s or s[:6] == "BITMAP": + if not s or s[:6] == b"BITMAP": break - i = string.find(s, " ") - props[s[:i]] = s[i+1:-1] + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') # load bitmap bitmap = [] - while 1: + while True: s = f.readline() - if not s or s[:7] == "ENDCHAR": + if not s or s[:7] == b"ENDCHAR": break bitmap.append(s[:-1]) - bitmap = string.join(bitmap, "") + bitmap = b"".join(bitmap) - [x, y, l, d] = map(int, string.split(props["BBX"])) - [dx, dy] = map(int, string.split(props["DWIDTH"])) + [x, y, l, d] = [int(s) for s in props["BBX"].split()] + [dx, dy] = [int(s) for s in props["DWIDTH"].split()] bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) try: - im = Image.fromstring("1", (x, y), bitmap, "hex", "1") + im = Image.frombytes("1", (x, y), bitmap, "hex", "1") except ValueError: # deal with zero-width characters im = Image.new("1", (x, y)) @@ -93,38 +92,38 @@ class BdfFontFile(FontFile.FontFile): FontFile.FontFile.__init__(self) s = fp.readline() - if s[:13] != "STARTFONT 2.1": - raise SyntaxError, "not a valid BDF file" + if s[:13] != b"STARTFONT 2.1": + raise SyntaxError("not a valid BDF file") props = {} comments = [] - while 1: + while True: s = fp.readline() - if not s or s[:13] == "ENDPROPERTIES": + if not s or s[:13] == b"ENDPROPERTIES": break - i = string.find(s, " ") - props[s[:i]] = s[i+1:-1] - if s[:i] in ["COMMENT", "COPYRIGHT"]: - if string.find(s, "LogicalFontDescription") < 0: - comments.append(s[i+1:-1]) + i = s.find(b" ") + props[s[:i].decode('ascii')] = s[i+1:-1].decode('ascii') + if s[:i] in [b"COMMENT", b"COPYRIGHT"]: + if s.find(b"LogicalFontDescription") < 0: + comments.append(s[i+1:-1].decode('ascii')) - font = string.split(props["FONT"], "-") + font = props["FONT"].split("-") - font[4] = bdf_slant[string.upper(font[4])] - font[11] = bdf_spacing[string.upper(font[11])] + font[4] = bdf_slant[font[4].upper()] + font[11] = bdf_spacing[font[11].upper()] ascent = int(props["FONT_ASCENT"]) descent = int(props["FONT_DESCENT"]) - fontname = string.join(font[1:], ";") + fontname = ";".join(font[1:]) # print "#", fontname # for i in comments: # print "#", i font = [] - while 1: + while True: c = bdf_char(fp) if not c: break diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py index 09c0a2141..a4eb1d3f2 100644 --- a/PIL/BmpImagePlugin.py +++ b/PIL/BmpImagePlugin.py @@ -27,21 +27,19 @@ __version__ = "0.7" -import string -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le # # -------------------------------------------------------------------- # Read BMP 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) - - BIT2MODE = { # bits => mode, rawmode 1: ("P", "P;1"), @@ -53,7 +51,7 @@ BIT2MODE = { } def _accept(prefix): - return prefix[:2] == "BM" + return prefix[:2] == b"BM" ## # Image plugin for the Windows BMP format. @@ -93,7 +91,7 @@ class BmpImageFile(ImageFile.ImageFile): lutsize = 4 colors = i32(s[32:]) direction = -1 - if s[11] == '\xff': + if i8(s[11]) == 0xff: # upside-down storage self.size = self.size[0], 2**32 - self.size[1] direction = 0 @@ -132,10 +130,10 @@ class BmpImageFile(ImageFile.ImageFile): if colors == 2: indices = (0, 255) else: - indices = range(colors) + indices = list(range(colors)) for i in indices: rgb = read(lutsize)[:3] - if rgb != chr(i)*3: + if rgb != o8(i)*3: greyscale = 0 palette.append(rgb) if greyscale: @@ -146,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile): else: self.mode = "P" self.palette = ImagePalette.raw( - "BGR", string.join(palette, "") + "BGR", b"".join(palette) ) if not offset: @@ -163,7 +161,7 @@ class BmpImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(14) - if s[:2] != "BM": + if s[:2] != b"BM": raise SyntaxError("Not a BMP file") offset = i32(s[10:]) @@ -182,12 +180,6 @@ class DibImageFile(BmpImageFile): # -------------------------------------------------------------------- # Write BMP 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, 2), "L": ("L", 8, 256), @@ -205,13 +197,13 @@ def _save(im, fp, filename, check=0): if check: return check - stride = ((im.size[0]*bits+7)/8+3)&(~3) + stride = ((im.size[0]*bits+7)//8+3)&(~3) header = 40 # or 64 for OS/2 version 2 offset = 14 + header + colors * 4 image = stride * im.size[1] # bitmap header - fp.write("BM" + # file type (magic) + fp.write(b"BM" + # file type (magic) o32(offset+image) + # file size o32(0) + # reserved o32(offset)) # image data offset @@ -228,14 +220,14 @@ def _save(im, fp, filename, check=0): o32(colors) + # colors used o32(colors)) # colors important - fp.write("\000" * (header - 40)) # padding (for OS/2 format) + fp.write(b"\0" * (header - 40)) # padding (for OS/2 format) if im.mode == "1": for i in (0, 255): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "L": for i in range(256): - fp.write(chr(i) * 4) + fp.write(o8(i) * 4) elif im.mode == "P": fp.write(im.im.getpalette("RGB", "BGRX")) diff --git a/PIL/BufrStubImagePlugin.py b/PIL/BufrStubImagePlugin.py index 4b111b393..a55ae5613 100644 --- a/PIL/BufrStubImagePlugin.py +++ b/PIL/BufrStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from PIL import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:4] == "BUFR" or prefix[:4] == "ZCZC" + return prefix[:4] == b"BUFR" or prefix[:4] == b"ZCZC" class BufrStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/ContainerIO.py b/PIL/ContainerIO.py index dff50e6bf..f4a15b813 100644 --- a/PIL/ContainerIO.py +++ b/PIL/ContainerIO.py @@ -92,7 +92,7 @@ class ContainerIO: def readline(self): s = "" - while 1: + while True: c = self.read(1) if not c: break @@ -108,7 +108,7 @@ class ContainerIO: def readlines(self): l = [] - while 1: + while True: s = self.readline() if not s: break diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py index 6a126e701..4cf2882e2 100644 --- a/PIL/CurImagePlugin.py +++ b/PIL/CurImagePlugin.py @@ -19,21 +19,19 @@ __version__ = "0.1" -import Image, BmpImagePlugin +from PIL import Image, BmpImagePlugin, _binary # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _accept(prefix): - return prefix[:4] == "\0\0\2\0" + return prefix[:4] == b"\0\0\2\0" ## # Image plugin for Windows Cursor files. @@ -50,20 +48,20 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): # check magic s = self.fp.read(6) if not _accept(s): - raise SyntaxError, "not an CUR file" + raise SyntaxError("not an CUR file") # pick the largest cursor in the file - m = "" + m = b"" 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]): + elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(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 "width", i8(s[0]) + #print "height", i8(s[1]) + #print "colors", i8(s[2]) + #print "reserved", i8(s[3]) #print "hotspot x", i16(s[4:]) #print "hotspot y", i16(s[6:]) #print "bytes", i32(s[8:]) @@ -73,7 +71,7 @@ class CurImageFile(BmpImagePlugin.BmpImageFile): self._bitmap(i32(m[12:]) + offset) # patch up the bitmap height - self.size = self.size[0], self.size[1]/2 + 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 diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 32da7d831..631875e68 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -23,14 +23,13 @@ __version__ = "0.2" -import Image +from PIL import Image, _binary -from PcxImagePlugin import PcxImageFile +from PIL.PcxImagePlugin import PcxImageFile MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then? -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i32 = _binary.i32le def _accept(prefix): return i32(prefix) == MAGIC @@ -48,7 +47,7 @@ class DcxImageFile(PcxImageFile): # Header s = self.fp.read(4) if i32(s) != MAGIC: - raise SyntaxError, "not a DCX file" + raise SyntaxError("not a DCX file") # Component directory self._offset = [] diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py index 1927b3227..a8706b05f 100644 --- a/PIL/EpsImagePlugin.py +++ b/PIL/EpsImagePlugin.py @@ -20,29 +20,52 @@ __version__ = "0.5" -import re, string -import Image, ImageFile +import re +import io +from PIL import Image, ImageFile, _binary # # -------------------------------------------------------------------- -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +i32 = _binary.i32le +o32 = _binary.o32le split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") -def Ghostscript(tile, size, fp): - """Render an image using Ghostscript (Unix only)""" +gs_windows_binary = None +import sys +if sys.platform.startswith('win'): + import shutil + if hasattr(shutil, 'which'): + which = shutil.which + else: + # Python < 3.3 + import distutils.spawn + which = distutils.spawn.find_executable + for binary in ('gswin32c', 'gswin64c', 'gs'): + if which(binary) is not None: + gs_windows_binary = binary + break + else: + gs_windows_binary = False + +def Ghostscript(tile, size, fp, scale=1): + """Render an image using Ghostscript""" # Unpack decoder tile decoder, tile, offset, data = tile[0] length, bbox = data - import tempfile, os + #Hack to support hi-res rendering + scale = int(scale) or 1 + orig_size = size + orig_bbox = bbox + size = (size[0] * scale, size[1] * scale) + bbox = [bbox[0], bbox[1], bbox[2] * scale, bbox[3] * scale] + #print("Ghostscript", scale, size, orig_size, bbox, orig_bbox) + + import tempfile, os, subprocess file = tempfile.mktemp() @@ -50,27 +73,32 @@ def Ghostscript(tile, size, fp): command = ["gs", "-q", # quite mode "-g%dx%d" % size, # set output geometry (pixels) + "-r%d" % (72*scale), # set input DPI (dots per inch) "-dNOPAUSE -dSAFER", # don't pause between pages, safe mode "-sDEVICE=ppmraw", # ppm driver "-sOutputFile=%s" % file,# output file - "- >/dev/null 2>/dev/null"] + ] - command = string.join(command) + if gs_windows_binary is not None: + if gs_windows_binary is False: + raise WindowsError('Unable to locate Ghostscript on paths') + command[0] = gs_windows_binary # push data through ghostscript try: - gs = os.popen(command, "w") + gs = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # adjust for image origin if bbox[0] != 0 or bbox[1] != 0: - gs.write("%d %d translate\n" % (-bbox[0], -bbox[1])) + gs.stdin.write(("%d %d translate\n" % (-bbox[0], -bbox[1])).encode('ascii')) fp.seek(offset) while length > 0: s = fp.read(8192) if not s: break length = length - len(s) - gs.write(s) - status = gs.close() + gs.stdin.write(s) + gs.stdin.close() + status = gs.wait() if status: raise IOError("gs failed (status %d)" % status) im = Image.core.open_ppm(file) @@ -93,30 +121,32 @@ class PSFile: def seek(self, offset, whence=0): self.char = None self.fp.seek(offset, whence) + def read(self, count): + return self.fp.read(count).decode('latin-1') def tell(self): pos = self.fp.tell() if self.char: pos = pos - 1 return pos def readline(self): - s = "" + s = b"" if self.char: c = self.char self.char = None else: c = self.fp.read(1) - while c not in "\r\n": + while c not in b"\r\n": s = s + c c = self.fp.read(1) - if c == "\r": + if c == b"\r": self.char = self.fp.read(1) - if self.char == "\n": + if self.char == b"\n": self.char = None - return s + "\n" + return s.decode('latin-1') + "\n" def _accept(prefix): - return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L + return prefix[:4] == b"%!PS" or i32(prefix) == 0xC6D3D0C5 ## # Image plugin for Encapsulated Postscript. This plugin supports only @@ -141,12 +171,12 @@ class EpsImageFile(ImageFile.ImageFile): offset = 0 fp.seek(0, 2) length = fp.tell() - elif i32(s) == 0xC6D3D0C5L: + elif i32(s) == 0xC6D3D0C5: offset = i32(s[4:]) length = i32(s[8:]) fp.seek(offset) else: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") fp.seek(offset) @@ -163,7 +193,7 @@ class EpsImageFile(ImageFile.ImageFile): while s: if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -172,8 +202,8 @@ class EpsImageFile(ImageFile.ImageFile): try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an EPS file" + except re.error as v: + raise SyntaxError("not an EPS file") if m: k, v = m.group(1, 2) @@ -183,7 +213,7 @@ class EpsImageFile(ImageFile.ImageFile): # Note: The DSC spec says that BoundingBox # fields should be integers, but some drivers # put floating point values there anyway. - box = map(int, map(float, string.split(v))) + box = [int(float(s)) for s in v.split()] self.size = box[2] - box[0], box[3] - box[1] self.tile = [("eps", (0,0) + self.size, offset, (length, box))] @@ -196,14 +226,19 @@ class EpsImageFile(ImageFile.ImageFile): if m: k = m.group(1) + if k == "EndComments": break if k[:8] == "PS-Adobe": self.info[k[:8]] = k[9:] else: self.info[k] = "" + elif s[0:1] == '%': + # handle non-DSC Postscript comments that some + # tools mistakenly put in the Comments section + pass else: - raise IOError, "bad EPS header" + raise IOError("bad EPS header") s = fp.readline() @@ -217,7 +252,7 @@ class EpsImageFile(ImageFile.ImageFile): while s[0] == "%": if len(s) > 255: - raise SyntaxError, "not an EPS file" + raise SyntaxError("not an EPS file") if s[-2:] == '\r\n': s = s[:-2] @@ -227,7 +262,7 @@ class EpsImageFile(ImageFile.ImageFile): if s[:11] == "%ImageData:": [x, y, bi, mo, z3, z4, en, id] =\ - string.split(s[11:], maxsplit=7) + s[11:].split(None, 7) x = int(x); y = int(y) @@ -257,7 +292,7 @@ class EpsImageFile(ImageFile.ImageFile): id = id[1:-1] # Scan forward to the actual image data - while 1: + while True: s = fp.readline() if not s: break @@ -274,13 +309,13 @@ class EpsImageFile(ImageFile.ImageFile): break if not box: - raise IOError, "cannot determine EPS bounding box" + raise IOError("cannot determine EPS bounding box") - def load(self): + def load(self, scale=1): # Load EPS via Ghostscript if not self.tile: return - self.im = Ghostscript(self.tile, self.size, self.fp) + self.im = Ghostscript(self.tile, self.size, self.fp, scale) self.mode = self.im.mode self.size = self.im.size self.tile = [] @@ -304,7 +339,18 @@ def _save(im, fp, filename, eps=1): elif im.mode == "CMYK": operator = (8, 4, "false 4 colorimage") else: - raise ValueError, "image mode is not supported" + raise ValueError("image mode is not supported") + + class NoCloseStream: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def close(self): + pass + + base_fp = fp + fp = io.TextIOWrapper(NoCloseStream(fp), encoding='latin-1') if eps: # @@ -328,9 +374,10 @@ def _save(im, fp, filename, eps=1): fp.write("%d %d 8\n" % im.size) # <= bits fp.write("[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1])) fp.write("{ currentfile buf readhexstring pop } bind\n") - fp.write("%s\n" % operator[2]) + fp.write(operator[2] + "\n") + fp.flush() - ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)]) + ImageFile._save(im, base_fp, [("eps", (0,0)+im.size, 0, None)]) fp.write("\n%%%%EndBinary\n") fp.write("grestore end\n") diff --git a/PIL/ExifTags.py b/PIL/ExifTags.py index b033aad35..16473f930 100644 --- a/PIL/ExifTags.py +++ b/PIL/ExifTags.py @@ -20,11 +20,19 @@ TAGS = { # possibly incomplete + 0x00fe: "NewSubfileType", + 0x00ff: "SubfileType", 0x0100: "ImageWidth", 0x0101: "ImageLength", 0x0102: "BitsPerSample", 0x0103: "Compression", 0x0106: "PhotometricInterpretation", + 0x0107: "Threshholding", + 0x0108: "CellWidth", + 0x0109: "CellLenght", + 0x010a: "FillOrder", + 0x010d: "DocumentName", + 0x011d: "PageName", 0x010e: "ImageDescription", 0x010f: "Make", 0x0110: "Model", @@ -33,19 +41,25 @@ TAGS = { 0x0115: "SamplesPerPixel", 0x0116: "RowsPerStrip", 0x0117: "StripByteConunts", + 0x0118: "MinSampleValue", + 0x0119: "MaxSampleValue", 0x011a: "XResolution", - 0x011a: "XResolution", - 0x011b: "YResolution", 0x011b: "YResolution", 0x011c: "PlanarConfiguration", - 0x0128: "ResolutionUnit", + 0x0120: "FreeOffsets", + 0x0121: "FreeByteCounts", + 0x0122: "GrayResponseUnit", + 0x0123: "GrayResponseCurve", 0x0128: "ResolutionUnit", 0x012d: "TransferFunction", 0x0131: "Software", 0x0132: "DateTime", 0x013b: "Artist", + 0x013c: "HostComputer", 0x013e: "WhitePoint", 0x013f: "PrimaryChromaticities", + 0x0140: "ColorMap", + 0x0152: "ExtraSamples", 0x0201: "JpegIFOffset", 0x0202: "JpegIFByteCount", 0x0211: "YCbCrCoefficients", @@ -120,11 +134,31 @@ TAGS = { 0xa300: "FileSource", 0xa301: "SceneType", 0xa302: "CFAPattern", + 0xa401: "CustomRendered", + 0xa402: "ExposureMode", + 0xa403: "WhiteBalance", + 0xa404: "DigitalZoomRatio", + 0xa405: "FocalLengthIn35mmFilm", + 0xa406: "SceneCaptureType", + 0xa407: "GainControl", + 0xa408: "Contrast", + 0xa409: "Saturation", + 0xa40a: "Sharpness", + 0xa40b: "DeviceSettingDescription", + 0xa40c: "SubjectDistanceRange", + 0xa420: "ImageUniqueID", + 0xa430: "CameraOwnerName", + 0xa431: "BodySerialNumber", + 0xa432: "LensSpecification", + 0xa433: "LensMake", + 0xa434: "LensModel", + 0xa435: "LensSerialNumber", + 0xa500: "Gamma", } ## -# Maps EXIF GSP tags to tag names. +# Maps EXIF GPS tags to tag names. GPSTAGS = { 0: "GPSVersionID", @@ -153,5 +187,10 @@ GPSTAGS = { 23: "GPSDestBearingRef", 24: "GPSDestBearing", 25: "GPSDestDistanceRef", - 26: "GPSDestDistance" + 26: "GPSDestDistance", + 27: "GPSProcessingMethod", + 28: "GPSAreaInformation", + 29: "GPSDateStamp", + 30: "GPSDifferential", + 31: "GPSHPositioningError", } diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py index ac8be80a9..0b851ae59 100644 --- a/PIL/FitsStubImagePlugin.py +++ b/PIL/FitsStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from PIL import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:6] == "SIMPLE" + return prefix[:6] == b"SIMPLE" class FITSStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 706c36a7f..bef9b722e 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -18,15 +18,12 @@ __version__ = "0.2" -import Image, ImageFile, ImagePalette -import string +from PIL import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +o8 = _binary.o8 # # decoder @@ -48,8 +45,10 @@ class FliImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(128) magic = i16(s[4:6]) - if magic not in [0xAF11, 0xAF12]: - raise SyntaxError, "not an FLI/FLC file" + if not (magic in [0xAF11, 0xAF12] and + i16(s[14:16]) in [0, 3] and # flags + s[20:22] == b"\x00\x00"): # reserved + raise SyntaxError("not an FLI/FLC file") # image characteristics self.mode = "P" @@ -62,7 +61,7 @@ class FliImageFile(ImageFile.ImageFile): self.info["duration"] = duration # look for palette - palette = map(lambda a: (a,a,a), range(256)) + palette = [(a,a,a) for a in range(256)] s = self.fp.read(16) @@ -81,8 +80,8 @@ class FliImageFile(ImageFile.ImageFile): elif i16(s[4:6]) == 4: self._palette(palette, 0) - palette = map(lambda (r,g,b): chr(r)+chr(g)+chr(b), palette) - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + palette = [o8(r)+o8(g)+o8(b) for (r,g,b) in palette] + self.palette = ImagePalette.raw("RGB", b"".join(palette)) # set things up to decode first frame self.frame = -1 @@ -96,22 +95,22 @@ class FliImageFile(ImageFile.ImageFile): i = 0 for e in range(i16(self.fp.read(2))): s = self.fp.read(2) - i = i + ord(s[0]) - n = ord(s[1]) + i = i + i8(s[0]) + n = i8(s[1]) if n == 0: n = 256 s = self.fp.read(n * 3) for n in range(0, len(s), 3): - r = ord(s[n]) << shift - g = ord(s[n+1]) << shift - b = ord(s[n+2]) << shift + r = i8(s[n]) << shift + g = i8(s[n+1]) << shift + b = i8(s[n+2]) << shift palette[i] = (r, g, b) i = i + 1 def seek(self, frame): if frame != self.frame + 1: - raise ValueError, "cannot seek to frame %d" % frame + raise ValueError("cannot seek to frame %d" % frame) self.frame = frame # move to next frame diff --git a/PIL/FontFile.py b/PIL/FontFile.py index d9d8eb38a..4dce0a292 100644 --- a/PIL/FontFile.py +++ b/PIL/FontFile.py @@ -15,7 +15,7 @@ # import os -import Image +from PIL import Image, _binary import marshal @@ -31,7 +31,7 @@ def puti16(fp, values): for v in values: if v < 0: v = v + 65536 - fp.write(chr(v>>8&255) + chr(v&255)) + fp.write(_binary.o16be(v)) ## # Base class for raster font file handlers. @@ -106,9 +106,9 @@ class FontFile: # font metrics fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont\n") - fp.write(";;;;;;%d;\n" % self.ysize) # HACK!!! - fp.write("DATA\n") + fp.write(b"PILfont\n") + fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!! + fp.write(b"DATA\n") for id in range(256): m = self.metrics[id] if not m: @@ -128,13 +128,13 @@ class FontFile: data = marshal.dumps((self.metrics, self.info)) if zlib: - data = "z" + zlib.compress(data, 9) + data = b"z" + zlib.compress(data, 9) else: - data = "u" + data + data = b"u" + data fp = open(os.path.splitext(filename)[0] + ".pil", "wb") - fp.write("PILfont2\n" + self.name + "\n" + "DATA\n") + fp.write(b"PILfont2\n" + self.name + "\n" + "DATA\n") fp.write(data) diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py index b78fe8c70..b712557d7 100644 --- a/PIL/FpxImagePlugin.py +++ b/PIL/FpxImagePlugin.py @@ -19,8 +19,8 @@ __version__ = "0.1" -import Image, ImageFile -from OleFileIO import * +from PIL import Image, ImageFile +from PIL.OleFileIO import * # we map from colour field tuples to (mode, rawmode) descriptors @@ -60,10 +60,10 @@ class FpxImageFile(ImageFile.ImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an FPX file; invalid OLE file" + raise SyntaxError("not an FPX file; invalid OLE file") if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": - raise SyntaxError, "not an FPX file; bad root CLSID" + raise SyntaxError("not an FPX file; bad root CLSID") self._open_index(1) @@ -108,7 +108,7 @@ class FpxImageFile(ImageFile.ImageFile): self.jpeg = {} for i in range(256): id = 0x3000001|(i << 16) - if prop.has_key(id): + if id in prop: self.jpeg[i] = prop[id] # print len(self.jpeg), "tables loaded" @@ -143,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile): # print size, self.mode, self.rawmode if size != self.size: - raise IOError, "subimage mismatch" + raise IOError("subimage mismatch") # get tile descriptors fp.seek(28 + offset) @@ -170,8 +170,8 @@ class FpxImageFile(ImageFile.ImageFile): elif compression == 2: - internal_color_conversion = ord(s[14]) - jpeg_tables = ord(s[15]) + internal_color_conversion = i8(s[14]) + jpeg_tables = i8(s[15]) rawmode = self.rawmode if internal_color_conversion: @@ -198,7 +198,7 @@ class FpxImageFile(ImageFile.ImageFile): self.tile_prefix = self.jpeg[jpeg_tables] else: - raise IOError, "unknown/invalid compression" + raise IOError("unknown/invalid compression") x = x + xtile if x >= xsize: diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index c71a0f77f..e2a5d0c9c 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -13,10 +13,9 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from PIL import Image, ImageFile, _binary -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24L) +i32 = _binary.i32be def _accept(prefix): return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 @@ -34,13 +33,13 @@ class GbrImageFile(ImageFile.ImageFile): header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) if header_size < 20 or version != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) bytes = i32(self.fp.read(4)) if width <= 0 or height <= 0 or bytes != 1: - raise SyntaxError, "not a GIMP brush" + raise SyntaxError("not a GIMP brush") comment = self.fp.read(header_size - 20)[:-1] @@ -59,8 +58,8 @@ class GbrImageFile(ImageFile.ImageFile): # create an image out of the brush data block self.im = Image.core.new(self.mode, self.size) - self.im.fromstring(self.data) - self.data = "" + self.im.frombytes(self.data) + self.data = b"" # # registry diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py index b5f1dd25d..f1dbc7c62 100644 --- a/PIL/GdImageFile.py +++ b/PIL/GdImageFile.py @@ -25,10 +25,16 @@ __version__ = "0.1" -import ImageFile, ImagePalette +from PIL import ImageFile, ImagePalette, _binary +from PIL._util import isPath -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i16 = _binary.i16be ## # Image plugin for the GD uncompressed format. Note that this format @@ -72,10 +78,9 @@ def open(fp, mode = "r"): if mode != "r": raise ValueError("bad mode") - if type(fp) == type(""): - import __builtin__ + if isPath(fp): filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index 4d03493b5..aed525824 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -28,24 +28,23 @@ __version__ = "0.9" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary # -------------------------------------------------------------------- # Helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def o16(i): - return chr(i&255) + chr(i>>8&255) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 +o16 = _binary.o16le # -------------------------------------------------------------------- # Identify/read GIF files def _accept(prefix): - return prefix[:6] in ["GIF87a", "GIF89a"] + return prefix[:6] in [b"GIF87a", b"GIF89a"] ## # Image plugin for GIF images. This plugin supports both GIF87 and @@ -55,39 +54,34 @@ class GifImageFile(ImageFile.ImageFile): format = "GIF" format_description = "Compuserve GIF" - global_palette = None def data(self): s = self.fp.read(1) - if s and ord(s): - return self.fp.read(ord(s)) + if s and i8(s): + return self.fp.read(i8(s)) return None def _open(self): # Screen s = self.fp.read(13) - if s[:6] not in ["GIF87a", "GIF89a"]: - raise SyntaxError, "not a GIF file" + if s[:6] not in [b"GIF87a", b"GIF89a"]: + raise SyntaxError("not a GIF file") self.info["version"] = s[:6] - self.size = i16(s[6:]), i16(s[8:]) - self.tile = [] - - flags = ord(s[10]) - + flags = i8(s[10]) bits = (flags & 7) + 1 if flags & 128: # get global palette - self.info["background"] = ord(s[11]) + self.info["background"] = i8(s[11]) # check if palette contains colour indices p = self.fp.read(3<= 3 and ord(block[0]) == 1: + if len(block) >= 3 and i8(block[0]) == 1: self.info["loop"] = i16(block[1:3]) while self.data(): pass - elif s == ",": + elif s == b",": # # local image # @@ -177,7 +172,7 @@ class GifImageFile(ImageFile.ImageFile): # extent x0, y0 = i16(s[0:]), i16(s[2:]) x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) - flags = ord(s[8]) + flags = i8(s[8]) interlace = (flags & 64) != 0 @@ -187,7 +182,7 @@ class GifImageFile(ImageFile.ImageFile): ImagePalette.raw("RGB", self.fp.read(3< 0: + paletteBytes += o8(0) * 3 * actualTargetSizeDiff + + # Header + Logical Screen Descriptor + Global Color Table + header.append(paletteBytes) + return header, usedPaletteColors - return s def getdata(im, offset = (0, 0), **params): """Return a list of strings representing this image. @@ -374,17 +435,17 @@ def getdata(im, offset = (0, 0), **params): im.encoderinfo = params # local image header - fp.write("," + + fp.write(b"," + o16(offset[0]) + # offset o16(offset[1]) + o16(im.size[0]) + # size o16(im.size[1]) + - chr(0) + # flags - chr(8)) # bits + o8(0) + # flags + o8(8)) # bits ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])]) - fp.write("\0") # end of image data + fp.write(b"\0") # end of image data finally: del im.encoderinfo diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py index 1a850dc53..4ae727773 100644 --- a/PIL/GimpGradientFile.py +++ b/PIL/GimpGradientFile.py @@ -14,7 +14,7 @@ # from math import pi, log, sin, sqrt -import string +from PIL._binary import o8 # -------------------------------------------------------------------- # Stuff to translate curve segments to palette values (derived from @@ -79,15 +79,15 @@ class GradientFile: 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)) + r = o8(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = o8(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = o8(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = o8(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" + return b"".join(palette), "RGBA" ## # File handler for GIMP's gradient format. @@ -96,8 +96,8 @@ class GimpGradientFile(GradientFile): def __init__(self, fp): - if fp.readline()[:13] != "GIMP Gradient": - raise SyntaxError, "not a GIMP gradient file" + if fp.readline()[:13] != b"GIMP Gradient": + raise SyntaxError("not a GIMP gradient file") count = int(fp.readline()) @@ -105,8 +105,8 @@ class GimpGradientFile(GradientFile): for i in range(count): - s = string.split(fp.readline()) - w = map(float, s[:11]) + s = fp.readline().split() + w = [float(x) for x in s[:11]] x0, x1 = w[0], w[2] xm = w[1] @@ -117,7 +117,7 @@ class GimpGradientFile(GradientFile): cspace = int(s[12]) if cspace != 0: - raise IOError, "cannot handle HSV colour space" + raise IOError("cannot handle HSV colour space") gradient.append((x0, x1, xm, rgb0, rgb1, segment)) diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py index aba85b71f..c271b964f 100644 --- a/PIL/GimpPaletteFile.py +++ b/PIL/GimpPaletteFile.py @@ -14,7 +14,8 @@ # See the README file for information on usage and redistribution. # -import re, string +import re +from PIL._binary import o8 ## # File handler for GIMP's palette format. @@ -25,10 +26,10 @@ class GimpPaletteFile: def __init__(self, fp): - self.palette = map(lambda i: chr(i)*3, range(256)) + self.palette = [o8(i)*3 for i in range(256)] - if fp.readline()[:12] != "GIMP Palette": - raise SyntaxError, "not a GIMP palette file" + if fp.readline()[:12] != b"GIMP Palette": + raise SyntaxError("not a GIMP palette file") i = 0 @@ -39,21 +40,21 @@ class GimpPaletteFile: if not s: break # skip fields and comment lines - if re.match("\w+:|#", s): + if re.match(b"\w+:|#", s): continue if len(s) > 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") - v = tuple(map(int, string.split(s)[:3])) + v = tuple(map(int, s.split()[:3])) if len(v) != 3: - raise ValueError, "bad palette entry" + raise ValueError("bad palette entry") if 0 <= i <= 255: - self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2]) + self.palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) i = i + 1 - self.palette = string.join(self.palette, "") + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py index e232f90db..d76585c99 100644 --- a/PIL/GribStubImagePlugin.py +++ b/PIL/GribStubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from PIL import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[0:4] == "GRIB" and prefix[7] == chr(1) + return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01' class GribStubImageFile(ImageFile.StubImageFile): diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py index bc5e37433..eb888d8c3 100644 --- a/PIL/Hdf5StubImagePlugin.py +++ b/PIL/Hdf5StubImagePlugin.py @@ -9,7 +9,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile +from PIL import Image, ImageFile _handler = None @@ -26,7 +26,7 @@ def register_handler(handler): # Image adapter def _accept(prefix): - return prefix[:8] == "\x89HDF\r\n\x1a\n" + return prefix[:8] == b"\x89HDF\r\n\x1a\n" class HDF5StubImageFile(ImageFile.StubImageFile): diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py index 4d68fa232..8fac1308c 100644 --- a/PIL/IcnsImagePlugin.py +++ b/PIL/IcnsImagePlugin.py @@ -14,27 +14,31 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFile -import string, struct +from PIL import Image, ImageFile, _binary +import struct + +i8 = _binary.i8 HEADERSIZE = 8 def nextheader(fobj): return struct.unpack('>4sI', fobj.read(HEADERSIZE)) -def read_32t(fobj, (start, length), (width, height)): +def read_32t(fobj, start_length, size): # The 128x128 icon seems to have an extra header for some reason. + (start, length) = start_length 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)) + if sig != b'\x00\x00\x00\x00': + raise SyntaxError('Unknown signature, expecting 0x00000000') + return read_32(fobj, (start + 4, length - 4), size) -def read_32(fobj, (start, length), size): +def read_32(fobj, start_length, size): """ Read a 32bit RGB icon resource. Seems to be either uncompressed or an RLE packbits-like scheme. """ + (start, length) = start_length fobj.seek(start) sizesq = size[0] * size[1] if length == sizesq * 3: @@ -51,7 +55,7 @@ def read_32(fobj, (start, length), size): byte = fobj.read(1) if not byte: break - byte = ord(byte) + byte = i8(byte) if byte & 0x80: blocksize = byte - 125 byte = fobj.read(1) @@ -68,13 +72,14 @@ def read_32(fobj, (start, length), size): "Error reading channel [%r left]" % bytesleft ) band = Image.frombuffer( - "L", size, string.join(data, ""), "raw", "L", 0, 1 + "L", size, b"".join(data), "raw", "L", 0, 1 ) im.im.putband(band.im, band_ix) return {"RGB": im} -def read_mk(fobj, (start, length), size): +def read_mk(fobj, start_length, size): # Alpha masks seem to be uncompressed + (start, length) = start_length fobj.seek(start) band = Image.frombuffer( "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 @@ -85,20 +90,20 @@ class IcnsFile: SIZES = { (128, 128): [ - ('it32', read_32t), - ('t8mk', read_mk), + (b'it32', read_32t), + (b't8mk', read_mk), ], (48, 48): [ - ('ih32', read_32), - ('h8mk', read_mk), + (b'ih32', read_32), + (b'h8mk', read_mk), ], (32, 32): [ - ('il32', read_32), - ('l8mk', read_mk), + (b'il32', read_32), + (b'l8mk', read_mk), ], (16, 16): [ - ('is32', read_32), - ('s8mk', read_mk), + (b'is32', read_32), + (b's8mk', read_mk), ], } @@ -111,7 +116,7 @@ class IcnsFile: self.fobj = fobj sig, filesize = nextheader(fobj) if sig != 'icns': - raise SyntaxError, 'not an icns file' + raise SyntaxError('not an icns file') i = HEADERSIZE while i < filesize: sig, blocksize = nextheader(fobj) @@ -125,7 +130,7 @@ class IcnsFile: sizes = [] for size, fmts in self.SIZES.items(): for (fmt, reader) in fmts: - if self.dct.has_key(fmt): + if fmt in self.dct: sizes.append(size) break return sizes @@ -133,7 +138,7 @@ class IcnsFile: def bestsize(self): sizes = self.itersizes() if not sizes: - raise SyntaxError, "No 32bit icon resources found" + raise SyntaxError("No 32bit icon resources found") return max(sizes) def dataforsize(self, size): @@ -201,7 +206,7 @@ class IcnsImageFile(ImageFile.ImageFile): self.load_end() -Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == 'icns') +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == b'icns') Image.register_extension("ICNS", '.icns') if __name__ == '__main__': diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py index 94846dc0c..82d33e383 100644 --- a/PIL/IcoImagePlugin.py +++ b/PIL/IcoImagePlugin.py @@ -4,9 +4,6 @@ # # Windows Icon support for PIL # -# Notes: -# uses BmpImagePlugin.py to read the bitmap data. -# # History: # 96-05-27 fl Created # @@ -16,71 +13,217 @@ # See the README file for information on usage and redistribution. # +# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis . +# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin +# +# Icon format references: +# * http://en.wikipedia.org/wiki/ICO_(file_format) +# * http://msdn.microsoft.com/en-us/library/ms997538.aspx + __version__ = "0.1" -import Image, BmpImagePlugin - +from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary +from math import log, ceil # # -------------------------------------------------------------------- -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le +_MAGIC = b"\0\0\1\0" def _accept(prefix): - return prefix[:4] == "\0\0\1\0" + return prefix[:4] == _MAGIC + + +class IcoFile: + def __init__(self, buf): + """ + Parse image from file-like object containing ico file data + """ + + # check magic + s = buf.read(6) + if not _accept(s): + raise SyntaxError("not an ICO file") + + self.buf = buf + self.entry = [] + + # Number of items in file + self.nb_items = i16(s[4:]) + + # Get headers for each item + for i in range(self.nb_items): + s = buf.read(16) + + icon_header = { + 'width': i8(s[0]), + 'height': i8(s[1]), + 'nb_color': i8(s[2]), # Number of colors in image (0 if >=8bpp) + 'reserved': i8(s[3]), + 'planes': i16(s[4:]), + 'bpp': i16(s[6:]), + 'size': i32(s[8:]), + 'offset': i32(s[12:]) + } + + # See Wikipedia + for j in ('width', 'height'): + if not icon_header[j]: + icon_header[j] = 256 + + # See Wikipedia notes about color depth. + # We need this just to differ images with equal sizes + icon_header['color_depth'] = (icon_header['bpp'] or (icon_header['nb_color'] != 0 and ceil(log(icon_header['nb_color'],2))) or 256) + + icon_header['dim'] = (icon_header['width'], icon_header['height']) + icon_header['square'] = icon_header['width'] * icon_header['height'] + + self.entry.append(icon_header) + + self.entry = sorted(self.entry, key=lambda x: x['color_depth']) + # ICO images are usually squares + # self.entry = sorted(self.entry, key=lambda x: x['width']) + self.entry = sorted(self.entry, key=lambda x: x['square']) + self.entry.reverse() + + def sizes(self): + """ + Get a list of all available icon sizes and color depths. + """ + return set((h['width'], h['height']) for h in self.entry) + + def getimage(self, size, bpp=False): + """ + Get an image from the icon + """ + for (i, h) in enumerate(self.entry): + if size == h['dim'] and (bpp == False or bpp == h['color_depth']): + return self.frame(i) + return self.frame(0) + + def frame(self, idx): + """ + Get an image from frame idx + """ + + header = self.entry[idx] + + self.buf.seek(header['offset']) + data = self.buf.read(8) + self.buf.seek(header['offset']) + + if data[:8] == PngImagePlugin._MAGIC: + # png frame + im = PngImagePlugin.PngImageFile(self.buf) + else: + # XOR + AND mask bmp frame + im = BmpImagePlugin.DibImageFile(self.buf) + + # change tile dimension to only encompass XOR image + im.size = (im.size[0], int(im.size[1] / 2)) + d, e, o, a = im.tile[0] + im.tile[0] = d, (0,0) + im.size, o, a + + # figure out where AND mask image starts + mode = a[0] + bpp = 8 + for k in BmpImagePlugin.BIT2MODE.keys(): + if mode == BmpImagePlugin.BIT2MODE[k][1]: + bpp = k + break + + if 32 == bpp: + # 32-bit color depth icon image allows semitransparent areas + # PIL's DIB format ignores transparency bits, recover them + # The DIB is packed in BGRX byte order where X is the alpha channel + + # Back up to start of bmp data + self.buf.seek(o) + # extract every 4th byte (eg. 3,7,11,15,...) + alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] + + # convert to an 8bpp grayscale image + mask = Image.frombuffer( + 'L', # 8bpp + im.size, # (w, h) + alpha_bytes, # source chars + 'raw', # raw decoder + ('L', 0, -1) # 8bpp inverted, unpadded, reversed + ) + else: + # get AND image from end of bitmap + w = im.size[0] + if (w % 32) > 0: + # bitmap row data is aligned to word boundaries + w += 32 - (im.size[0] % 32) + + # the total mask data is padded row size * height / bits per char + + and_mask_offset = o + int(im.size[0] * im.size[1] * (bpp / 8.0)) + total_bytes = int((w * im.size[1]) / 8) + + self.buf.seek(and_mask_offset) + maskData = self.buf.read(total_bytes) + + # convert raw data to image + mask = Image.frombuffer( + '1', # 1 bpp + im.size, # (w, h) + maskData, # source chars + 'raw', # raw decoder + ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed + ) + + # now we have two images, im is XOR image and mask is AND image + + # apply mask image as alpha channel + im = im.convert('RGBA') + im.putalpha(mask) + + return im ## # Image plugin for Windows Icon files. -class IcoImageFile(BmpImagePlugin.BmpImageFile): +class IcoImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Microsoft Windows .ico files. + By default the largest resolution image in the file will be loaded. This can + be changed by altering the 'size' attribute before calling 'load'. + + The info dictionary has a key 'sizes' that is a list of the sizes available + in the icon file. + + Handles classic, XP and Vista icon formats. + + This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis . + https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin + """ format = "ICO" format_description = "Windows Icon" def _open(self): + self.ico = IcoFile(self.fp) + self.info['sizes'] = self.ico.sizes() + self.size = self.ico.entry[0]['dim'] + self.load() - # 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 - + def load(self): + im = self.ico.getimage(self.size) + # if tile is PNG, it won't really be loaded yet + im.load() + self.im = im.im + self.mode = im.mode + self.size = im.size # # -------------------------------------------------------------------- Image.register_open("ICO", IcoImageFile, _accept) - Image.register_extension("ICO", ".ico") diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index a76dcf04e..1f8f011dc 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -28,8 +28,9 @@ __version__ = "0.7" -import re, string -import Image, ImageFile, ImagePalette +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8, o8 # -------------------------------------------------------------------- @@ -91,7 +92,7 @@ for i in range(2, 33): # -------------------------------------------------------------------- # Read IM directory -split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") +split = re.compile(br"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") def number(s): try: @@ -112,8 +113,8 @@ class ImImageFile(ImageFile.ImageFile): # 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" + if not b"\n" in self.fp.read(100): + raise SyntaxError("not an IM file") self.fp.seek(0) n = 0 @@ -125,91 +126,96 @@ class ImImageFile(ImageFile.ImageFile): self.rawmode = "L" - while 1: + while True: s = self.fp.read(1) # Some versions of IFUNC uses \n\r instead of \r\n... - if s == "\r": + if s == b"\r": continue - if not s or s[0] == chr(0) or s[0] == chr(26): + if not s or s == b'\0' or s == b'\x1A': 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" + raise SyntaxError("not an IM file") - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] == '\n': + elif s[-1:] == b'\n': s = s[:-1] try: m = split.match(s) - except re.error, v: - raise SyntaxError, "not an IM file" + except re.error as v: + raise SyntaxError("not an IM file") if m: k, v = m.group(1,2) + # Don't know if this is the correct encoding, but a decent guess + # (I guess) + k = k.decode('latin-1', 'replace') + v = v.decode('latin-1', 'replace') + # Convert value as appropriate if k in [FRAMES, SCALE, SIZE]: - v = string.replace(v, "*", ",") - v = tuple(map(number, string.split(v, ","))) + v = v.replace("*", ",") + v = tuple(map(number, v.split(","))) if len(v) == 1: v = v[0] - elif k == MODE and OPEN.has_key(v): + elif k == MODE and v in OPEN: 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): + if k in self.info: self.info[k].append(v) else: self.info[k] = [v] else: self.info[k] = v - if TAGS.has_key(k): + if k in TAGS: n = n + 1 else: - raise SyntaxError, "Syntax error in IM header: " + s + raise SyntaxError("Syntax error in IM header: " + s.decode('ascii', 'replace')) if not n: - raise SyntaxError, "Not an IM file" + 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): + while s and s[0:1] != b'\x1A': s = self.fp.read(1) if not s: - raise SyntaxError, "File truncated" + raise SyntaxError("File truncated") - if self.info.has_key(LUT): + if LUT in self.info: # 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): + if i8(palette[i]) != 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]) + self.lut = [i8(c) for c in palette[:256]] else: if self.mode == "L": self.mode = self.rawmode = "P" @@ -218,7 +224,7 @@ class ImImageFile(ImageFile.ImageFile): self.palette = ImagePalette.raw("RGB;L", palette) elif self.mode == "RGB": if not greyscale or not linear: - self.lut = map(ord, palette) + self.lut = [i8(c) for c in palette] self.frame = 0 @@ -253,7 +259,7 @@ class ImImageFile(ImageFile.ImageFile): def seek(self, frame): if frame < 0 or frame >= self.info[FRAMES]: - raise EOFError, "seek outside sequence" + raise EOFError("seek outside sequence") if self.frame == frame: return @@ -265,7 +271,7 @@ class ImImageFile(ImageFile.ImageFile): else: bits = 8 * len(self.mode) - size = ((self.size[0] * bits + 7) / 8) * self.size[1] + size = ((self.size[0] * bits + 7) // 8) * self.size[1] offs = self.__offset + frame * size self.fp = self.__fp @@ -304,7 +310,7 @@ 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 + raise ValueError("Cannot save %s images as IM" % im.mode) try: frames = im.encoderinfo["frames"] @@ -314,14 +320,14 @@ def _save(im, fp, filename, check=0): if check: return check - fp.write("Image type: %s image\r\n" % type) + fp.write(("Image type: %s image\r\n" % type).encode('ascii')) 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) + fp.write(("Name: %s\r\n" % filename).encode('ascii')) + fp.write(("Image size (x*y): %d*%d\r\n" % im.size).encode('ascii')) + fp.write(("File size (no of images): %d\r\n" % frames).encode('ascii')) if im.mode == "P": - fp.write("Lut: 1\r\n") - fp.write("\000" * (511-fp.tell()) + "\032") + fp.write(b"Lut: 1\r\n") + fp.write(b"\000" * (511-fp.tell()) + b"\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))]) diff --git a/PIL/Image.py b/PIL/Image.py index 77a532afa..0ab0367f7 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -24,12 +24,11 @@ # See the README file for information on usage and redistribution. # -VERSION = "1.1.7" +from __future__ import print_function -try: - import warnings -except ImportError: - warnings = None +from PIL import VERSION, PILLOW_VERSION, _plugins + +import warnings class _imaging_not_installed: # module placeholder @@ -51,62 +50,70 @@ try: # 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: + from PIL import _imaging as core + if PILLOW_VERSION != getattr(core, 'PILLOW_VERSION', None): + raise ImportError("The _imaging extension was built for another " + " version of Pillow or PIL") + +except ImportError as v: core = _imaging_not_installed() - if str(v)[:20] == "Module use of python" and warnings: + # Explanations for ways that we know we might have an import error + if str(v).startswith("Module use of python"): # The _imaging C module is present, but not compiled for # the right version (windows only). Print a warning, if # possible. warnings.warn( "The _imaging extension was built for another version " - "of Python; most PIL functions will be disabled", + "of Python.", RuntimeWarning ) - -import ImageMode -import ImagePalette - -import os, string, sys - -# type stuff -from types import IntType, StringType, TupleType + elif str(v).startswith("The _imaging extension"): + warnings.warn(str(v), RuntimeWarning) + elif "Symbol not found: _PyUnicodeUCS2_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS2 support; " + "recompile PIL or build Python --without-wide-unicode. ", + RuntimeWarning + ) + elif "Symbol not found: _PyUnicodeUCS4_FromString" in str(v): + warnings.warn( + "The _imaging extension was built for Python with UCS4 support; " + "recompile PIL or build Python --with-wide-unicode. ", + RuntimeWarning + ) + # Fail here anyway. Don't let people run with a mostly broken Pillow. + raise 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) + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ -## -# (Internal) Checks if an object is a tuple. +from PIL import ImageMode +from PIL._binary import i8, o8 +from PIL._util import isPath, isStringType -def isTupleType(t): - return isinstance(t, TupleType) +import os, sys + +# type stuff +import collections +import numbers -## -# (Internal) Checks if an object is an image object. def isImageType(t): + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ 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 @@ -149,11 +156,23 @@ FLOYDSTEINBERG = 3 # default WEB = 0 ADAPTIVE = 1 +MEDIANCUT = 0 +MAXCOVERAGE = 1 +FASTOCTREE = 2 + # categories NORMAL = 0 SEQUENCE = 1 CONTAINER = 2 +if hasattr(core, 'DEFAULT_STRATEGY'): + DEFAULT_STRATEGY = core.DEFAULT_STRATEGY + FILTERED = core.FILTERED + HUFFMAN_ONLY = core.HUFFMAN_ONLY + RLE = core.RLE + FIXED = core.FIXED + + # -------------------------------------------------------------------- # Registries @@ -181,6 +200,7 @@ _MODEINFO = { "RGBA": ("RGB", "L", ("R", "G", "B", "A")), "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + "LAB": ("RGB", "L", ("L", "A", "B")), # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and # BGR;24. Use these modes only if you know exactly what you're @@ -188,16 +208,7 @@ _MODEINFO = { } -try: - byteorder = sys.byteorder -except AttributeError: - import struct - if struct.unpack("h", "\0\1")[0] == 1: - byteorder = "big" - else: - byteorder = "little" - -if byteorder == 'little': +if sys.byteorder == 'little': _ENDIAN = '<' else: _ENDIAN = '>' @@ -213,7 +224,21 @@ _MODE_CONV = { "RGBX": ('|u1', 4), "RGBA": ('|u1', 4), "CMYK": ('|u1', 4), - "YCbCr": ('|u1', 4), + "YCbCr": ('|u1', 3), + "LAB": ('|u1', 3), # UNDONE - unsigned |u1i1i1 + # I;16 == I;16L, and I;32 == I;32L + "I;16": ('u2', None), + "I;16L": ('i2', None), + "I;16LS": ('u4', None), + "I;32L": ('i4', None), + "I;32LS": ('= 1: return try: - import BmpImagePlugin + from PIL import BmpImagePlugin except ImportError: pass try: - import GifImagePlugin + from PIL import GifImagePlugin except ImportError: pass try: - import JpegImagePlugin + from PIL import JpegImagePlugin except ImportError: pass try: - import PpmImagePlugin + from PIL import PpmImagePlugin except ImportError: pass try: - import PngImagePlugin + from PIL import PngImagePlugin except ImportError: pass # try: @@ -321,66 +347,47 @@ def preinit(): _initialized = 1 -## -# Explicitly initializes the Python Imaging Library. This function -# loads all available file format drivers. def init(): - "Load all file format drivers." + """ + Explicitly initializes the Python Imaging Library. This function + loads all available file format drivers. + """ global _initialized if _initialized >= 2: return 0 - visited = {} - - directories = sys.path - - try: - directories = directories + [os.path.dirname(__file__)] - except NameError: - pass - - # only check directories (including current, if present in the path) - for directory in filter(isDirectory, directories): - fullpath = os.path.abspath(directory) - if visited.has_key(fullpath): - continue - for file in os.listdir(directory): - if file[-14:] == "ImagePlugin.py": - f, e = os.path.splitext(file) - try: - sys.path.insert(0, directory) - try: - __import__(f, globals(), locals(), []) - finally: - del sys.path[0] - except ImportError: - if DEBUG: - print "Image: failed to import", - print f, ":", sys.exc_value - visited[fullpath] = None + for plugin in _plugins: + try: + if DEBUG: + print ("Importing %s"%plugin) + __import__("PIL.%s"%plugin, globals(), locals(), []) + except ImportError: + if DEBUG: + print("Image: failed to import", end=' ') + print(plugin, ":", sys.exc_info()[1]) if OPEN or SAVE: _initialized = 2 return 1 # -------------------------------------------------------------------- -# Codec factories (used by tostring/fromstring and ImageFile.load) +# Codec factories (used by tobytes/frombytes and ImageFile.load) def _getdecoder(mode, decoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: # get decoder decoder = getattr(core, decoder_name + "_decoder") - # print decoder, (mode,) + args + extra - return apply(decoder, (mode,) + args + extra) + # print(decoder, mode, args + extra) + return decoder(mode, *args + extra) except AttributeError: raise IOError("decoder %s not available" % decoder_name) @@ -389,14 +396,14 @@ def _getencoder(mode, encoder_name, args, extra=()): # tweak arguments if args is None: args = () - elif not isTupleType(args): + elif not isinstance(args, tuple): args = (args,) try: # get encoder encoder = getattr(core, encoder_name + "_encoder") - # print encoder, (mode,) + args + extra - return apply(encoder, (mode,) + args + extra) + # print(encoder, mode, args + extra) + return encoder(mode, *args + extra) except AttributeError: raise IOError("encoder %s not available" % encoder_name) @@ -404,26 +411,31 @@ def _getencoder(mode, encoder_name, args, extra=()): # -------------------------------------------------------------------- # Simple expression analyzer +def coerce_e(value): + return value if isinstance(value, _E) else _E(value) + class _E: - def __init__(self, data): self.data = data - def __coerce__(self, other): return self, _E(other) - def __add__(self, other): return _E((self.data, "__add__", other.data)) - def __mul__(self, other): return _E((self.data, "__mul__", other.data)) + def __init__(self, data): + self.data = data + def __add__(self, other): + return _E((self.data, "__add__", coerce_e(other).data)) + def __mul__(self, other): + return _E((self.data, "__mul__", coerce_e(other).data)) def _getscaleoffset(expr): stub = ["stub"] data = expr(_E(stub)).data try: (a, b, c) = data # simplified syntax - if (a is stub and b == "__mul__" and isNumberType(c)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number)): return c, 0.0 - if (a is stub and b == "__add__" and isNumberType(c)): + if (a is stub and b == "__add__" and isinstance(c, numbers.Number)): return 1.0, c except TypeError: pass try: ((a, b, c), d, e) = data # full syntax - if (a is stub and b == "__mul__" and isNumberType(c) and - d == "__add__" and isNumberType(e)): + if (a is stub and b == "__mul__" and isinstance(c, numbers.Number) and + d == "__add__" and isinstance(e, numbers.Number)): return c, e except TypeError: pass raise ValueError("illegal expression") @@ -432,17 +444,17 @@ def _getscaleoffset(expr): # -------------------------------------------------------------------- # Implementation wrapper -## -# This class represents an image object. To create Image objects, use -# the appropriate factory functions. There's hardly ever any reason -# to call the Image constructor directly. -# -# @see #open -# @see #new -# @see #fromstring - class Image: + """ + This class represents an image object. To create + :py:class:`~PIL.Image.Image` objects, use the appropriate factory + functions. There's hardly ever any reason to call the Image constructor + directly. + * :py:func:`~PIL.Image.open` + * :py:func:`~PIL.Image.new` + * :py:func:`~PIL.Image.frombytes` + """ format = None format_description = None @@ -463,7 +475,8 @@ class Image: new.mode = im.mode new.size = im.size new.palette = self.palette - if im.mode == "P": + if im.mode == "P" and not new.palette: + from PIL import ImagePalette new.palette = ImagePalette.ImagePalette() try: new.info = self.info.copy() @@ -507,23 +520,22 @@ class Image: shape, typestr = _conv_type_shape(self) new['shape'] = shape new['typestr'] = typestr - new['data'] = self.tostring() + new['data'] = self.tobytes() return new raise AttributeError(name) - ## - # Returns a string containing pixel data. - # - # @param encoder_name What encoder to use. The default is to - # use the standard "raw" encoder. - # @param *args Extra arguments to the encoder. - # @return An 8-bit string. + def tobytes(self, encoder_name="raw", *args): + """ + Return image as a bytes object - def tostring(self, encoder_name="raw", *args): - "Return image as a binary string" + :param encoder_name: What encoder to use. The default is to + use the standard "raw" encoder. + :param args: Extra arguments to the encoder. + :rtype: A bytes object. + """ # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if encoder_name == "raw" and args == (): @@ -538,47 +550,54 @@ class Image: bufsize = max(65536, self.size[0] * 4) # see RawEncode.c data = [] - while 1: + while True: l, s, d = e.encode(bufsize) data.append(d) if s: break if s < 0: - raise RuntimeError("encoder error %d in tostring" % s) + raise RuntimeError("encoder error %d in tobytes" % s) - return string.join(data, "") + return b"".join(data) - ## - # Returns the image converted to an X11 bitmap. This method - # only works for mode "1" images. - # - # @param name The name prefix to use for the bitmap variables. - # @return A string containing an X11 bitmap. - # @exception ValueError If the mode is not "1" + # Declare tostring as alias to tobytes + def tostring(self, *args, **kw): + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2, + ) + return self.tobytes(*args, **kw) def tobitmap(self, name="image"): - "Return image as an XBM bitmap" + """ + Returns the image converted to an X11 bitmap. + + .. note:: This method only works for mode "1" images. + + :param name: The name prefix to use for the bitmap variables. + :returns: A string containing an X11 bitmap. + :raises ValueError: If the mode is not "1" + """ self.load() if self.mode != "1": raise ValueError("not a bitmap") - data = self.tostring("xbm") - return string.join(["#define %s_width %d\n" % (name, self.size[0]), - "#define %s_height %d\n"% (name, self.size[1]), - "static char %s_bits[] = {\n" % name, data, "};"], "") + data = self.tobytes("xbm") + return b"".join([("#define %s_width %d\n" % (name, self.size[0])).encode('ascii'), + ("#define %s_height %d\n"% (name, self.size[1])).encode('ascii'), + ("static char %s_bits[] = {\n" % name).encode('ascii'), data, b"};"]) - ## - # Loads this image with pixel data from a string. - #

- # This method is similar to the {@link #fromstring} function, but - # loads data into this image instead of creating a new image - # object. + def frombytes(self, data, decoder_name="raw", *args): + """ + Loads this image with pixel data from a bytes object. - def fromstring(self, data, decoder_name="raw", *args): - "Load data to image from binary string" + This method is similar to the :py:func:`~PIL.Image.frombytes` function, + but loads data into this image instead of creating a new image object. + """ # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] # default format @@ -595,75 +614,87 @@ class Image: if s[1] != 0: raise ValueError("cannot decode image data") - ## - # Allocates storage for the image and loads the pixel data. In - # normal cases, you don't need to call this method, since the - # Image class automatically loads an opened image when it is - # accessed for the first time. - # - # @return An image access object. + def fromstring(self, *args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn('fromstring() is deprecated. Please call frombytes() instead.', DeprecationWarning) + return self.frombytes(*args, **kw) def load(self): - "Explicitly load pixel data." + """ + Allocates storage for the image and loads the pixel data. In + normal cases, you don't need to call this method, since the + Image class automatically loads an opened image when it is + accessed for the first time. + + :returns: An image access object. + """ if self.im and self.palette and self.palette.dirty: # realize palette - apply(self.im.putpalette, self.palette.getdata()) + self.im.putpalette(*self.palette.getdata()) self.palette.dirty = 0 self.palette.mode = "RGB" self.palette.rawmode = None - if self.info.has_key("transparency"): - self.im.putpalettealpha(self.info["transparency"], 0) + if "transparency" in self.info: + if isinstance(self.info["transparency"], int): + self.im.putpalettealpha(self.info["transparency"], 0) + else: + self.im.putpalettealphas(self.info["transparency"]) self.palette.mode = "RGBA" + if self.im: return self.im.pixel_access(self.readonly) - ## - # Verifies the contents of a file. For data read from a file, this - # method attempts to determine if the file is broken, without - # actually decoding the image data. If this method finds any - # problems, it raises suitable exceptions. If you need to load - # the image after using this method, you must reopen the image - # file. - def verify(self): - "Verify file contents." + """ + Verifies the contents of a file. For data read from a file, this + method attempts to determine if the file is broken, without + actually decoding the image data. If this method finds any + problems, it raises suitable exceptions. If you need to load + the image after using this method, you must reopen the image + file. + """ pass - ## - # Returns a converted copy of this image. For the "P" mode, this - # method translates pixels through the palette. If mode is - # omitted, a mode is chosen so that all information in the image - # and the palette can be represented without a palette. - #

- # The current version supports all possible conversions between - # "L", "RGB" and "CMYK." - #

- # When translating a colour image to black and white (mode "L"), - # the library uses the ITU-R 601-2 luma transform: - #

- # L = R * 299/1000 + G * 587/1000 + B * 114/1000 - #

- # When translating a greyscale image into a bilevel image (mode - # "1"), all non-zero values are set to 255 (white). To use other - # thresholds, use the {@link #Image.point} method. - # - # @def convert(mode, matrix=None, **options) - # @param mode The requested mode. - # @param matrix An optional conversion matrix. If given, this - # should be 4- or 16-tuple containing floating point values. - # @param options Additional options, given as keyword arguments. - # @keyparam dither Dithering method, used when converting from - # mode "RGB" to "P". - # Available methods are NONE or FLOYDSTEINBERG (default). - # @keyparam palette Palette to use when converting from mode "RGB" - # to "P". Available palettes are WEB or ADAPTIVE. - # @keyparam colors Number of colors to use for the ADAPTIVE palette. - # Defaults to 256. - # @return An Image object. - - def convert(self, mode=None, data=None, dither=None, + def convert(self, mode=None, matrix=None, dither=None, palette=WEB, colors=256): - "Convert to other pixel format" + """ + Returns a converted copy of this image. For the "P" mode, this + method translates pixels through the palette. If mode is + omitted, a mode is chosen so that all information in the image + and the palette can be represented without a palette. + + The current version supports all possible conversions between + "L", "RGB" and "CMYK." The **matrix** argument only supports "L" + and "RGB". + + When translating a color image to black and white (mode "L"), + the library uses the ITU-R 601-2 luma transform:: + + L = R * 299/1000 + G * 587/1000 + B * 114/1000 + + The default method of converting a greyscale ("L") or "RGB" + image into a bilevel (mode "1") image uses Floyd-Steinberg + dither to approximate the original image luminosity levels. If + dither is NONE, all non-zero values are set to 255 (white). To + use other thresholds, use the :py:meth:`~PIL.Image.Image.point` + method. + + :param mode: The requested mode. + :param matrix: An optional conversion matrix. If given, this + should be 4- or 16-tuple containing floating point values. + :param dither: Dithering method, used when converting from + mode "RGB" to "P" or from "RGB" or "L" to "1". + Available methods are NONE or FLOYDSTEINBERG (default). + :param palette: Palette to use when converting from mode "RGB" + to "P". Available palettes are WEB or ADAPTIVE. + :param colors: Number of colors to use for the ADAPTIVE palette. + Defaults to 256. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ if not mode: # determine default mode @@ -678,21 +709,25 @@ class Image: self.load() - if data: + if matrix: # matrix conversion if mode not in ("L", "RGB"): raise ValueError("illegal conversion") - im = self.im.convert_matrix(mode, data) + im = self.im.convert_matrix(mode, matrix) return self._new(im) if mode == "P" and palette == ADAPTIVE: im = self.im.quantize(colors) return self._new(im) - # colourspace conversion + # colorspace conversion if dither is None: dither = FLOYDSTEINBERG + # Use transparent conversion to promote from transparent color to an alpha channel. + if self.mode in ("L", "RGB") and mode == "RGBA" and "transparency" in self.info: + return self._new(self.im.convert_transparent(mode, self.info['transparency'])) + try: im = self.im.convert(mode, dither) except ValueError: @@ -710,6 +745,7 @@ class Image: # methods: # 0 = median cut # 1 = maximum coverage + # 2 = fast octree # NOTE: this functionality will be moved to the extended # quantizer interface in a later version of PIL. @@ -731,34 +767,33 @@ class Image: im = self.im.quantize(colors, method, kmeans) return self._new(im) - ## - # Copies this image. Use this method if you wish to paste things - # into an image, but still retain the original. - # - # @return An Image object. - def copy(self): - "Copy raster data" + """ + Copies this image. Use this method if you wish to paste things + into an image, but still retain the original. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ self.load() im = self.im.copy() return self._new(im) - ## - # Returns a rectangular region from this image. The box is a - # 4-tuple defining the left, upper, right, and lower pixel - # coordinate. - #

- # This is a lazy operation. Changes to the source image may or - # may not be reflected in the cropped image. To break the - # connection, call the {@link #Image.load} method on the cropped - # copy. - # - # @param The crop rectangle, as a (left, upper, right, lower)-tuple. - # @return An Image object. - def crop(self, box=None): - "Crop region from image" + """ + Returns a rectangular region from this image. The box is a + 4-tuple defining the left, upper, right, and lower pixel + coordinate. + + This is a lazy operation. Changes to the source image may or + may not be reflected in the cropped image. To break the + connection, call the :py:meth:`~PIL.Image.Image.load` method on + the cropped copy. + + :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. + :rtype: :py:class:`~PIL.Image.Image` + :returns: An :py:class:`~PIL.Image.Image` object. + """ self.load() if box is None: @@ -767,22 +802,23 @@ class Image: # lazy operation return _ImageCrop(self, box) - ## - # Configures the image file loader so it returns a version of the - # image that as closely as possible matches the given mode and - # size. For example, you can use this method to convert a colour - # JPEG to greyscale while loading it, or to extract a 128x192 - # version from a PCD file. - #

- # Note that this method modifies the Image object in place. If - # the image has already been loaded, this method has no effect. - # - # @param mode The requested mode. - # @param size The requested size. - def draft(self, mode, size): - "Configure image decoder" + """ + NYI + + Configures the image file loader so it returns a version of the + image that as closely as possible matches the given mode and + size. For example, you can use this method to convert a color + JPEG to greyscale while loading it, or to extract a 128x192 + version from a PCD file. + Note that this method modifies the :py:class:`~PIL.Image.Image` object + in place. If the image has already been loaded, this method has no + effect. + + :param mode: The requested mode. + :param size: The requested size. + """ pass def _expand(self, xmargin, ymargin=None): @@ -791,20 +827,17 @@ class Image: self.load() return self._new(self.im.expand(xmargin, ymargin, 0)) - ## - # Filters this image using the given filter. For a list of - # available filters, see the ImageFilter module. - # - # @param filter Filter kernel. - # @return An Image object. - # @see ImageFilter - def filter(self, filter): - "Apply environment filter to image" + """ + Filters this image using the given filter. For a list of + available filters, see the :py:mod:`~PIL.ImageFilter` module. + + :param filter: Filter kernel. + :returns: An :py:class:`~PIL.Image.Image` object. """ self.load() - if callable(filter): + if isinstance(filter, collections.Callable): filter = filter() if not hasattr(filter, "filter"): raise TypeError("filter argument should be ImageFilter.Filter instance or class") @@ -817,41 +850,39 @@ class Image: ims.append(self._new(filter.filter(self.im.getband(c)))) return merge(self.mode, ims) - ## - # Returns a tuple containing the name of each band in this image. - # For example, getbands on an RGB image returns ("R", "G", "B"). - # - # @return A tuple containing band names. - def getbands(self): - "Get band names" + """ + Returns a tuple containing the name of each band in this image. + For example, **getbands** on an RGB image returns ("R", "G", "B"). + :returns: A tuple containing band names. + :rtype: tuple + """ return ImageMode.getmode(self.mode).bands - ## - # Calculates the bounding box of the non-zero regions in the - # image. - # - # @return The bounding box is returned as a 4-tuple defining the - # left, upper, right, and lower pixel coordinate. If the image - # is completely empty, this method returns None. - def getbbox(self): - "Get bounding box of actual data (non-zero pixels) in image" + """ + Calculates the bounding box of the non-zero regions in the + image. + + :returns: The bounding box is returned as a 4-tuple defining the + left, upper, right, and lower pixel coordinate. If the image + is completely empty, this method returns None. + + """ self.load() return self.im.getbbox() - ## - # Returns a list of colors used in this image. - # - # @param maxcolors Maximum number of colors. If this number is - # exceeded, this method returns None. The default limit is - # 256 colors. - # @return An unsorted list of (count, pixel) values. - def getcolors(self, maxcolors=256): - "Get colors from image, up to given limit" + """ + Returns a list of colors used in this image. + + :param maxcolors: Maximum number of colors. If this number is + exceeded, this method returns None. The default limit is + 256 colors. + :returns: An unsorted list of (count, pixel) values. + """ self.load() if self.mode in ("1", "L", "P"): @@ -865,40 +896,38 @@ class Image: return out return self.im.getcolors(maxcolors) - ## - # Returns the contents of this image as a sequence object - # containing pixel values. The sequence object is flattened, so - # that values for line one follow directly after the values of - # line zero, and so on. - #

- # Note that the sequence object returned by this method is an - # internal PIL data type, which only supports certain sequence - # operations. To convert it to an ordinary sequence (e.g. for - # printing), use list(im.getdata()). - # - # @param band What band to return. The default is to return - # all bands. To return a single band, pass in the index - # value (e.g. 0 to get the "R" band from an "RGB" image). - # @return A sequence-like object. - def getdata(self, band = None): - "Get image data as sequence object." + """ + Returns the contents of this image as a sequence object + containing pixel values. The sequence object is flattened, so + that values for line one follow directly after the values of + line zero, and so on. + + Note that the sequence object returned by this method is an + internal PIL data type, which only supports certain sequence + operations. To convert it to an ordinary sequence (e.g. for + printing), use **list(im.getdata())**. + + :param band: What band to return. The default is to return + all bands. To return a single band, pass in the index + value (e.g. 0 to get the "R" band from an "RGB" image). + :returns: A sequence-like object. + """ self.load() if band is not None: return self.im.getband(band) return self.im # could be abused - ## - # Gets the the minimum and maximum pixel values for each band in - # the image. - # - # @return For a single-band image, a 2-tuple containing the - # minimum and maximum pixel value. For a multi-band image, - # a tuple containing one 2-tuple for each band. - def getextrema(self): - "Get min/max value" + """ + Gets the the minimum and maximum pixel values for each band in + the image. + + :returns: For a single-band image, a 2-tuple containing the + minimum and maximum pixel value. For a multi-band image, + a tuple containing one 2-tuple for each band. + """ self.load() if self.im.bands > 1: @@ -908,82 +937,78 @@ class Image: return tuple(extrema) return self.im.getextrema() - ## - # Returns a PyCObject that points to the internal image memory. - # - # @return A PyCObject object. - def getim(self): - "Get PyCObject pointer to internal image memory" + """ + Returns a capsule that points to the internal image memory. + + :returns: A capsule object. + """ self.load() return self.im.ptr - ## - # Returns the image palette as a list. - # - # @return A list of color values [r, g, b, ...], or None if the - # image has no palette. - def getpalette(self): - "Get palette contents." + """ + Returns the image palette as a list. + + :returns: A list of color values [r, g, b, ...], or None if the + image has no palette. + """ self.load() try: - return map(ord, self.im.getpalette()) + if bytes is str: + return [i8(c) for c in self.im.getpalette()] + else: + return list(self.im.getpalette()) except ValueError: return None # no palette - ## - # Returns the pixel value at a given position. - # - # @param xy The coordinate, given as (x, y). - # @return The pixel value. If the image is a multi-layer image, - # this method returns a tuple. - def getpixel(self, xy): - "Get pixel value" + """ + Returns the pixel value at a given position. + + :param xy: The coordinate, given as (x, y). + :returns: The pixel value. If the image is a multi-layer image, + this method returns a tuple. + """ self.load() return self.im.getpixel(xy) - ## - # Returns the horizontal and vertical projection. - # - # @return Two sequences, indicating where there are non-zero - # pixels along the X-axis and the Y-axis, respectively. - def getprojection(self): - "Get projection to x and y axes" + """ + Get projection to x and y axes + + :returns: Two sequences, indicating where there are non-zero + pixels along the X-axis and the Y-axis, respectively. + """ self.load() x, y = self.im.getprojection() - return map(ord, x), map(ord, y) - - ## - # Returns a histogram for the image. The histogram is returned as - # a list of pixel counts, one for each pixel value in the source - # image. If the image has more than one band, the histograms for - # all bands are concatenated (for example, the histogram for an - # "RGB" image contains 768 values). - #

- # A bilevel image (mode "1") is treated as a greyscale ("L") image - # by this method. - #

- # If a mask is provided, the method returns a histogram for those - # parts of the image where the mask image is non-zero. The mask - # image must have the same size as the image, and be either a - # bi-level image (mode "1") or a greyscale image ("L"). - # - # @def histogram(mask=None) - # @param mask An optional mask. - # @return A list containing pixel counts. + return [i8(c) for c in x], [i8(c) for c in y] def histogram(self, mask=None, extrema=None): - "Take histogram of image" + """ + Returns a histogram for the image. The histogram is returned as + a list of pixel counts, one for each pixel value in the source + image. If the image has more than one band, the histograms for + all bands are concatenated (for example, the histogram for an + "RGB" image contains 768 values). + A bilevel image (mode "1") is treated as a greyscale ("L") image + by this method. + + If a mask is provided, the method returns a histogram for those + parts of the image where the mask image is non-zero. The mask + image must have the same size as the image, and be either a + bi-level image (mode "1") or a greyscale image ("L"). + + :param mask: An optional mask. + :returns: A list containing pixel counts. + """ self.load() if mask: mask.load() @@ -994,70 +1019,69 @@ class Image: return self.im.histogram(extrema) return self.im.histogram() - ## - # (Deprecated) Returns a copy of the image where the data has been - # offset by the given distances. Data wraps around the edges. If - # yoffset is omitted, it is assumed to be equal to xoffset. - #

- # This method is deprecated. New code should use the offset - # function in the ImageChops module. - # - # @param xoffset The horizontal distance. - # @param yoffset The vertical distance. If omitted, both - # distances are set to the same value. - # @return An Image object. - def offset(self, xoffset, yoffset=None): - "(deprecated) Offset image in horizontal and/or vertical direction" + """ + .. deprecated:: 2.0 + + .. note:: New code should use :py:func:`PIL.ImageChops.offset`. + + Returns a copy of the image where the data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if warnings: warnings.warn( "'offset' is deprecated; use 'ImageChops.offset' instead", DeprecationWarning, stacklevel=2 ) - import ImageChops + from PIL import ImageChops return ImageChops.offset(self, xoffset, yoffset) - ## - # Pastes another image into this image. The box argument is either - # a 2-tuple giving the upper left corner, a 4-tuple defining the - # left, upper, right, and lower pixel coordinate, or None (same as - # (0, 0)). If a 4-tuple is given, the size of the pasted image - # must match the size of the region. - #

- # If the modes don't match, the pasted image is converted to the - # mode of this image (see the {@link #Image.convert} method for - # details). - #

- # Instead of an image, the source can be a integer or tuple - # containing pixel values. The method then fills the region - # with the given colour. When creating RGB images, you can - # also use colour strings as supported by the ImageColor module. - #

- # If a mask is given, this method updates only the regions - # indicated by the mask. You can use either "1", "L" or "RGBA" - # images (in the latter case, the alpha band is used as mask). - # Where the mask is 255, the given image is copied as is. Where - # the mask is 0, the current value is preserved. Intermediate - # values can be used for transparency effects. - #

- # Note that if you paste an "RGBA" image, the alpha band is - # ignored. You can work around this by using the same image as - # both source image and mask. - # - # @param im Source image or pixel value (integer or tuple). - # @param box An optional 4-tuple giving the region to paste into. - # If a 2-tuple is used instead, it's treated as the upper left - # corner. If omitted or None, the source is pasted into the - # upper left corner. - #

- # If an image is given as the second argument and there is no - # third, the box defaults to (0, 0), and the second argument - # is interpreted as a mask image. - # @param mask An optional mask image. - # @return An Image object. - def paste(self, im, box=None, mask=None): - "Paste other image into region" + """ + Pastes another image into this image. The box argument is either + a 2-tuple giving the upper left corner, a 4-tuple defining the + left, upper, right, and lower pixel coordinate, or None (same as + (0, 0)). If a 4-tuple is given, the size of the pasted image + must match the size of the region. + + If the modes don't match, the pasted image is converted to the mode of + this image (see the :py:meth:`~PIL.Image.Image.convert` method for + details). + + Instead of an image, the source can be a integer or tuple + containing pixel values. The method then fills the region + with the given color. When creating RGB images, you can + also use color strings as supported by the ImageColor module. + + If a mask is given, this method updates only the regions + indicated by the mask. You can use either "1", "L" or "RGBA" + images (in the latter case, the alpha band is used as mask). + Where the mask is 255, the given image is copied as is. Where + the mask is 0, the current value is preserved. Intermediate + values can be used for transparency effects. + + Note that if you paste an "RGBA" image, the alpha band is + ignored. You can work around this by using the same image as + both source image and mask. + + :param im: Source image or pixel value (integer or tuple). + :param box: An optional 4-tuple giving the region to paste into. + If a 2-tuple is used instead, it's treated as the upper left + corner. If omitted or None, the source is pasted into the + upper left corner. + + If an image is given as the second argument and there is no + third, the box defaults to (0, 0), and the second argument + is interpreted as a mask image. + :param mask: An optional mask image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if isImageType(box) and mask is None: # abbreviated paste(im, mask) syntax @@ -1081,7 +1105,7 @@ class Image: box = box + (box[0]+size[0], box[1]+size[1]) if isStringType(im): - import ImageColor + from PIL import ImageColor im = ImageColor.getcolor(im, self.mode) elif isImageType(im): @@ -1102,35 +1126,38 @@ class Image: else: self.im.paste(im, box) - ## - # Maps this image through a lookup table or function. - # - # @param lut A lookup table, containing 256 values per band in the - # image. A function can be used instead, it should take a single - # argument. The function is called once for each possible pixel - # value, and the resulting table is applied to all bands of the - # image. - # @param mode Output mode (default is same as input). In the - # current version, this can only be used if the source image - # has mode "L" or "P", and the output has mode "1". - # @return An Image object. - def point(self, lut, mode=None): - "Map image through lookup table" + """ + Maps this image through a lookup table or function. + + :param lut: A lookup table, containing 256 (or 65336 if + self.mode=="I" and mode == "L") values per band in the + image. A function can be used instead, it should take a + single argument. The function is called once for each + possible pixel value, and the resulting table is applied to + all bands of the image. + :param mode: Output mode (default is same as input). In the + current version, this can only be used if the source image + has mode "L" or "P", and the output has mode "1" or the + source image mode is "I" and the output mode is "L". + :returns: An :py:class:`~PIL.Image.Image` object. + """ self.load() if isinstance(lut, ImagePointHandler): return lut.point(self) - if not isSequenceType(lut): + if callable(lut): # if it isn't a list, it should be a function if self.mode in ("I", "I;16", "F"): # check if the function can be used with point_transform + # UNDONE wiredfool -- I think this prevents us from ever doing + # a gamma function point transform on > 8bit images. scale, offset = _getscaleoffset(lut) return self._new(self.im.point_transform(scale, offset)) # for other modes, convert the function to a table - lut = map(lut, range(256)) * self.im.bands + lut = [lut(i) for i in range(256)] * self.im.bands if self.mode == "F": # FIXME: _imaging returns a confusing error message for this case @@ -1138,17 +1165,16 @@ class Image: return self._new(self.im.point(lut, mode)) - ## - # Adds or replaces the alpha layer in this image. If the image - # does not have an alpha layer, it's converted to "LA" or "RGBA". - # The new layer must be either "L" or "1". - # - # @param im The new alpha layer. This can either be an "L" or "1" - # image having the same size as this image, or an integer or - # other color value. - def putalpha(self, alpha): - "Set alpha layer" + """ + Adds or replaces the alpha layer in this image. If the image + does not have an alpha layer, it's converted to "LA" or "RGBA". + The new layer must be either "L" or "1". + + :param alpha: The new alpha layer. This can either be an "L" or "1" + image having the same size as this image, or an integer or + other color value. + """ self.load() if self.readonly: @@ -1194,19 +1220,18 @@ class Image: self.im.putband(alpha.im, band) - ## - # Copies pixel data to this image. This method copies data from a - # sequence object into the image, starting at the upper left - # corner (0, 0), and continuing until either the image or the - # sequence ends. The scale and offset values are used to adjust - # the sequence values: pixel = value*scale + offset. - # - # @param data A sequence object. - # @param scale An optional scale value. The default is 1.0. - # @param offset An optional offset value. The default is 0.0. - def putdata(self, data, scale=1.0, offset=0.0): - "Put data from a sequence object into an image." + """ + Copies pixel data to this image. This method copies data from a + sequence object into the image, starting at the upper left + corner (0, 0), and continuing until either the image or the + sequence ends. The scale and offset values are used to adjust + the sequence values: **pixel = value*scale + offset**. + + :param data: A sequence object. + :param scale: An optional scale value. The default is 1.0. + :param offset: An optional offset value. The default is 0.0. + """ self.load() if self.readonly: @@ -1214,19 +1239,18 @@ class Image: self.im.putdata(data, scale, offset) - ## - # Attaches a palette to this image. The image must be a "P" or - # "L" image, and the palette sequence must contain 768 integer - # values, where each group of three values represent the red, - # green, and blue values for the corresponding pixel - # index. Instead of an integer sequence, you can use an 8-bit - # string. - # - # @def putpalette(data) - # @param data A palette sequence (either a list or a string). - def putpalette(self, data, rawmode="RGB"): - "Put palette data into an image." + """ + Attaches a palette to this image. The image must be a "P" or + "L" image, and the palette sequence must contain 768 integer + values, where each group of three values represent the red, + green, and blue values for the corresponding pixel + index. Instead of an integer sequence, you can use an 8-bit + string. + + :param data: A palette sequence (either a list or a string). + """ + from PIL import ImagePalette if self.mode not in ("L", "P"): raise ValueError("illegal image mode") @@ -1234,31 +1258,36 @@ class Image: if isinstance(data, ImagePalette.ImagePalette): palette = ImagePalette.raw(data.rawmode, data.palette) else: - if not isStringType(data): - data = string.join(map(chr, data), "") + if not isinstance(data, bytes): + if bytes is str: + data = "".join(chr(x) for x in data) + else: + data = bytes(data) palette = ImagePalette.raw(rawmode, data) self.mode = "P" self.palette = palette self.palette.mode = "RGB" self.load() # install new palette - ## - # Modifies the pixel at the given position. The colour is given as - # a single numerical value for single-band images, and a tuple for - # multi-band images. - #

- # Note that this method is relatively slow. For more extensive - # changes, use {@link #Image.paste} or the ImageDraw module - # instead. - # - # @param xy The pixel coordinate, given as (x, y). - # @param value The pixel value. - # @see #Image.paste - # @see #Image.putdata - # @see ImageDraw - def putpixel(self, xy, value): - "Set pixel value" + """ + Modifies the pixel at the given position. The color is given as + a single numerical value for single-band images, and a tuple for + multi-band images. + + Note that this method is relatively slow. For more extensive changes, + use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` + module instead. + + See: + + * :py:meth:`~PIL.Image.Image.paste` + * :py:meth:`~PIL.Image.Image.putdata` + * :py:mod:`~PIL.ImageDraw` + + :param xy: The pixel coordinate, given as (x, y). + :param value: The pixel value. + """ self.load() if self.readonly: @@ -1266,23 +1295,22 @@ class Image: return self.im.putpixel(xy, value) - ## - # Returns a resized copy of this image. - # - # @def resize(size, filter=NEAREST) - # @param size The requested size in pixels, as a 2-tuple: - # (width, height). - # @param filter An optional resampling filter. This can be - # one of NEAREST (use nearest neighbour), BILINEAR - # (linear interpolation in a 2x2 environment), BICUBIC - # (cubic spline interpolation in a 4x4 environment), or - # ANTIALIAS (a high-quality downsampling filter). - # If omitted, or if the image has mode "1" or "P", it is - # set NEAREST. - # @return An Image object. - def resize(self, size, resample=NEAREST): - "Resize image" + """ + Returns a resized copy of this image. + + :param size: The requested size in pixels, as a 2-tuple: + (width, height). + :param resample: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), :py:attr:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment), or + :py:attr:`PIL.Image.ANTIALIAS` (a high-quality downsampling filter). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS): raise ValueError("unknown resampling filter") @@ -1292,6 +1320,9 @@ class Image: if self.mode in ("1", "P"): resample = NEAREST + if self.mode == 'RGBA': + return self.convert('RGBa').resize(size, resample).convert('RGBA') + if resample == ANTIALIAS: # requires stretch support (imToolkit & PIL 1.1.3) try: @@ -1303,27 +1334,26 @@ class Image: return self._new(im) - ## - # Returns a rotated copy of this image. This method returns a - # copy of this image, rotated the given number of degrees counter - # clockwise around its centre. - # - # @def rotate(angle, filter=NEAREST) - # @param angle In degrees counter clockwise. - # @param filter An optional resampling filter. This can be - # one of NEAREST (use nearest neighbour), BILINEAR - # (linear interpolation in a 2x2 environment), or BICUBIC - # (cubic spline interpolation in a 4x4 environment). - # If omitted, or if the image has mode "1" or "P", it is - # set NEAREST. - # @param expand Optional expansion flag. If true, expands the output - # image to make it large enough to hold the entire rotated image. - # If false or omitted, make the output image the same size as the - # input image. - # @return An Image object. - def rotate(self, angle, resample=NEAREST, expand=0): - "Rotate image. Angle given as degrees counter-clockwise." + """ + Returns a rotated copy of this image. This method returns a + copy of this image, rotated the given number of degrees counter + clockwise around its centre. + + :param angle: In degrees counter clockwise. + :param filter: An optional resampling filter. This can be + one of :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` + (cubic spline interpolation in a 4x4 environment). + If omitted, or if the image has mode "1" or "P", it is + set :py:attr:`PIL.Image.NEAREST`. + :param expand: Optional expansion flag. If true, expands the output + image to make it large enough to hold the entire rotated image. + If false or omitted, make the output image the same size as the + input image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if expand: import math @@ -1332,7 +1362,8 @@ class Image: math.cos(angle), math.sin(angle), 0.0, -math.sin(angle), math.cos(angle), 0.0 ] - def transform(x, y, (a, b, c, d, e, f)=matrix): + def transform(x, y, matrix=matrix): + (a, b, c, d, e, f) = matrix return a*x + b*y + c, d*x + e*y + f # calculate output size @@ -1363,41 +1394,39 @@ class Image: return self._new(self.im.rotate(angle, resample)) - ## - # Saves this image under the given filename. If no format is - # specified, the format to use is determined from the filename - # extension, if possible. - #

- # Keyword options can be used to provide additional instructions - # to the writer. If a writer doesn't recognise an option, it is - # silently ignored. The available options are described later in - # this handbook. - #

- # You can use a file object instead of a filename. In this case, - # you must always specify the format. The file object must - # implement the seek, tell, and write - # methods, and be opened in binary mode. - # - # @def save(file, format=None, **options) - # @param file File name or file object. - # @param format Optional format override. If omitted, the - # format to use is determined from the filename extension. - # If a file object was used instead of a filename, this - # parameter should always be used. - # @param **options Extra parameters to the image writer. - # @return None - # @exception KeyError If the output format could not be determined - # from the file name. Use the format option to solve this. - # @exception IOError If the file could not be written. The file - # may have been created, and may contain partial data. - def save(self, fp, format=None, **params): - "Save image to file or stream" + """ + Saves this image under the given filename. If no format is + specified, the format to use is determined from the filename + extension, if possible. - if isStringType(fp): + Keyword options can be used to provide additional instructions + to the writer. If a writer doesn't recognise an option, it is + silently ignored. The available options are described later in + this handbook. + + You can use a file object instead of a filename. In this case, + you must always specify the format. The file object must + implement the **seek**, **tell**, and **write** + methods, and be opened in binary mode. + + :param file: File name or file object. + :param format: Optional format override. If omitted, the + format to use is determined from the filename extension. + If a file object was used instead of a filename, this + parameter should always be used. + :param options: Extra parameters to the image writer. + :returns: None + :exception KeyError: If the output format could not be determined + from the file name. Use the format option to solve this. + :exception IOError: If the file could not be written. The file + may have been created, and may contain partial data. + """ + + if isPath(fp): filename = fp else: - if hasattr(fp, "name") and isStringType(fp.name): + if hasattr(fp, "name") and isPath(fp.name): filename = fp.name else: filename = "" @@ -1410,7 +1439,7 @@ class Image: preinit() - ext = string.lower(os.path.splitext(filename)[1]) + ext = os.path.splitext(filename)[1].lower() if not format: try: @@ -1423,14 +1452,13 @@ class Image: raise KeyError(ext) # unknown extension try: - save_handler = SAVE[string.upper(format)] + save_handler = SAVE[format.upper()] except KeyError: init() - save_handler = SAVE[string.upper(format)] # unknown format + save_handler = SAVE[format.upper()] # unknown format - if isStringType(fp): - import __builtin__ - fp = __builtin__.open(fp, "wb") + if isPath(fp): + fp = builtins.open(fp, "wb") close = 1 else: close = 0 @@ -1442,113 +1470,108 @@ class Image: if close: fp.close() - ## - # Seeks to the given frame in this sequence file. If you seek - # beyond the end of the sequence, the method raises an - # EOFError exception. When a sequence file is opened, the - # library automatically seeks to frame 0. - #

- # Note that in the current version of the library, most sequence - # formats only allows you to seek to the next frame. - # - # @param frame Frame number, starting at 0. - # @exception EOFError If the call attempts to seek beyond the end - # of the sequence. - # @see #Image.tell - def seek(self, frame): - "Seek to given frame in sequence file" + """ + Seeks to the given frame in this sequence file. If you seek + beyond the end of the sequence, the method raises an + **EOFError** exception. When a sequence file is opened, the + library automatically seeks to frame 0. + + Note that in the current version of the library, most sequence + formats only allows you to seek to the next frame. + + See :py:meth:`~PIL.Image.Image.tell`. + + :param frame: Frame number, starting at 0. + :exception EOFError: If the call attempts to seek beyond the end + of the sequence. + """ # overridden by file handlers if frame != 0: raise EOFError - ## - # Displays this image. This method is mainly intended for - # debugging purposes. - #

- # On Unix platforms, this method saves the image to a temporary - # PPM file, and calls the xv utility. - #

- # On Windows, it saves the image to a temporary BMP file, and uses - # the standard BMP display utility to show it (usually Paint). - # - # @def show(title=None) - # @param title Optional title to use for the image window, - # where possible. - def show(self, title=None, command=None): - "Display image (for debug purposes only)" + """ + Displays this image. This method is mainly intended for + debugging purposes. + + On Unix platforms, this method saves the image to a temporary + PPM file, and calls the **xv** utility. + + On Windows, it saves the image to a temporary BMP file, and uses + the standard BMP display utility to show it (usually Paint). + + :param title: Optional title to use for the image window, + where possible. + :param command: command used to show the image + """ _show(self, title=title, command=command) - ## - # Split this image into individual bands. This method returns a - # tuple of individual image bands from an image. For example, - # splitting an "RGB" image creates three new images each - # containing a copy of one of the original bands (red, green, - # blue). - # - # @return A tuple containing bands. - def split(self): - "Split image into bands" + """ + Split this image into individual bands. This method returns a + tuple of individual image bands from an image. For example, + splitting an "RGB" image creates three new images each + containing a copy of one of the original bands (red, green, + blue). + :returns: A tuple containing bands. + """ + + self.load() if self.im.bands == 1: ims = [self.copy()] else: ims = [] - self.load() for i in range(self.im.bands): ims.append(self._new(self.im.getband(i))) return tuple(ims) - ## - # Returns the current frame number. - # - # @return Frame number, starting with 0. - # @see #Image.seek - def tell(self): - "Return current frame number" + """ + Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. + :returns: Frame number, starting with 0. + """ return 0 - ## - # Make this image into a thumbnail. This method modifies the - # image to contain a thumbnail version of itself, no larger than - # the given size. This method calculates an appropriate thumbnail - # size to preserve the aspect of the image, calls the {@link - # #Image.draft} method to configure the file reader (where - # applicable), and finally resizes the image. - #

- # Note that the bilinear and bicubic filters in the current - # version of PIL are not well-suited for thumbnail generation. - # You should use ANTIALIAS unless speed is much more - # important than quality. - #

- # Also note that this function modifies the Image object in place. - # If you need to use the full resolution image as well, apply this - # method to a {@link #Image.copy} of the original image. - # - # @param size Requested size. - # @param resample Optional resampling filter. This can be one - # of NEAREST, BILINEAR, BICUBIC, or - # ANTIALIAS (best quality). If omitted, it defaults - # to NEAREST (this will be changed to ANTIALIAS in a - # future version). - # @return None - def thumbnail(self, size, resample=NEAREST): - "Create thumbnail representation (modifies image in place)" + """ + Make this image into a thumbnail. This method modifies the + image to contain a thumbnail version of itself, no larger than + the given size. This method calculates an appropriate thumbnail + size to preserve the aspect of the image, calls the + :py:meth:`~PIL.Image.Image.draft` method to configure the file reader + (where applicable), and finally resizes the image. + + Note that the bilinear and bicubic filters in the current + version of PIL are not well-suited for thumbnail generation. + You should use :py:attr:`PIL.Image.ANTIALIAS` unless speed is much more + important than quality. + + Also note that this function modifies the :py:class:`~PIL.Image.Image` + object in place. If you need to use the full resolution image as well, apply + this method to a :py:meth:`~PIL.Image.Image.copy` of the original image. + + :param size: Requested size. + :param resample: Optional resampling filter. This can be one + of :py:attr:`PIL.Image.NEAREST`, :py:attr:`PIL.Image.BILINEAR`, + :py:attr:`PIL.Image.BICUBIC`, or :py:attr:`PIL.Image.ANTIALIAS` + (best quality). If omitted, it defaults to + :py:attr:`PIL.Image.NEAREST` (this will be changed to ANTIALIAS in a + future version). + :returns: None + """ # FIXME: the default resampling filter will be changed # to ANTIALIAS in future versions # preserve aspect ratio x, y = self.size - if x > size[0]: y = max(y * size[0] / x, 1); x = size[0] - if y > size[1]: x = max(x * size[1] / y, 1); y = size[1] + if x > size[0]: y = int(max(y * size[0] / x, 1)); x = int(size[0]) + if y > size[1]: x = int(max(x * size[1] / y, 1)); y = int(size[1]) size = x, y if size == self.size: @@ -1573,31 +1596,32 @@ class Image: # FIXME: the different tranform methods need further explanation # instead of bloating the method docs, add a separate chapter. - - ## - # Transforms this image. This method creates a new image with the - # given size, and the same mode as the original, and copies data - # to the new image using the given transform. - #

- # @def transform(size, method, data, resample=NEAREST) - # @param size The output size. - # @param method The transformation method. This is one of - # EXTENT (cut out a rectangular subregion), AFFINE - # (affine transform), PERSPECTIVE (perspective - # transform), QUAD (map a quadrilateral to a - # rectangle), or MESH (map a number of source quadrilaterals - # in one operation). - # @param data Extra data to the transformation method. - # @param resample Optional resampling filter. It can be one of - # NEAREST (use nearest neighbour), BILINEAR - # (linear interpolation in a 2x2 environment), or - # BICUBIC (cubic spline interpolation in a 4x4 - # environment). If omitted, or if the image has mode - # "1" or "P", it is set to NEAREST. - # @return An Image object. - def transform(self, size, method, data=None, resample=NEAREST, fill=1): - "Transform image" + """ + Transforms this image. This method creates a new image with the + given size, and the same mode as the original, and copies data + to the new image using the given transform. + + :param size: The output size. + :param method: The transformation method. This is one of + :py:attr:`PIL.Image.EXTENT` (cut out a rectangular subregion), + :py:attr:`PIL.Image.AFFINE` (affine transform), + :py:attr:`PIL.Image.PERSPECTIVE` (perspective transform), + :py:attr:`PIL.Image.QUAD` (map a quadrilateral to a rectangle), or + :py:attr:`PIL.Image.MESH` (map a number of source quadrilaterals + in one operation). + :param data: Extra data to the transformation method. + :param resample: Optional resampling filter. It can be one of + :py:attr:`PIL.Image.NEAREST` (use nearest neighbour), + :py:attr:`PIL.Image.BILINEAR` (linear interpolation in a 2x2 + environment), or :py:attr:`PIL.Image.BICUBIC` (cubic spline + interpolation in a 4x4 environment). If omitted, or if the image + has mode "1" or "P", it is set to :py:attr:`PIL.Image.NEAREST`. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if self.mode == 'RGBA': + return self.convert('RGBa').transform(size, method, data, resample, fill).convert('RGBA') if isinstance(method, ImageTransformHandler): return method.transform(size, self, resample=resample, fill=fill) @@ -1606,6 +1630,7 @@ class Image: method, data = method.getdata() if data is None: raise ValueError("missing method data") + im = new(self.mode, size, None) if method == MESH: # list of quads @@ -1664,14 +1689,15 @@ class Image: self.im.transform2(box, image.im, method, data, resample, fill) - ## - # Returns a flipped or rotated copy of this image. - # - # @param method One of FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, - # ROTATE_90, ROTATE_180, or ROTATE_270. - def transpose(self, method): - "Transpose image (flip or rotate in 90 degree steps)" + """ + Transpose image (flip or rotate in 90 degree steps) + + :param method: One of :py:attr:`PIL.Image.FLIP_LEFT_RIGHT`, + :py:attr:`PIL.Image.FLIP_TOP_BOTTOM`, :py:attr:`PIL.Image.ROTATE_90`, + :py:attr:`PIL.Image.ROTATE_180`, or :py:attr:`PIL.Image.ROTATE_270`. + :returns: Returns a flipped or rotated copy of this image. + """ self.load() im = self.im.transpose(method) @@ -1734,21 +1760,21 @@ def _wedge(): return Image()._new(core.wedge("L")) -## -# Creates a new image with the given mode and size. -# -# @param mode The mode to use for the new image. -# @param size A 2-tuple, containing (width, height) in pixels. -# @param color What colour to use for the image. Default is black. -# If given, this should be a single integer or floating point value -# for single-band modes, and a tuple for multi-band modes (one value -# per band). When creating RGB images, you can also use colour -# strings as supported by the ImageColor module. If the colour is -# None, the image is not initialised. -# @return An Image object. def new(mode, size, color=0): - "Create a new image" + """ + Creates a new image with the given mode and size. + + :param mode: The mode to use for the new image. + :param size: A 2-tuple, containing (width, height) in pixels. + :param color: What color to use for the image. Default is black. + If given, this should be a single integer or floating point value + for single-band modes, and a tuple for multi-band modes (one value + per band). When creating RGB images, you can also use color + strings as supported by the ImageColor module. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if color is None: # don't initialize @@ -1757,84 +1783,98 @@ def new(mode, size, color=0): if isStringType(color): # css3-style specifier - import ImageColor + from PIL import ImageColor color = ImageColor.getcolor(color, mode) return Image()._new(core.fill(mode, size, color)) -## -# Creates an image memory from pixel data in a string. -#

-# In its simplest form, this function takes three arguments -# (mode, size, and unpacked pixel data). -#

-# You can also use any pixel decoder supported by PIL. For more -# information on available decoders, see the section Writing Your Own File Decoder. -#

-# Note that this function decodes pixel data only, not entire images. -# If you have an entire image in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. -# -# @param mode The image mode. -# @param size The image size. -# @param data An 8-bit string containing raw data for the given mode. -# @param decoder_name What decoder to use. -# @param *args Additional parameters for the given decoder. -# @return An Image object. -def fromstring(mode, size, data, decoder_name="raw", *args): - "Load image from string" +def frombytes(mode, size, data, decoder_name="raw", *args): + """ + Creates a copy of an image memory from pixel data in a buffer. + + In its simplest form, this function takes three arguments + (mode, size, and unpacked pixel data). + + You can also use any pixel decoder supported by PIL. For more + information on available decoders, see the section + **Writing Your Own File Decoder**. + + Note that this function decodes pixel data only, not entire images. + If you have an entire image in a string, wrap it in a + :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load + it. + + :param mode: The image mode. + :param size: The image size. + :param data: A byte buffer containing raw data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. + :returns: An :py:class:`~PIL.Image.Image` object. + """ # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw" and args == (): args = mode im = new(mode, size) - im.fromstring(data, decoder_name, args) + im.frombytes(data, decoder_name, args) return im -## -# (New in 1.1.4) Creates an image memory from pixel data in a string -# or byte buffer. -#

-# This function is similar to {@link #fromstring}, but uses data in -# the byte buffer, where possible. This means that changes to the -# original buffer object are reflected in this image). Not all modes -# can share memory; supported modes include "L", "RGBX", "RGBA", and -# "CMYK". -#

-# Note that this function decodes pixel data only, not entire images. -# If you have an entire image file in a string, wrap it in a -# StringIO object, and use {@link #open} to load it. -#

-# In the current version, the default parameters used for the "raw" -# decoder differs from that used for {@link fromstring}. This is a -# bug, and will probably be fixed in a future release. The current -# release issues a warning if you do this; to disable the warning, -# you should provide the full set of parameters. See below for -# details. -# -# @param mode The image mode. -# @param size The image size. -# @param data An 8-bit string or other buffer object containing raw -# data for the given mode. -# @param decoder_name What decoder to use. -# @param *args Additional parameters for the given decoder. For the -# default encoder ("raw"), it's recommended that you provide the -# full set of parameters: -# frombuffer(mode, size, data, "raw", mode, 0, 1). -# @return An Image object. -# @since 1.1.4 +def fromstring(*args, **kw): + """Deprecated alias to frombytes. + + .. deprecated:: 2.0 + """ + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return frombytes(*args, **kw) + def frombuffer(mode, size, data, decoder_name="raw", *args): - "Load image from string or buffer" + """ + Creates an image memory referencing pixel data in a byte buffer. + + This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data + in the byte buffer, where possible. This means that changes to the + original buffer object are reflected in this image). Not all modes can + share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". + + Note that this function decodes pixel data only, not entire images. + If you have an entire image file in a string, wrap it in a + **BytesIO** object, and use :py:func:`~PIL.Image.open` to load it. + + In the current version, the default parameters used for the "raw" decoder + differs from that used for :py:func:`~PIL.Image.fromstring`. This is a + bug, and will probably be fixed in a future release. The current release + issues a warning if you do this; to disable the warning, you should provide + the full set of parameters. See below for details. + + :param mode: The image mode. + :param size: The image size. + :param data: A bytes or other buffer object containing raw + data for the given mode. + :param decoder_name: What decoder to use. + :param args: Additional parameters for the given decoder. For the + default encoder ("raw"), it's recommended that you provide the + full set of parameters:: + + frombuffer(mode, size, data, "raw", mode, 0, 1) + + :returns: An :py:class:`~PIL.Image.Image` object. + + .. versionadded:: 1.1.4 + """ + "Load image from bytes or buffer" # may pass tuple instead of argument list - if len(args) == 1 and isTupleType(args[0]): + if len(args) == 1 and isinstance(args[0], tuple): args = args[0] if decoder_name == "raw": @@ -1855,21 +1895,23 @@ def frombuffer(mode, size, data, decoder_name="raw", *args): im.readonly = 1 return im - return fromstring(mode, size, data, decoder_name, args) + return frombytes(mode, size, data, decoder_name, args) -## -# (New in 1.1.6) Creates an image memory from an object exporting -# the array interface (using the buffer protocol). -# -# If obj is not contiguous, then the tostring method is called -# and {@link frombuffer} is used. -# -# @param obj Object with array interface -# @param mode Mode to use (will be determined from type if None) -# @return An image memory. - def fromarray(obj, mode=None): + """ + Creates an image memory from an object exporting the array interface + (using the buffer protocol). + + If obj is not contiguous, then the tobytes method is called + and :py:func:`~PIL.Image.frombuffer` is used. + + :param obj: Object with array interface + :param mode: Mode to use (will be determined from type if None) + :returns: An image memory. + + .. versionadded:: 1.1.6 + """ arr = obj.__array_interface__ shape = arr['shape'] ndim = len(shape) @@ -1897,7 +1939,10 @@ def fromarray(obj, mode=None): size = shape[1], shape[0] if strides is not None: - obj = obj.tostring() + if hasattr(obj, 'tobytes'): + obj = obj.tobytes() + else: + obj = obj.tostring() return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) @@ -1923,33 +1968,31 @@ _fromarray_typemap = { _fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") _fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") -## -# Opens and identifies the given image file. -#

-# This is a lazy operation; this function identifies the file, but the -# actual image data is not read from the file until you try to process -# the data (or call the {@link #Image.load} method). -# -# @def open(file, mode="r") -# @param file A filename (string) or a file object. The file object -# must implement read, seek, and tell methods, -# and be opened in binary mode. -# @param mode The mode. If given, this argument must be "r". -# @return An Image object. -# @exception IOError If the file cannot be found, or the image cannot be -# opened and identified. -# @see #new def open(fp, mode="r"): - "Open an image file, without loading the raster data" + """ + Opens and identifies the given image file. + + This is a lazy operation; this function identifies the file, but the + actual image data is not read from the file until you try to process + the data (or call the :py:meth:`~PIL.Image.Image.load` method). + See :py:func:`~PIL.Image.new`. + + :param file: A filename (string) or a file object. The file object + must implement :py:meth:`~file.read`, :py:meth:`~file.seek`, and + :py:meth:`~file.tell` methods, and be opened in binary mode. + :param mode: The mode. If given, this argument must be "r". + :returns: An :py:class:`~PIL.Image.Image` object. + :exception IOError: If the file cannot be found, or the image cannot be + opened and identified. + """ if mode != "r": raise ValueError("bad mode") - if isStringType(fp): - import __builtin__ + if isPath(fp): filename = fp - fp = __builtin__.open(fp, "rb") + fp = builtins.open(fp, "rb") else: filename = "" @@ -1964,6 +2007,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass if init(): @@ -1975,6 +2020,8 @@ def open(fp, mode="r"): fp.seek(0) return factory(fp, filename) except (SyntaxError, IndexError, TypeError): + #import traceback + #traceback.print_exc() pass raise IOError("cannot identify image file") @@ -1982,77 +2029,87 @@ def open(fp, mode="r"): # # Image processing. -## -# Creates a new image by interpolating between two input images, using -# a constant alpha. -# -#

-#    out = image1 * (1.0 - alpha) + image2 * alpha
-# 
-# -# @param im1 The first image. -# @param im2 The second image. Must have the same mode and size as -# the first image. -# @param alpha The interpolation alpha factor. If alpha is 0.0, a -# copy of the first image is returned. If alpha is 1.0, a copy of -# the second image is returned. There are no restrictions on the -# alpha value. If necessary, the result is clipped to fit into -# the allowed output range. -# @return An Image object. +def alpha_composite(im1, im2): + """ + Alpha composite im2 over im1. + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + im1.load() + im2.load() + return im1._new(core.alpha_composite(im1.im, im2.im)) + def blend(im1, im2, alpha): - "Interpolate between images." + """ + Creates a new image by interpolating between two input images, using + a constant alpha.:: + + out = image1 * (1.0 - alpha) + image2 * alpha + + :param im1: The first image. + :param im2: The second image. Must have the same mode and size as + the first image. + :param alpha: The interpolation alpha factor. If alpha is 0.0, a + copy of the first image is returned. If alpha is 1.0, a copy of + the second image is returned. There are no restrictions on the + alpha value. If necessary, the result is clipped to fit into + the allowed output range. + :returns: An :py:class:`~PIL.Image.Image` object. + """ im1.load() im2.load() return im1._new(core.blend(im1.im, im2.im, alpha)) -## -# Creates a new image by interpolating between two input images, -# using the mask as alpha. -# -# @param image1 The first image. -# @param image2 The second image. Must have the same mode and -# size as the first image. -# @param mask A mask image. This image can can have mode -# "1", "L", or "RGBA", and must have the same size as the -# other two images. def composite(image1, image2, mask): - "Create composite image by blending images using a transparency mask" + """ + Create composite image by blending images using a transparency mask. + + :param image1: The first image. + :param image2: The second image. Must have the same mode and + size as the first image. + :param mask: A mask image. This image can can have mode + "1", "L", or "RGBA", and must have the same size as the + other two images. + """ image = image2.copy() image.paste(image1, None, mask) return image -## -# Applies the function (which should take one argument) to each pixel -# in the given image. If the image has more than one band, the same -# function is applied to each band. Note that the function is -# evaluated once for each possible pixel value, so you cannot use -# random components or other generators. -# -# @def eval(image, function) -# @param image The input image. -# @param function A function object, taking one integer argument. -# @return An Image object. def eval(image, *args): - "Evaluate image expression" + """ + Applies the function (which should take one argument) to each pixel + in the given image. If the image has more than one band, the same + function is applied to each band. Note that the function is + evaluated once for each possible pixel value, so you cannot use + random components or other generators. + + :param image: The input image. + :param function: A function object, taking one integer argument. + :returns: An :py:class:`~PIL.Image.Image` object. + """ return image.point(args[0]) -## -# Creates a new image from a number of single-band images. -# -# @param mode The mode to use for the output image. -# @param bands A sequence containing one single-band image for -# each band in the output image. All bands must have the -# same size. -# @return An Image object. def merge(mode, bands): - "Merge a set of single band images into a new multiband image." + """ + Merge a set of single band images into a new multiband image. + + :param mode: The mode to use for the output image. + :param bands: A sequence containing one single-band image for + each band in the output image. All bands must have the + same size. + :returns: An :py:class:`~PIL.Image.Image` object. + """ if getmodebands(mode) != len(bands) or "*" in mode: raise ValueError("wrong number of bands") @@ -2070,49 +2127,52 @@ def merge(mode, bands): # -------------------------------------------------------------------- # Plugin registry -## -# Register an image file plugin. This function should not be used -# in application code. -# -# @param id An image format identifier. -# @param factory An image file factory method. -# @param accept An optional function that can be used to quickly -# reject images having another format. - def register_open(id, factory, accept=None): - id = string.upper(id) + """ + Register an image file plugin. This function should not be used + in application code. + + :param id: An image format identifier. + :param factory: An image file factory method. + :param accept: An optional function that can be used to quickly + reject images having another format. + """ + id = id.upper() ID.append(id) OPEN[id] = factory, accept -## -# Registers an image MIME type. This function should not be used -# in application code. -# -# @param id An image format identifier. -# @param mimetype The image MIME type for this format. def register_mime(id, mimetype): - MIME[string.upper(id)] = mimetype + """ + Registers an image MIME type. This function should not be used + in application code. + + :param id: An image format identifier. + :param mimetype: The image MIME type for this format. + """ + MIME[id.upper()] = mimetype -## -# Registers an image save function. This function should not be -# used in application code. -# -# @param id An image format identifier. -# @param driver A function to save images in this format. def register_save(id, driver): - SAVE[string.upper(id)] = driver + """ + Registers an image save function. This function should not be + used in application code. + + :param id: An image format identifier. + :param driver: A function to save images in this format. + """ + SAVE[id.upper()] = driver -## -# Registers an image extension. This function should not be -# used in application code. -# -# @param id An image format identifier. -# @param extension An extension used for this format. def register_extension(id, extension): - EXTENSION[string.lower(extension)] = string.upper(id) + """ + Registers an image extension. This function should not be + used in application code. + + :param id: An image format identifier. + :param extension: An extension used for this format. + """ + EXTENSION[extension.lower()] = id.upper() # -------------------------------------------------------------------- @@ -2120,8 +2180,8 @@ def register_extension(id, extension): def _show(image, **options): # override me, as necessary - apply(_showxv, (image,), options) + _showxv(image, **options) def _showxv(image, title=None, **options): - import ImageShow - apply(ImageShow.show, (image, title), options) + from PIL import ImageShow + ImageShow.show(image, title, **options) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py index 82861fc7a..ba5350e02 100644 --- a/PIL/ImageChops.py +++ b/PIL/ImageChops.py @@ -15,287 +15,268 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image -## -# The ImageChops module contains a number of arithmetical image -# operations, called channel operations ("chops"). These can be -# used for various purposes, including special effects, image -# compositions, algorithmic painting, and more. -#

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

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

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

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

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

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

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

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

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

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

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

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

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

-# Returns a copy of the image where data has been offset by the given -# distances. Data wraps around the edges. If yoffset is omitted, it -# is assumed to be equal to xoffset. -# -# @param image Source image. -# @param xoffset The horizontal distance. -# @param yoffset The vertical distance. If omitted, both -# distances are set to the same value. -# @return An Image object. def offset(image, xoffset, yoffset=None): - "Offset image in horizontal and/or vertical direction" + """Returns a copy of the image where data has been offset by the given + distances. Data wraps around the edges. If **yoffset** is omitted, it + is assumed to be equal to **xoffset**. + + :param xoffset: The horizontal distance. + :param yoffset: The vertical distance. If omitted, both + distances are set to the same value. + :rtype: :py:class:`~PIL.Image.Image` + """ + if yoffset is None: yoffset = xoffset image.load() diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py index b8a6dca71..20ba6a11f 100644 --- a/PIL/ImageCms.py +++ b/PIL/ImageCms.py @@ -15,6 +15,8 @@ # below for the original description. # +from __future__ import print_function + DESCRIPTION = """ pyCMS @@ -40,6 +42,8 @@ pyCMS Version History: + 1.0.0 pil Oct 2013 Port to LCMS 2. + 0.1.0 pil mod March 10, 2009 Renamed display profile to proof profile. The proof @@ -75,12 +79,13 @@ pyCMS """ -VERSION = "0.1.0 pil" +VERSION = "1.0.0 pil" # --------------------------------------------------------------------. -import Image -import _imagingcms +from PIL import Image +from PIL import _imagingcms +from PIL._util import isStringType core = _imagingcms @@ -122,7 +127,7 @@ FLAGS = { _MAX_FLAG = 0 for flag in FLAGS.values(): - if isinstance(flag, type(0)): + if isinstance(flag, int): _MAX_FLAG = _MAX_FLAG | flag # --------------------------------------------------------------------. @@ -137,10 +142,10 @@ class ImageCmsProfile: def __init__(self, profile): # accepts a string (filename), a file-like object, or a low-level # profile object - if Image.isStringType(profile): + if isStringType(profile): self._set(core.profile_open(profile), profile) elif hasattr(profile, "read"): - self._set(core.profile_fromstring(profile.read())) + self._set(core.profile_frombytes(profile.read())) else: self._set(profile) # assume it's already a profile @@ -148,8 +153,8 @@ class ImageCmsProfile: self.profile = profile self.filename = filename if profile: - self.product_name = profile.product_name - self.product_info = profile.product_info + self.product_name = None #profile.product_name + self.product_info = None #profile.product_info else: self.product_name = None self.product_info = None @@ -200,12 +205,12 @@ class ImageCmsTransform(Image.ImagePointHandler): ## # (experimental) Fetches the profile for the current display device. -# Returns None if the profile is not known. +# @return None if the profile is not known. def get_display_profile(handle=None): import sys if sys.platform == "win32": - import ImageWin + from PIL import ImageWin if isinstance(handle, ImageWin.HDC): profile = core.get_display_profile_win32(handle, 1) else: @@ -232,66 +237,57 @@ class PyCMSError(Exception): ## # (pyCMS) Applies an ICC transformation to a given image, mapping from # inputProfile to outputProfile. +# +# If the input or output profiles specified are not valid filenames, a +# PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, +# a PyCMSError will be raised. If an error occurs during application of +# the profiles, a PyCMSError will be raised. If outputMode is not a mode +# supported by the outputProfile (or by pyCMS), a PyCMSError will be +# raised. +# +# This function applies an ICC transformation to im from inputProfile's +# color space to outputProfile's color space using the specified rendering +# intent to decide how to handle out-of-gamut colors. +# +# OutputMode can be used to specify that a color mode conversion is to +# be done using these profiles, but the specified profiles must be able +# to handle that mode. I.e., if converting im from RGB to CMYK using +# profiles, the input profile must handle RGB data, and the output +# profile must handle CMYK data. +# +# @param im An open PIL image object (i.e. Image.new(...) or Image.open(...), etc.) +# @param inputProfile String, as a valid filename path to the ICC input profile +# you wish to use for this image, or a profile object +# @param outputProfile String, as a valid filename path to the ICC output +# profile you wish to use for this image, or a profile object +# @param renderingIntent Integer (0-3) specifying the rendering intent you wish +# to use for the transform +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @param outputMode A valid PIL mode for the output image (i.e. "RGB", "CMYK", +# etc.). Note: if rendering the image "inPlace", outputMode MUST be the +# same mode as the input, or omitted completely. If omitted, the outputMode +# will be the same as the mode of the input image (im.mode) +# @param inPlace Boolean (1 = True, None or 0 = False). If True, the original +# image is modified in-place, and None is returned. If False (default), a +# new Image object is returned with the transform applied. +# @param flags Integer (0-...) specifying additional flags +# @return Either None or a new PIL image object, depending on value of inPlace +# @exception PyCMSError def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0): - """ - ImageCms.profileToProfile(im, inputProfile, outputProfile, - [renderingIntent], [outputMode], [inPlace]) - - Returns either None or a new PIL image object, depending on value of - inPlace (see below). - - im = an open PIL image object (i.e. Image.new(...) or - Image.open(...), etc.) - inputProfile = string, as a valid filename path to the ICC input - profile you wish to use for this image, or a profile object - outputProfile = string, as a valid filename path to the ICC output - profile you wish to use for this image, or a profile object - renderingIntent = integer (0-3) specifying the rendering intent you - wish to use for the transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - - see the pyCMS documentation for details on rendering intents and - what they do. - outputMode = a valid PIL mode for the output image (i.e. "RGB", "CMYK", - etc.). Note: if rendering the image "inPlace", outputMode MUST be - the same mode as the input, or omitted completely. If omitted, the - outputMode will be the same as the mode of the input image (im.mode) - inPlace = BOOL (1 = TRUE, None or 0 = FALSE). If TRUE, the original - image is modified in-place, and None is returned. If FALSE - (default), a new Image object is returned with the transform - applied. - flags = integer (0-...) specifying additional flags - - If the input or output profiles specified are not valid filenames, a - PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, - a PyCMSError will be raised. If an error occurs during application of - the profiles, a PyCMSError will be raised. If outputMode is not a mode - supported by the outputProfile (or by pyCMS), a PyCMSError will be - raised. - - This function applies an ICC transformation to im from inputProfile's - color space to outputProfile's color space using the specified rendering - intent to decide how to handle out-of-gamut colors. - - OutputMode can be used to specify that a color mode conversion is to - be done using these profiles, but the specified profiles must be able - to handle that mode. I.e., if converting im from RGB to CMYK using - profiles, the input profile must handle RGB data, and the output - profile must handle CMYK data. - - """ - if outputMode is None: outputMode = im.mode - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -307,99 +303,89 @@ def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PER imOut = None else: imOut = transform.apply(im) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) return imOut ## # (pyCMS) Opens an ICC profile file. +# +# The PyCMSProfile object can be passed back into pyCMS for use in creating +# transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). +# +# If profileFilename is not a vaild filename for an ICC profile, a PyCMSError +# will be raised. +# +# @param profileFilename String, as a valid filename path to the ICC profile you +# wish to open, or a file-like object. +# @return A CmsProfile class object. +# @exception PyCMSError def getOpenProfile(profileFilename): - """ - ImageCms.getOpenProfile(profileFilename) - - Returns a CmsProfile class object. - - profileFilename = string, as a valid filename path to the ICC profile - you wish to open, or a file-like object. - - The PyCMSProfile object can be passed back into pyCMS for use in creating - transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). - - If profileFilename is not a vaild filename for an ICC profile, a - PyCMSError will be raised. - - """ - try: return ImageCmsProfile(profileFilename) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## # (pyCMS) Builds an ICC transform mapping from the inputProfile to the # outputProfile. Use applyTransform to apply the transform to a given # image. +# +# If the input or output profiles specified are not valid filenames, a +# PyCMSError will be raised. If an error occurs during creation of the +# transform, a PyCMSError will be raised. +# +# If inMode or outMode are not a mode supported by the outputProfile (or +# by pyCMS), a PyCMSError will be raised. +# +# This function builds and returns an ICC transform from the inputProfile +# to the outputProfile using the renderingIntent to determine what to do +# with out-of-gamut colors. It will ONLY work for converting images that +# are in inMode to images that are in outMode color format (PIL mode, +# i.e. "RGB", "RGBA", "CMYK", etc.). +# +# Building the transform is a fair part of the overhead in +# ImageCms.profileToProfile(), so if you're planning on converting multiple +# images using the same input/output settings, this can save you time. +# Once you have a transform object, it can be used with +# ImageCms.applyProfile() to convert images without the need to re-compute +# the lookup table for the transform. +# +# The reason pyCMS returns a class object rather than a handle directly +# to the transform is that it needs to keep track of the PIL input/output +# modes that the transform is meant for. These attributes are stored in +# the "inMode" and "outMode" attributes of the object (which can be +# manually overridden if you really want to, but I don't know of any +# time that would be of use, or would even work). +# +# @param inputProfile String, as a valid filename path to the ICC input profile +# you wish to use for this transform, or a profile object +# @param outputProfile String, as a valid filename path to the ICC output +# profile you wish to use for this transform, or a profile object +# @param inMode String, as a valid PIL mode that the appropriate profile also +# supports (i.e. "RGB", "RGBA", "CMYK", etc.) +# @param outMode String, as a valid PIL mode that the appropriate profile also +# supports (i.e. "RGB", "RGBA", "CMYK", etc.) +# @param renderingIntent Integer (0-3) specifying the rendering intent you +# wish to use for the transform +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @param flags Integer (0-...) specifying additional flags +# @return A CmsTransform class object. +# @exception PyCMSError def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0): - """ - ImageCms.buildTransform(inputProfile, outputProfile, inMode, outMode, - [renderingIntent]) - - Returns a CmsTransform class object. - - inputProfile = string, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - outputProfile = string, as a valid filename path to the ICC output - profile you wish to use for this transform, or a profile object - inMode = string, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - outMode = string, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - renderingIntent = integer (0-3) specifying the rendering intent you - wish to use for the transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and - what they do. - flags = integer (0-...) specifying additional flags - - If the input or output profiles specified are not valid filenames, a - PyCMSError will be raised. If an error occurs during creation of the - transform, a PyCMSError will be raised. - - If inMode or outMode are not a mode supported by the outputProfile (or - by pyCMS), a PyCMSError will be raised. - - This function builds and returns an ICC transform from the inputProfile - to the outputProfile using the renderingIntent to determine what to do - with out-of-gamut colors. It will ONLY work for converting images that - are in inMode to images that are in outMode color format (PIL mode, - i.e. "RGB", "RGBA", "CMYK", etc.). - - Building the transform is a fair part of the overhead in - ImageCms.profileToProfile(), so if you're planning on converting multiple - images using the same input/output settings, this can save you time. - Once you have a transform object, it can be used with - ImageCms.applyProfile() to convert images without the need to re-compute - the lookup table for the transform. - - The reason pyCMS returns a class object rather than a handle directly - to the transform is that it needs to keep track of the PIL input/output - modes that the transform is meant for. These attributes are stored in - the "inMode" and "outMode" attributes of the object (which can be - manually overridden if you really want to, but I don't know of any - time that would be of use, or would even work). - - """ - - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -408,89 +394,85 @@ def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent if not isinstance(outputProfile, ImageCmsProfile): outputProfile = ImageCmsProfile(outputProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## # (pyCMS) Builds an ICC transform mapping from the inputProfile to the # outputProfile, but tries to simulate the result that would be # obtained on the proofProfile device. +# +# If the input, output, or proof profiles specified are not valid +# filenames, a PyCMSError will be raised. +# +# If an error occurs during creation of the transform, a PyCMSError will +# be raised. +# +# If inMode or outMode are not a mode supported by the outputProfile +# (or by pyCMS), a PyCMSError will be raised. +# +# This function builds and returns an ICC transform from the inputProfile +# to the outputProfile, but tries to simulate the result that would be +# obtained on the proofProfile device using renderingIntent and +# proofRenderingIntent to determine what to do with out-of-gamut +# colors. This is known as "soft-proofing". It will ONLY work for +# converting images that are in inMode to images that are in outMode +# color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). +# +# Usage of the resulting transform object is exactly the same as with +# ImageCms.buildTransform(). +# +# Proof profiling is generally used when using an output device to get a +# good idea of what the final printed/displayed image would look like on +# the proofProfile device when it's quicker and easier to use the +# output device for judging color. Generally, this means that the +# output device is a monitor, or a dye-sub printer (etc.), and the simulated +# device is something more expensive, complicated, or time consuming +# (making it difficult to make a real print for color judgement purposes). +# +# Soft-proofing basically functions by adjusting the colors on the +# output device to match the colors of the device being simulated. However, +# when the simulated device has a much wider gamut than the output +# device, you may obtain marginal results. +# +# @param inputProfile String, as a valid filename path to the ICC input profile +# you wish to use for this transform, or a profile object +# @param outputProfile String, as a valid filename path to the ICC output +# (monitor, usually) profile you wish to use for this transform, or a +# profile object +# @param proofProfile String, as a valid filename path to the ICC proof profile +# you wish to use for this transform, or a profile object +# @param inMode String, as a valid PIL mode that the appropriate profile also +# supports (i.e. "RGB", "RGBA", "CMYK", etc.) +# @param outMode String, as a valid PIL mode that the appropriate profile also +# supports (i.e. "RGB", "RGBA", "CMYK", etc.) +# @param renderingIntent Integer (0-3) specifying the rendering intent you +# wish to use for the input->proof (simulated) transform +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @param proofRenderingIntent Integer (0-3) specifying the rendering intent you +# wish to use for proof->output transform +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @param flags Integer (0-...) specifying additional flags +# @return A CmsTransform class object. +# @exception PyCMSError def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]): - """ - ImageCms.buildProofTransform(inputProfile, outputProfile, proofProfile, - inMode, outMode, [renderingIntent], [proofRenderingIntent]) - - Returns a CmsTransform class object. - - inputProfile = string, as a valid filename path to the ICC input - profile you wish to use for this transform, or a profile object - outputProfile = string, as a valid filename path to the ICC output - (monitor, usually) profile you wish to use for this transform, - or a profile object - proofProfile = string, as a valid filename path to the ICC proof - profile you wish to use for this transform, or a profile object - inMode = string, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - outMode = string, as a valid PIL mode that the appropriate profile also - supports (i.e. "RGB", "RGBA", "CMYK", etc.) - renderingIntent = integer (0-3) specifying the rendering intent you - wish to use for the input->proof (simulated) transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and - what they do. - proofRenderingIntent = integer (0-3) specifying the rendering intent - you wish to use for proof->output transform - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and - what they do. - flags = integer (0-...) specifying additional flags - - If the input, output, or proof profiles specified are not valid - filenames, a PyCMSError will be raised. - - If an error occurs during creation of the transform, a PyCMSError will - be raised. - - If inMode or outMode are not a mode supported by the outputProfile - (or by pyCMS), a PyCMSError will be raised. - - This function builds and returns an ICC transform from the inputProfile - to the outputProfile, but tries to simulate the result that would be - obtained on the proofProfile device using renderingIntent and - proofRenderingIntent to determine what to do with out-of-gamut - colors. This is known as "soft-proofing". It will ONLY work for - converting images that are in inMode to images that are in outMode - color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). - - Usage of the resulting transform object is exactly the same as with - ImageCms.buildTransform(). - - Proof profiling is generally used when using an output device to get a - good idea of what the final printed/displayed image would look like on - the proofProfile device when it's quicker and easier to use the - output device for judging color. Generally, this means that the - output device is a monitor, or a dye-sub printer (etc.), and the simulated - device is something more expensive, complicated, or time consuming - (making it difficult to make a real print for color judgement purposes). - - Soft-proofing basically functions by adjusting the colors on the - output device to match the colors of the device being simulated. However, - when the simulated device has a much wider gamut than the output - device, you may obtain marginal results. - - """ - - if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <=3): raise PyCMSError("renderingIntent must be an integer between 0 and 3") - if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG): raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) try: @@ -501,7 +483,7 @@ def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMo if not isinstance(proofProfile, ImageCmsProfile): proofProfile = ImageCmsProfile(proofProfile) return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags) - except (IOError, TypeError, ValueError), v: + except (IOError, TypeError, ValueError) as v: raise PyCMSError(v) buildTransformFromOpenProfiles = buildTransform @@ -509,240 +491,349 @@ buildProofTransformFromOpenProfiles = buildProofTransform ## # (pyCMS) Applies a transform to a given image. +# +# If im.mode != transform.inMode, a PyCMSError is raised. +# +# If inPlace == TRUE and transform.inMode != transform.outMode, a +# PyCMSError is raised. +# +# If im.mode, transfer.inMode, or transfer.outMode is not supported by +# pyCMSdll or the profiles you used for the transform, a PyCMSError is +# raised. +# +# If an error occurs while the transform is being applied, a PyCMSError +# is raised. +# +# This function applies a pre-calculated transform (from +# ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an +# image. The transform can be used for multiple images, saving +# considerable calcuation time if doing the same conversion multiple times. +# +# If you want to modify im in-place instead of receiving a new image as +# the return value, set inPlace to TRUE. This can only be done if +# transform.inMode and transform.outMode are the same, because we can't +# change the mode in-place (the buffer sizes for some modes are +# different). The default behavior is to return a new Image object of +# the same dimensions in mode transform.outMode. +# +# @param im A PIL Image object, and im.mode must be the same as the inMode +# supported by the transform. +# @param transform A valid CmsTransform class object +# @param inPlace Bool (1 == True, 0 or None == False). If True, im is modified +# in place and None is returned, if False, a new Image object with the +# transform applied is returned (and im is not changed). The default is False. +# @return Either None, or a new PIL Image object, depending on the value of inPlace +# @exception PyCMSError def applyTransform(im, transform, inPlace=0): - """ - ImageCms.applyTransform(im, transform, [inPlace]) - - Returns either None, or a new PIL Image object, depending on the value - of inPlace (see below) - - im = a PIL Image object, and im.mode must be the same as the inMode - supported by the transform. - transform = a valid CmsTransform class object - inPlace = BOOL (1 == TRUE, 0 or None == FALSE). If TRUE, im is - modified in place and None is returned, if FALSE, a new Image - object with the transform applied is returned (and im is not - changed). The default is FALSE. - - If im.mode != transform.inMode, a PyCMSError is raised. - - If inPlace == TRUE and transform.inMode != transform.outMode, a - PyCMSError is raised. - - If im.mode, transfer.inMode, or transfer.outMode is not supported by - pyCMSdll or the profiles you used for the transform, a PyCMSError is - raised. - - If an error occurs while the transform is being applied, a PyCMSError - is raised. - - This function applies a pre-calculated transform (from - ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an - image. The transform can be used for multiple images, saving - considerable calcuation time if doing the same conversion multiple times. - - If you want to modify im in-place instead of receiving a new image as - the return value, set inPlace to TRUE. This can only be done if - transform.inMode and transform.outMode are the same, because we can't - change the mode in-place (the buffer sizes for some modes are - different). The default behavior is to return a new Image object of - the same dimensions in mode transform.outMode. - - """ - try: if inPlace: transform.apply_in_place(im) imOut = None else: imOut = transform.apply(im) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) return imOut ## # (pyCMS) Creates a profile. +# +# If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised +# +# If using LAB and colorTemp != a positive integer, a PyCMSError is raised. +# +# If an error occurs while creating the profile, a PyCMSError is raised. +# +# Use this function to create common profiles on-the-fly instead of +# having to supply a profile on disk and knowing the path to it. It +# returns a normal CmsProfile object that can be passed to +# ImageCms.buildTransformFromOpenProfiles() to create a transform to apply +# to images. +# +# @param colorSpace String, the color space of the profile you wish to create. +# Currently only "LAB", "XYZ", and "sRGB" are supported. +# @param colorTemp Positive integer for the white point for the profile, in +# degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50 +# illuminant if omitted (5000k). colorTemp is ONLY applied to LAB profiles, +# and is ignored for XYZ and sRGB. +# @return A CmsProfile class object +# @exception PyCMSError def createProfile(colorSpace, colorTemp=-1): - """ - ImageCms.createProfile(colorSpace, [colorTemp]) - - Returns a CmsProfile class object - - colorSpace = string, the color space of the profile you wish to create. - Currently only "LAB", "XYZ", and "sRGB" are supported. - colorTemp = positive integer for the white point for the profile, in - degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for - D50 illuminant if omitted (5000k). colorTemp is ONLY applied to - LAB profiles, and is ignored for XYZ and sRGB. - - If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised - - If using LAB and colorTemp != a positive integer, a PyCMSError is raised. - - If an error occurs while creating the profile, a PyCMSError is raised. - - Use this function to create common profiles on-the-fly instead of - having to supply a profile on disk and knowing the path to it. It - returns a normal CmsProfile object that can be passed to - ImageCms.buildTransformFromOpenProfiles() to create a transform to apply - to images. - - """ if colorSpace not in ["LAB", "XYZ", "sRGB"]: raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) if colorSpace == "LAB": - if type(colorTemp) == type(5000.0): - colorTemp = int(colorTemp + 0.5) - if type (colorTemp) != type (5000): - raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) + try: + colorTemp = float(colorTemp) + except: + raise PyCMSError("Color temperature must be numeric, \"%s\" not valid" % colorTemp) try: return core.createProfile(colorSpace, colorTemp) - except (TypeError, ValueError), v: + except (TypeError, ValueError) as v: raise PyCMSError(v) ## # (pyCMS) Gets the internal product name for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised If an error occurs while trying to obtain the +# name tag, a PyCMSError is raised. +# +# Use this function to obtain the INTERNAL name of the profile (stored +# in an ICC tag in the profile itself), usually the one used when the +# profile was originally created. Sometimes this tag also contains +# additional information supplied by the creator. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal name of the profile as stored in an +# ICC tag. +# @exception PyCMSError def getProfileName(profile): - """ - ImageCms.getProfileName(profile) - - Returns a string containing the internal name of the profile as stored - in an ICC tag. - - profile = EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised If an error occurs while trying to obtain the - name tag, a PyCMSError is raised. - - Use this function to obtain the INTERNAL name of the profile (stored - in an ICC tag in the profile itself), usually the one used when the - profile was originally created. Sometimes this tag also contains - additional information supplied by the creator. - - """ try: # add an extra newline to preserve pyCMS compatibility if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) - return profile.profile.product_name + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + # do it in python, not c. + # // name was "%s - %s" (model, manufacturer) || Description , + # // but if the Model and Manufacturer were the same or the model + # // was long, Just the model, in 1.x + model = profile.profile.product_model + manufacturer = profile.profile.product_manufacturer + + if not (model or manufacturer): + return profile.profile.product_description+"\n" + if not manufacturer or len(model) > 30: + return model + "\n" + return "%s - %s\n" % (model, manufacturer) + + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## # (pyCMS) Gets the internal product information for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the info tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# info tag. This often contains details about the profile, and how it +# was created, as supplied by the creator. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError def getProfileInfo(profile): - """ - ImageCms.getProfileInfo(profile) - - Returns a string containing the internal profile information stored in - an ICC tag. - - profile = EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. - - If an error occurs while trying to obtain the info tag, a PyCMSError - is raised - - Use this function to obtain the information stored in the profile's - info tag. This often contains details about the profile, and how it - was created, as supplied by the creator. - - """ try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) # add an extra newline to preserve pyCMS compatibility - return profile.product_info + "\n" - except (AttributeError, IOError, TypeError, ValueError), v: + # Python, not C. the white point bits weren't working well, so skipping. + # // info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint + description = profile.profile.product_description + cpright = profile.profile.product_copyright + arr = [] + for elt in (description, cpright): + if elt: + arr.append(elt) + return "\r\n\r\n".join(arr)+"\r\n\r\n" + + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + +## +# (pyCMS) Gets the copyright for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the copyright tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# copyright tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileCopyright(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_copyright + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) +## +# (pyCMS) Gets the manufacturer for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the manufacturer tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# manufacturer tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileManufacturer(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_manufacturer + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the model for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the model tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# model tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileModel(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_model + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the description for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the description tag, a PyCMSError +# is raised +# +# Use this function to obtain the information stored in the profile's +# description tag. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return A string containing the internal profile information stored in an ICC +# tag. +# @exception PyCMSError + +def getProfileDescription(profile): + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_description + "\n" + except (AttributeError, IOError, TypeError, ValueError) as v: + raise PyCMSError(v) + + + ## # (pyCMS) Gets the default intent name for the given profile. +# +# If profile isn't a valid CmsProfile object or filename to a profile, +# a PyCMSError is raised. +# +# If an error occurs while trying to obtain the default intent, a +# PyCMSError is raised. +# +# Use this function to determine the default (and usually best optomized) +# rendering intent for this profile. Most profiles support multiple +# rendering intents, but are intended mostly for one type of conversion. +# If you wish to use a different intent than returned, use +# ImageCms.isIntentSupported() to verify it will work first. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @return Integer 0-3 specifying the default rendering intent for this profile. +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @exception PyCMSError def getDefaultIntent(profile): - """ - ImageCms.getDefaultIntent(profile) - - Returns integer 0-3 specifying the default rendering intent for this - profile. - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and - what they do. - - profile = EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - - If profile isn't a valid CmsProfile object or filename to a profile, - a PyCMSError is raised. - - If an error occurs while trying to obtain the default intent, a - PyCMSError is raised. - - Use this function to determine the default (and usually best optomized) - rendering intent for this profile. Most profiles support multiple - rendering intents, but are intended mostly for one type of conversion. - If you wish to use a different intent than returned, use - ImageCms.isIntentSupported() to verify it will work first. - """ try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) return profile.profile.rendering_intent - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## # (pyCMS) Checks if a given intent is supported. +# +# Use this function to verify that you can use your desired +# renderingIntent with profile, and that profile can be used for the +# input/output/proof profile as you desire. +# +# Some profiles are created specifically for one "direction", can cannot +# be used for others. Some profiles can only be used for certain +# rendering intents... so it's best to either verify this before trying +# to create a transform with them (using this function), or catch the +# potential PyCMSError that will occur if they don't support the modes +# you select. +# +# @param profile EITHER a valid CmsProfile object, OR a string of the filename +# of an ICC profile. +# @param intent Integer (0-3) specifying the rendering intent you wish to use +# with this profile +# +# INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) +# INTENT_RELATIVE_COLORIMETRIC = 1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) +# INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) +# INTENT_ABSOLUTE_COLORIMETRIC = 3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) +# +# see the pyCMS documentation for details on rendering intents and what they do. +# @param direction Integer specifing if the profile is to be used for input, +# output, or proof +# +# INPUT = 0 (or use ImageCms.DIRECTION_INPUT) +# OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) +# PROOF = 2 (or use ImageCms.DIRECTION_PROOF) +# +# @return 1 if the intent/direction are supported, -1 if they are not. +# @exception PyCMSError def isIntentSupported(profile, intent, direction): - """ - ImageCms.isIntentSupported(profile, intent, direction) - - Returns 1 if the intent/direction are supported, -1 if they are not. - - profile = EITHER a valid CmsProfile object, OR a string of the - filename of an ICC profile. - intent = integer (0-3) specifying the rendering intent you wish to use - with this profile - INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) - INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) - INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) - INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) - see the pyCMS documentation for details on rendering intents and - what they do. - direction = integer specifing if the profile is to be used for input, - output, or proof - INPUT = 0 (or use ImageCms.DIRECTION_INPUT) - OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) - PROOF = 2 (or use ImageCms.DIRECTION_PROOF) - - Use this function to verify that you can use your desired - renderingIntent with profile, and that profile can be used for the - input/output/proof profile as you desire. - - Some profiles are created specifically for one "direction", can cannot - be used for others. Some profiles can only be used for certain - rendering intents... so it's best to either verify this before trying - to create a transform with them (using this function), or catch the - potential PyCMSError that will occur if they don't support the modes - you select. - - """ try: if not isinstance(profile, ImageCmsProfile): profile = ImageCmsProfile(profile) @@ -752,7 +843,7 @@ def isIntentSupported(profile, intent, direction): return 1 else: return -1 - except (AttributeError, IOError, TypeError, ValueError), v: + except (AttributeError, IOError, TypeError, ValueError) as v: raise PyCMSError(v) ## @@ -769,18 +860,17 @@ def versions(): if __name__ == "__main__": # create a cheap manual from the __doc__ strings for the functions above - import ImageCms - import string - print __doc__ + from PIL import ImageCms + print(__doc__) for f in dir(pyCMS): - print "="*80 - print "%s" %f + print("="*80) + print("%s" %f) try: exec ("doc = ImageCms.%s.__doc__" %(f)) - if string.find(doc, "pyCMS") >= 0: + if "pyCMS" in doc: # so we don't get the __doc__ string for imported modules - print doc + print(doc) except AttributeError: pass diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py index c3cca46db..c14257151 100644 --- a/PIL/ImageColor.py +++ b/PIL/ImageColor.py @@ -17,16 +17,9 @@ # See the README file for information on usage and redistribution. # -import Image -import re, string +from PIL import Image +import re -try: - x = int("a", 16) -except TypeError: - # python 1.5.2 doesn't support int(x,b) - str2int = string.atoi -else: - str2int = int ## # Convert color string to RGB tuple. @@ -37,18 +30,26 @@ else: # as an RGB value. def getrgb(color): - # FIXME: add RGBA support + """ + Convert a color string to an RGB tuple. If the string cannot be parsed, + this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ try: rgb = colormap[color] except KeyError: try: # fall back on case-insensitive lookup - rgb = colormap[string.lower(color)] + rgb = colormap[color.lower()] except KeyError: rgb = None # found color in cache if rgb: - if isinstance(rgb, type(())): + if isinstance(rgb, tuple): return rgb colormap[color] = rgb = getrgb(rgb) return rgb @@ -56,30 +57,30 @@ def getrgb(color): m = re.match("#\w\w\w$", color) if m: return ( - str2int(color[1]*2, 16), - str2int(color[2]*2, 16), - str2int(color[3]*2, 16) + int(color[1]*2, 16), + int(color[2]*2, 16), + int(color[3]*2, 16) ) m = re.match("#\w\w\w\w\w\w$", color) if m: return ( - str2int(color[1:3], 16), - str2int(color[3:5], 16), - str2int(color[5:7], 16) + int(color[1:3], 16), + int(color[3:5], 16), + int(color[5:7], 16) ) m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) if m: return ( - str2int(m.group(1)), - str2int(m.group(2)), - str2int(m.group(3)) + int(m.group(1)), + int(m.group(2)), + int(m.group(3)) ) m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: return ( - int((str2int(m.group(1)) * 255) / 100.0 + 0.5), - int((str2int(m.group(2)) * 255) / 100.0 + 0.5), - int((str2int(m.group(3)) * 255) / 100.0 + 0.5) + int((int(m.group(1)) * 255) / 100.0 + 0.5), + int((int(m.group(2)) * 255) / 100.0 + 0.5), + int((int(m.group(3)) * 255) / 100.0 + 0.5) ) m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) if m: @@ -94,19 +95,39 @@ def getrgb(color): int(rgb[1] * 255 + 0.5), int(rgb[2] * 255 + 0.5) ) + m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return ( + int(m.group(1)), + int(m.group(2)), + int(m.group(3)), + int(m.group(4)) + ) raise ValueError("unknown color specifier: %r" % color) def getcolor(color, mode): + """ + Same as :py:func:`~PIL.ImageColor.getrgb`, but converts the RGB value to a + greyscale value if the mode is not color or a palette image. If the string + cannot be parsed, this function raises a :py:exc:`ValueError` exception. + + .. versionadded:: 1.1.4 + + :param color: A color string + :return: ``(red, green, blue)`` + """ # same as getrgb, but converts the result to the given mode color = getrgb(color) if mode == "RGB": return color if mode == "RGBA": - r, g, b = color - return r, g, b, 255 + if len(color) == 3: + color = (color + (255,)) + r, g, b, a = color + return r, g, b, a if Image.getmodebase(mode) == "L": r, g, b = color - return (r*299 + g*587 + b*114)/1000 + return (r*299 + g*587 + b*114)//1000 return color colormap = { diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 5217a7366..a03d26016 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -30,7 +30,10 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor +import numbers + +from PIL import Image, ImageColor +from PIL._util import isStringType try: import warnings @@ -96,9 +99,9 @@ class ImageDraw: "'setink' is deprecated; use keyword arguments instead", DeprecationWarning, stacklevel=2 ) - if Image.isStringType(ink): + if isStringType(ink): ink = ImageColor.getcolor(ink, self.mode) - if self.palette and not Image.isNumberType(ink): + if self.palette and not isinstance(ink, numbers.Number): ink = self.palette.getcolor(ink) self.ink = self.draw.draw_ink(ink, self.mode) @@ -127,7 +130,7 @@ class ImageDraw: def getfont(self): if not self.font: # FIXME: should add a font repository - import ImageFont + from PIL import ImageFont self.font = ImageFont.load_default() return self.font @@ -139,15 +142,15 @@ class ImageDraw: ink = self.ink else: if ink is not None: - if Image.isStringType(ink): + if isStringType(ink): ink = ImageColor.getcolor(ink, self.mode) - if self.palette and not Image.isNumberType(ink): + if self.palette and not isinstance(ink, numbers.Number): ink = self.palette.getcolor(ink) ink = self.draw.draw_ink(ink, self.mode) if fill is not None: - if Image.isStringType(fill): + if isStringType(fill): fill = ImageColor.getcolor(fill, self.mode) - if self.palette and not Image.isNumberType(fill): + if self.palette and not isinstance(fill, numbers.Number): fill = self.palette.getcolor(fill) fill = self.draw.draw_ink(fill, self.mode) return ink, fill @@ -313,13 +316,11 @@ def getdraw(im=None, hints=None): handler = None if not hints or "nicest" in hints: try: - import _imagingagg - handler = _imagingagg + from PIL import _imagingagg as handler except ImportError: pass if handler is None: - import ImageDraw2 - handler = ImageDraw2 + from PIL import ImageDraw2 as handler if im: im = handler.Draw(im) return im, handler diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py index dbf1a1f88..146cc8b16 100644 --- a/PIL/ImageDraw2.py +++ b/PIL/ImageDraw2.py @@ -16,7 +16,7 @@ # See the README file for information on usage and redistribution. # -import Image, ImageColor, ImageDraw, ImageFont, ImagePath +from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath class Pen: def __init__(self, color, width=1, opacity=255): @@ -68,7 +68,8 @@ class Draw: else: getattr(self.draw, op)(xy, fill=fill, outline=outline) - def settransform(self, (xoffset, yoffset)): + def settransform(self, offset): + (xoffset, yoffset) = offset self.transform = (1, 0, xoffset, 0, 1, yoffset) def arc(self, xy, start, end, *options): diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py index 86a19e650..10433343e 100644 --- a/PIL/ImageEnhance.py +++ b/PIL/ImageEnhance.py @@ -18,73 +18,70 @@ # See the README file for information on usage and redistribution. # -import Image, ImageFilter, ImageStat +from PIL import Image, ImageFilter, ImageStat + class _Enhance: - ## - # Returns an enhanced image. The enhancement factor is a floating - # point value controlling the enhancement. Factor 1.0 always - # returns a copy of the original image, lower factors mean less - # colour (brightness, contrast, etc), and higher values more. - # There are no restrictions on this value. - # - # @param factor Enhancement factor. - # @return An enhanced image. - def enhance(self, factor): + """ + Returns an enhanced image. + + :param factor: A floating point value controlling the enhancement. + Factor 1.0 always returns a copy of the original image, + lower factors mean less color (brightness, contrast, + etc), and higher values more. There are no restrictions + on this value. + :rtype: :py:class:`~PIL.Image.Image` + """ return Image.blend(self.degenerate, self.image, factor) -## -# Color enhancement object. -#

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

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

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

-# This class can be used to adjust the sharpness of an image. The -# enhancement factor 0.0 gives a blurred image, 1.0 gives the original -# image, and a factor of 2.0 gives a sharpened image. class Sharpness(_Enhance): - "Adjust image sharpness" + """Adjust image sharpness. + + This class can be used to adjust the sharpness of an image. An + enhancement factor of 0.0 gives a blurred image, a factor of 1.0 gives the + original image, and a factor of 2.0 gives a sharpened image. + """ def __init__(self, image): self.image = image self.degenerate = image.filter(ImageFilter.SMOOTH) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 8a97c1b5b..a63fe757e 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -27,13 +27,17 @@ # See the README file for information on usage and redistribution. # -import Image -import traceback, string, os +from PIL import Image +from PIL._util import isPath +import traceback, os, sys +import io MAXBLOCK = 65536 SAFEBLOCK = 1024*1024 +LOAD_TRUNCATED_IMAGES = False + ERRORS = { -1: "image buffer overrun error", -2: "decoding error", @@ -55,17 +59,14 @@ def raise_ioerror(error): # -------------------------------------------------------------------- # Helpers -def _tilesort(t1, t2): +def _tilesort(t): # sort on offset - return cmp(t1[2], t2[2]) + return t[2] # # -------------------------------------------------------------------- # ImageFile base class -## -# Base class for image file handlers. - class ImageFile(Image.Image): "Base class for image file format handlers." @@ -78,7 +79,7 @@ class ImageFile(Image.Image): self.decoderconfig = () self.decodermaxblock = MAXBLOCK - if Image.isStringType(fp): + if isPath(fp): # filename self.fp = open(fp, "rb") self.filename = fp @@ -89,25 +90,25 @@ class ImageFile(Image.Image): try: self._open() - except IndexError, v: # end of data + except IndexError as v: # end of data if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except TypeError, v: # end of data (ord) + raise SyntaxError(v) + except TypeError as v: # end of data (ord) if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except KeyError, v: # unsupported mode + raise SyntaxError(v) + except KeyError as v: # unsupported mode if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v - except EOFError, v: # got header but not the first frame + raise SyntaxError(v) + except EOFError as v: # got header but not the first frame if Image.DEBUG > 1: traceback.print_exc() - raise SyntaxError, v + raise SyntaxError(v) if not self.mode or self.size[0] <= 0: - raise SyntaxError, "not identified by this driver" + raise SyntaxError("not identified by this driver") def draft(self, mode, size): "Set draft mode" @@ -135,7 +136,8 @@ class ImageFile(Image.Image): readonly = 0 - if self.filename and len(self.tile) == 1: + if self.filename and len(self.tile) == 1 and not hasattr(sys, 'pypy_version_info'): + # As of pypy 2.1.0, memory mapping was failing here. # try memory mapping d, e, o, a = self.tile[0] if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: @@ -177,13 +179,13 @@ class ImageFile(Image.Image): if not self.map: # sort tiles in file order - self.tile.sort(_tilesort) + self.tile.sort(key=_tilesort) try: # FIXME: This is a hack to handle TIFF's JpegTables tag. prefix = self.tile_prefix except AttributeError: - prefix = "" + prefix = b"" for d, e, o, a in self.tile: d = Image._getdecoder(self.mode, d, a, self.decoderconfig) @@ -194,11 +196,27 @@ class ImageFile(Image.Image): continue b = prefix t = len(b) - while 1: - s = read(self.decodermaxblock) - if not s: + while True: + try: + s = read(self.decodermaxblock) + except IndexError as ie: # truncated png/gif + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IndexError(ie) + + if not s: # truncated jpeg self.tile = [] - raise IOError("image file is truncated (%d bytes not processed)" % len(b)) + + # JpegDecode needs to clean things up here either way + # If we don't destroy the decompressor, we have a memory leak. + d.cleanup() + + if LOAD_TRUNCATED_IMAGES: + break + else: + raise IOError("image file is truncated (%d bytes not processed)" % len(b)) + b = b + s n, e = d.decode(b) if n < 0: @@ -211,7 +229,8 @@ class ImageFile(Image.Image): self.fp = None # might be shared - if not self.map and e < 0: + if not self.map and (not LOAD_TRUNCATED_IMAGES or t == 0) and e < 0: + # still raised if decoder fails to return anything raise_ioerror(e) # post processing @@ -245,14 +264,14 @@ class ImageFile(Image.Image): # def load_read(self, bytes): # pass -## -# Base class for stub image loaders. -#

-# A stub loader is an image loader that can identify files of a -# certain format, but relies on external code to load the file. class StubImageFile(ImageFile): - "Base class for stub image loaders." + """ + Base class for stub image loaders. + + A stub loader is an image loader that can identify files of a + certain format, but relies on external code to load the file. + """ def _open(self): raise NotImplementedError( @@ -269,87 +288,41 @@ class StubImageFile(ImageFile): self.__class__ = image.__class__ self.__dict__ = image.__dict__ - ## - # (Hook) Find actual image loader. - def _load(self): + "(Hook) Find actual image loader." raise NotImplementedError( "StubImageFile subclass must implement _load" ) -## -# (Internal) Support class for the Parser file. - -class _ParserFile: - # parser support class. - - def __init__(self, data): - self.data = data - self.offset = 0 - - def close(self): - self.data = self.offset = None - - def tell(self): - return self.offset - - def seek(self, offset, whence=0): - if whence == 0: - self.offset = offset - elif whence == 1: - self.offset = self.offset + offset - else: - # force error in Image.open - raise IOError("illegal argument to seek") - - def read(self, bytes=0): - pos = self.offset - if bytes: - data = self.data[pos:pos+bytes] - else: - data = self.data[pos:] - self.offset = pos + len(data) - return data - - def readline(self): - # FIXME: this is slow! - s = "" - while 1: - c = self.read(1) - if not c: - break - s = s + c - if c == "\n": - break - return s - -## -# Incremental image parser. This class implements the standard -# feed/close consumer interface. class Parser: + """ + Incremental image parser. This class implements the standard + feed/close consumer interface. + In Python 2.x, this is an old-style class. + """ incremental = None image = None data = None decoder = None finished = 0 - ## - # (Consumer) Reset the parser. Note that you can only call this - # method immediately after you've created a parser; parser - # instances cannot be reused. - def reset(self): + """ + (Consumer) Reset the parser. Note that you can only call this + method immediately after you've created a parser; parser + instances cannot be reused. + """ assert self.data is None, "cannot reuse parsers" - ## - # (Consumer) Feed data to the parser. - # - # @param data A string buffer. - # @exception IOError If the parser failed to parse the image file. - def feed(self, data): + """ + (Consumer) Feed data to the parser. + + :param data: A string buffer. + :exception IOError: If the parser failed to parse the image file. + """ # collect data if self.finished: @@ -398,11 +371,12 @@ class Parser: # attempt to open this file try: try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) im = Image.open(fp) finally: fp.close() # explicitly close the virtual file except IOError: + # traceback.print_exc() pass # not enough data else: flag = hasattr(im, "load_seek") or hasattr(im, "load_read") @@ -427,17 +401,19 @@ class Parser: self.image = im - ## - # (Consumer) Close the stream. - # - # @return An image object. - # @exception IOError If the parser failed to parse the image file. - def close(self): + """ + (Consumer) Close the stream. + + :returns: An image object. + :exception IOError: If the parser failed to parse the image file either + because it cannot be identified or cannot be + decoded. + """ # finish decoding if self.decoder: # get rid of what's left in the buffers - self.feed("") + self.feed(b"") self.data = self.decoder = None if not self.finished: raise IOError("image was incomplete") @@ -447,7 +423,7 @@ class Parser: # incremental parsing not possible; reopen the file # not that we have all data try: - fp = _ParserFile(self.data) + fp = io.BytesIO(self.data) self.image = Image.open(fp) finally: self.image.load() @@ -456,33 +432,35 @@ class Parser: # -------------------------------------------------------------------- -## -# (Helper) Save image body to file. -# -# @param im Image object. -# @param fp File object. -# @param tile Tile list. +def _save(im, fp, tile, bufsize=0): + """Helper to save image based on tile list -def _save(im, fp, tile): - "Helper to save image based on tile list" + :param im: Image object. + :param fp: File object. + :param tile: Tile list. + :param bufsize: Optional buffer size + """ im.load() if not hasattr(im, "encoderconfig"): im.encoderconfig = () - tile.sort(_tilesort) + tile.sort(key=_tilesort) # FIXME: make MAXBLOCK a configuration parameter - bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c + # It would be great if we could have the encoder specifiy what it needs + # But, it would need at least the image size in most cases. RawEncode is + # a tricky case. + bufsize = max(MAXBLOCK, bufsize, im.size[0] * 4) # see RawEncode.c try: fh = fp.fileno() fp.flush() - except AttributeError: + except (AttributeError, io.UnsupportedOperation): # compress to Python file-compatible object for e, b, o, a in tile: e = Image._getencoder(im.mode, e, a, im.encoderconfig) if o > 0: fp.seek(o, 0) e.setimage(im.im, b) - while 1: + while True: l, s, d = e.encode(bufsize) fp.write(d) if s: @@ -504,18 +482,18 @@ def _save(im, fp, tile): except: pass -## -# Reads large blocks in a safe way. Unlike fp.read(n), this function -# doesn't trust the user. If the requested size is larger than -# SAFEBLOCK, the file is read block by block. -# -# @param fp File handle. Must implement a read method. -# @param size Number of bytes to read. -# @return A string containing up to size bytes of data. - def _safe_read(fp, size): + """ + Reads large blocks in a safe way. Unlike fp.read(n), this function + doesn't trust the user. If the requested size is larger than + SAFEBLOCK, the file is read block by block. + + :param fp: File handle. Must implement a read method. + :param size: Number of bytes to read. + :returns: A string containing up to size bytes of data. + """ if size <= 0: - return "" + return b"" if size <= SAFEBLOCK: return fp.read(size) data = [] @@ -525,4 +503,4 @@ def _safe_read(fp, size): break data.append(block) size = size - len(block) - return string.join(data, "") + return b"".join(data) diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py index c12a25738..e57d3f43e 100644 --- a/PIL/ImageFileIO.py +++ b/PIL/ImageFileIO.py @@ -11,29 +11,30 @@ # # See the README file for information on usage and redistribution. # +""" +The **ImageFileIO** module can be used to read an image from a +socket, or any other stream device. -from StringIO import StringIO +Deprecated. New code should use the :class:`PIL.ImageFile.Parser` +class in the :mod:`PIL.ImageFile` module instead. -## -# The ImageFileIO module can be used to read an image from a -# socket, or any other stream device. -#

-# This module is deprecated. New code should use the Parser -# class in the ImageFile module instead. -# -# @see ImageFile#Parser +.. seealso:: modules :class:`PIL.ImageFile.Parser` +""" -class ImageFileIO(StringIO): +from io import BytesIO - ## - # Adds buffering to a stream file object, in order to - # provide seek and tell methods required - # by the Image.open method. The stream object must - # implement read and close methods. - # - # @param fp Stream file handle. - # @see Image#open +class ImageFileIO(BytesIO): def __init__(self, fp): + """ + Adds buffering to a stream file object, in order to + provide **seek** and **tell** methods required + by the :func:`PIL.Image.Image.open` method. The stream object must + implement **read** and **close** methods. + + :param fp: Stream file handle. + + .. seealso:: modules :func:`PIL.Image.open` + """ data = fp.read() - StringIO.__init__(self, data) + BytesIO.__init__(self, data) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py index 918bab177..ac8fe9f19 100644 --- a/PIL/ImageFilter.py +++ b/PIL/ImageFilter.py @@ -15,31 +15,30 @@ # See the README file for information on usage and redistribution. # -class Filter: +from functools import reduce + + +class Filter(object): pass -## -# Convolution filter kernel. class Kernel(Filter): + """ + Create a convolution kernel. The current version only + supports 3x3 and 5x5 integer and floating point kernels. - ## - # Create a convolution kernel. The current version only - # supports 3x3 and 5x5 integer and floating point kernels. - #

- # In the current version, kernels can only be applied to - # "L" and "RGB" images. - # - # @def __init__(size, kernel, **options) - # @param size Kernel size, given as (width, height). In - # the current version, this must be (3,3) or (5,5). - # @param kernel A sequence containing kernel weights. - # @param **options Optional keyword arguments. - # @keyparam scale Scale factor. If given, the result for each - # pixel is divided by this value. The default is the sum - # of the kernel weights. - # @keyparam offset Offset. If given, this value is added to the - # result, after it has been divided by the scale factor. + In the current version, kernels can only be applied to + "L" and "RGB" images. + + :param size: Kernel size, given as (width, height). In the current + version, this must be (3,3) or (5,5). + :param kernel: A sequence containing kernel weights. + :param scale: Scale factor. If given, the result for each pixel is + divided by this value. the default is the sum of the + kernel weights. + :param offset: Offset. If given, this value is added to the result, + after it has been divided by the scale factor. + """ def __init__(self, size, kernel, scale=None, offset=0): if scale is None: @@ -52,26 +51,25 @@ class Kernel(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - return apply(image.filter, self.filterargs) + return image.filter(*self.filterargs) + class BuiltinFilter(Kernel): def __init__(self): pass -## -# Rank filter. class RankFilter(Filter): - name = "Rank" + """ + Create a rank filter. The rank filter sorts all pixels in + a window of the given size, and returns the **rank**'th value. - ## - # Create a rank filter. The rank filter sorts all pixels in - # a window of the given size, and returns the rank'th value. - # - # @param size The kernel size, in pixels. - # @param rank What pixel value to pick. Use 0 for a min filter, - # size*size/2 for a median filter, size*size-1 for a max filter, - # etc. + :param size: The kernel size, in pixels. + :param rank: What pixel value to pick. Use 0 for a min filter, + ``size * size / 2`` for a median filter, ``size * size - 1`` + for a max filter, etc. + """ + name = "Rank" def __init__(self, size, rank): self.size = size @@ -80,102 +78,102 @@ class RankFilter(Filter): def filter(self, image): if image.mode == "P": raise ValueError("cannot filter palette images") - image = image.expand(self.size/2, self.size/2) + image = image.expand(self.size//2, self.size//2) return image.rankfilter(self.size, self.rank) -## -# Median filter. Picks the median pixel value in a window with the -# given size. class MedianFilter(RankFilter): - name = "Median" + """ + Create a median filter. Picks the median pixel value in a window with the + given size. - ## - # Create a median filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Median" def __init__(self, size=3): self.size = size - self.rank = size*size/2 + self.rank = size*size//2 -## -# Min filter. Picks the lowest pixel value in a window with the given -# size. class MinFilter(RankFilter): - name = "Min" + """ + Create a min filter. Picks the lowest pixel value in a window with the + given size. - ## - # Create a min filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Min" def __init__(self, size=3): self.size = size self.rank = 0 -## -# Max filter. Picks the largest pixel value in a window with the -# given size. class MaxFilter(RankFilter): - name = "Max" + """ + Create a max filter. Picks the largest pixel value in a window with the + given size. - ## - # Create a max filter. - # - # @param size The kernel size, in pixels. + :param size: The kernel size, in pixels. + """ + name = "Max" def __init__(self, size=3): self.size = size self.rank = size*size-1 -## -# Mode filter. Picks the most frequent pixel value in a box with the -# given size. Pixel values that occur only once or twice are ignored; -# if no pixel value occurs more than twice, the original pixel value -# is preserved. class ModeFilter(Filter): - name = "Mode" + """ - ## - # Create a mode filter. - # - # @param size The kernel size, in pixels. + Create a mode filter. Picks the most frequent pixel value in a box with the + given size. Pixel values that occur only once or twice are ignored; if no + pixel value occurs more than twice, the original pixel value is preserved. + + :param size: The kernel size, in pixels. + """ + name = "Mode" def __init__(self, size=3): self.size = size + def filter(self, image): return image.modefilter(self.size) -## -# Gaussian blur filter. class GaussianBlur(Filter): + """Gaussian blur filter. + + :param radius: Blur radius. + """ name = "GaussianBlur" def __init__(self, radius=2): - self.radius = 2 + self.radius = radius + def filter(self, image): return image.gaussian_blur(self.radius) -## -# Unsharp mask filter. class UnsharpMask(Filter): + """Unsharp mask filter. + + See Wikipedia's entry on `digital unsharp masking`_ for an explanation of + the parameters. + + .. _digital unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking#Digital_unsharp_masking + """ name = "UnsharpMask" def __init__(self, radius=2, percent=150, threshold=3): - self.radius = 2 + self.radius = radius self.percent = percent self.threshold = threshold + def filter(self, image): return image.unsharp_mask(self.radius, self.percent, self.threshold) -## -# Simple blur filter. class BLUR(BuiltinFilter): name = "Blur" @@ -187,8 +185,6 @@ class BLUR(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple contour filter. class CONTOUR(BuiltinFilter): name = "Contour" @@ -198,8 +194,6 @@ class CONTOUR(BuiltinFilter): -1, -1, -1 ) -## -# Simple detail filter. class DETAIL(BuiltinFilter): name = "Detail" @@ -209,8 +203,6 @@ class DETAIL(BuiltinFilter): 0, -1, 0 ) -## -# Simple edge enhancement filter. class EDGE_ENHANCE(BuiltinFilter): name = "Edge-enhance" @@ -220,8 +212,6 @@ class EDGE_ENHANCE(BuiltinFilter): -1, -1, -1 ) -## -# Simple stronger edge enhancement filter. class EDGE_ENHANCE_MORE(BuiltinFilter): name = "Edge-enhance More" @@ -231,8 +221,6 @@ class EDGE_ENHANCE_MORE(BuiltinFilter): -1, -1, -1 ) -## -# Simple embossing filter. class EMBOSS(BuiltinFilter): name = "Emboss" @@ -242,8 +230,6 @@ class EMBOSS(BuiltinFilter): 0, 0, 0 ) -## -# Simple edge-finding filter. class FIND_EDGES(BuiltinFilter): name = "Find Edges" @@ -253,8 +239,6 @@ class FIND_EDGES(BuiltinFilter): -1, -1, -1 ) -## -# Simple smoothing filter. class SMOOTH(BuiltinFilter): name = "Smooth" @@ -264,8 +248,6 @@ class SMOOTH(BuiltinFilter): 1, 1, 1 ) -## -# Simple stronger smoothing filter. class SMOOTH_MORE(BuiltinFilter): name = "Smooth More" @@ -277,8 +259,6 @@ class SMOOTH_MORE(BuiltinFilter): 1, 1, 1, 1, 1 ) -## -# Simple sharpening filter. class SHARPEN(BuiltinFilter): name = "Sharpen" diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py index 3ea2f2f8e..9366937dd 100644 --- a/PIL/ImageFont.py +++ b/PIL/ImageFont.py @@ -25,8 +25,16 @@ # See the README file for information on usage and redistribution. # -import Image -import os, string, sys +from __future__ import print_function + +from PIL import Image +from PIL._util import isDirectory, isPath +import os, sys + +try: + import warnings +except ImportError: + warnings = None class _imagingft_not_installed: # module placeholder @@ -34,9 +42,7 @@ class _imagingft_not_installed: raise ImportError("The _imagingft C module is not installed") try: - import _imagingft - core = _imagingft - del _imagingft + from PIL import _imagingft as core except ImportError: core = _imagingft_not_installed() @@ -55,21 +61,6 @@ except ImportError: # position according to dx, dy. # -------------------------------------------------------------------- -## -# The ImageFont module defines a class with the same name. -# Instances of this class store bitmap fonts, and are used with the -# text method of the ImageDraw class. -#

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

-# Starting with version 1.1.4, PIL can be configured to support -# TrueType and OpenType fonts. For earlier version, TrueType -# support is only available as part of the imToolkit package -# -# @see ImageDraw#ImageDraw.text -# @see pilfont class ImageFont: "PIL font wrapper" @@ -97,13 +88,13 @@ class ImageFont: def _load_pilfont_data(self, file, image): # read PILfont header - if file.readline() != "PILfont\n": + if file.readline() != b"PILfont\n": raise SyntaxError("Not a PILfont file") - d = string.split(file.readline(), ";") + d = file.readline().split(b";") self.info = [] # FIXME: should be a dictionary while True: s = file.readline() - if not s or s == "DATA\n": + if not s or s == b"DATA\n": break self.info.append(s) @@ -129,9 +120,18 @@ class ImageFont: class FreeTypeFont: "FreeType font wrapper (requires _imagingft service)" - def __init__(self, file, size, index=0, encoding=""): + def __init__(self, font=None, size=10, index=0, encoding="", file=None): # FIXME: use service provider instead - self.font = core.getfont(file, size, index, encoding) + if file: + if warnings: + warnings.warn('file parameter deprecated, please use font parameter instead.', DeprecationWarning) + font = file + + if isPath(font): + self.font = core.getfont(font, size, index, encoding) + else: + self.font_bytes = font.read() + self.font = core.getfont("", size, index, encoding, self.font_bytes) def getname(self): return self.font.family, self.font.style @@ -142,6 +142,9 @@ class FreeTypeFont: def getsize(self, text): return self.font.getsize(text)[0] + def getoffset(self, text): + return self.font.getsize(text)[1] + def getmask(self, text, mode=""): return self.getmask2(text, mode)[0] @@ -179,43 +182,50 @@ class TransposedFont: return im.transpose(self.orientation) return im -## -# Load font file. This function loads a font object from the given -# bitmap font file, and returns the corresponding font object. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. def load(filename): - "Load a font file." + """ + Load a font file. This function loads a font object from the given + bitmap font file, and returns the corresponding font object. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ f = ImageFont() f._load_pilfont(filename) return f -## -# Load a TrueType or OpenType font file, and create a font object. -# This function loads a font object from the given file, and creates -# a font object for a font of the given size. -#

-# This function requires the _imagingft service. -# -# @param filename A truetype font file. Under Windows, if the file -# is not found in this filename, the loader also looks in Windows -# fonts directory -# @param size The requested size, in points. -# @param index Which font face to load (default is first available face). -# @param encoding Which font encoding to use (default is Unicode). Common -# encodings are "unic" (Unicode), "symb" (Microsoft Symbol), "ADOB" -# (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). -# See the FreeType documentation for more information. -# @return A font object. -# @exception IOError If the file could not be read. -def truetype(filename, size, index=0, encoding=""): - "Load a truetype font file." +def truetype(font=None, size=10, index=0, encoding="", filename=None): + """ + Load a TrueType or OpenType font file, and create a font object. + This function loads a font object from the given file, and creates + a font object for a font of the given size. + + This function requires the _imagingft service. + + :param filename: A truetype font file. Under Windows, if the file + is not found in this filename, the loader also looks in + Windows :file:`fonts/` directory. + :param size: The requested size, in points. + :param index: Which font face to load (default is first available face). + :param encoding: Which font encoding to use (default is Unicode). Common + encodings are "unic" (Unicode), "symb" (Microsoft + Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert), + and "armn" (Apple Roman). See the FreeType documentation + for more information. + :return: A font object. + :exception IOError: If the file could not be read. + """ + + if filename: + if warnings: + warnings.warn('filename parameter deprecated, please use font parameter instead.', DeprecationWarning) + font = filename + try: - return FreeTypeFont(filename, size, index, encoding) + return FreeTypeFont(font, size, index, encoding) except IOError: if sys.platform == "win32": # check the windows font repository @@ -223,42 +233,47 @@ def truetype(filename, size, index=0, encoding=""): # 1.5.2's os.environ.get() windir = os.environ.get("WINDIR") if windir: - filename = os.path.join(windir, "fonts", filename) + filename = os.path.join(windir, "fonts", font) return FreeTypeFont(filename, size, index, encoding) raise -## -# Load font file. Same as load, but searches for a bitmap font along -# the Python path. -# -# @param filename Name of font file. -# @return A font object. -# @exception IOError If the file could not be read. -# @see #load def load_path(filename): - "Load a font file, searching along the Python path." + """ + Load font file. Same as :py:func:`~PIL.ImageFont.load`, but searches for a + bitmap font along the Python path. + + :param filename: Name of font file. + :return: A font object. + :exception IOError: If the file could not be read. + """ for dir in sys.path: - if Image.isDirectory(dir): + if isDirectory(dir): + if not isinstance(filename, str): + if bytes is str: + filename = filename.encode("utf-8") + else: + filename = filename.decode("utf-8") try: return load(os.path.join(dir, filename)) except IOError: pass raise IOError("cannot find font file") -## -# Load a (probably rather ugly) default font. -# -# @return A font object. def load_default(): - "Load a default font." - from StringIO import StringIO + """Load a "better than nothing" default font. + + .. versionadded:: 1.1.4 + + :return: A font object. + """ + from io import BytesIO import base64 f = ImageFont() f._load_pilfont_data( # courB08 - StringIO(base64.decodestring(''' + BytesIO(base64.decodestring(b''' UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -350,7 +365,7 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// +QAGAAIAzgAKANUAEw== -''')), Image.open(StringIO(base64.decodestring(''' +''')), Image.open(BytesIO(base64.decodestring(b''' iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g @@ -377,14 +392,15 @@ w7IkEbzhVQAAAABJRU5ErkJggg== ''')))) return f + if __name__ == "__main__": # create font data chunk for embedding import base64, os, sys font = "../Images/courB08" - print " f._load_pilfont_data(" - print " # %s" % os.path.basename(font) - print " StringIO(base64.decodestring('''" + print(" f._load_pilfont_data(") + print(" # %s" % os.path.basename(font)) + print(" BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pil", "rb"), sys.stdout) - print "''')), Image.open(StringIO(base64.decodestring('''" + print("''')), Image.open(BytesIO(base64.decodestring(b'''") base64.encode(open(font + ".pbm", "rb"), sys.stdout) - print "'''))))" + print("'''))))") diff --git a/PIL/ImageGL.py b/PIL/ImageGL.py deleted file mode 100644 index 5c58b6ca9..000000000 --- a/PIL/ImageGL.py +++ /dev/null @@ -1,28 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# OpenGL pixmap/texture interface (requires imToolkit OpenGL extensions) -# -# History: -# 2003-09-13 fl Added -# -# Copyright (c) Secret Labs AB 2003. -# -# See the README file for information on usage and redistribution. -# - -## -# OpenGL pixmap/texture interface (requires imToolkit OpenGL -# extensions.) -## - -import _imaginggl - -## -# Texture factory. - -class TextureFactory: - pass # overwritten by the _imaginggl module - -from _imaginggl import * diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py index 9bcd804d3..9bb190934 100644 --- a/PIL/ImageGrab.py +++ b/PIL/ImageGrab.py @@ -15,16 +15,8 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image -## -# (New in 1.1.3) The ImageGrab module can be used to copy -# the contents of the screen to a PIL image memory. -#

-# The current version works on Windows only.

-# -# @since 1.1.3 -## try: # built-in driver (1.1.3 and later) @@ -34,18 +26,10 @@ except AttributeError: import _grabscreen grabber = _grabscreen.grab -## -# (New in 1.1.3) Take a snapshot of the screen. The pixels inside the -# bounding box are returned as an "RGB" image. If the bounding box is -# omitted, the entire screen is copied. -# -# @param bbox What region to copy. Default is the entire screen. -# @return An image -# @since 1.1.3 def grab(bbox=None): size, data = grabber() - im = Image.fromstring( + im = Image.frombytes( "RGB", size, data, # RGB, 32-bit line padding, origo in lower left corner "raw", "BGR", (size[0]*3 + 3) & -4, -1 @@ -54,18 +38,12 @@ def grab(bbox=None): im = im.crop(bbox) return im -## -# (New in 1.1.4) Take a snapshot of the clipboard image, if any. -# -# @return An image, a list of filenames, or None if the clipboard does -# not contain image data or filenames. Note that if a list is -# returned, the filenames may not represent image files. -# @since 1.1.4 def grabclipboard(): debug = 0 # temporary interface data = Image.core.grabclipboard(debug) - if Image.isStringType(data): - import BmpImagePlugin, StringIO - return BmpImagePlugin.DibImageFile(StringIO.StringIO(data)) + if isinstance(data, bytes): + from PIL import BmpImagePlugin + import io + return BmpImagePlugin.DibImageFile(io.BytesIO(data)) return data diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py index 56c42a45a..b2355ed1d 100644 --- a/PIL/ImageMath.py +++ b/PIL/ImageMath.py @@ -15,13 +15,20 @@ # See the README file for information on usage and redistribution. # -import Image -import _imagingmath +from PIL import Image +from PIL import _imagingmath +import sys + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ VERBOSE = 0 def _isconstant(v): - return isinstance(v, type(0)) or isinstance(v, type(0.0)) + return isinstance(v, int) or isinstance(v, float) class _Operand: # wraps an image operand, providing standard operators @@ -38,7 +45,7 @@ class _Operand: elif im1.im.mode in ("I", "F"): return im1.im else: - raise ValueError, "unsupported mode: %s" % im1.im.mode + raise ValueError("unsupported mode: %s" % im1.im.mode) else: # argument was a constant if _isconstant(im1) and self.im.mode in ("1", "L", "I"): @@ -55,7 +62,7 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.unop(op, out.im.id, im1.im.id) else: # binary operation @@ -65,7 +72,7 @@ class _Operand: if im1.mode != "F": im1 = im1.convert("F") if im2.mode != "F": im2 = im2.convert("F") if im1.mode != im2.mode: - raise ValueError, "mode mismatch" + raise ValueError("mode mismatch") if im1.size != im2.size: # crop both arguments to a common size size = (min(im1.size[0], im2.size[0]), @@ -79,14 +86,20 @@ class _Operand: try: op = getattr(_imagingmath, op+"_"+im1.mode) except AttributeError: - raise TypeError, "bad operand type for '%s'" % op + raise TypeError("bad operand type for '%s'" % op) _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) return _Operand(out) # unary operators - def __nonzero__(self): + def __bool__(self): # an image is "true" if it contains at least one non-zero pixel return self.im.getbbox() is not None + + if bytes is str: + # Provide __nonzero__ for pre-Py3k + __nonzero__ = __bool__ + del __bool__ + def __abs__(self): return self.apply("abs", self) def __pos__(self): @@ -107,9 +120,9 @@ class _Operand: return self.apply("mul", self, other) def __rmul__(self, other): return self.apply("mul", other, self) - def __div__(self, other): + def __truediv__(self, other): return self.apply("div", self, other) - def __rdiv__(self, other): + def __rtruediv__(self, other): return self.apply("div", other, self) def __mod__(self, other): return self.apply("mod", self, other) @@ -120,6 +133,13 @@ class _Operand: def __rpow__(self, other): return self.apply("pow", other, self) + if bytes is str: + # Provide __div__ and __rdiv__ for pre-Py3k + __div__ = __truediv__ + __rdiv__ = __rtruediv__ + del __truediv__ + del __rtruediv__ + # bitwise def __invert__(self): return self.apply("invert", self) @@ -175,32 +195,33 @@ def imagemath_convert(self, mode): return _Operand(self.im.convert(mode)) ops = {} -for k, v in globals().items(): +for k, v in list(globals().items()): if k[:10] == "imagemath_": ops[k[10:]] = v -## -# Evaluates an image expression. -# -# @param expression A string containing a Python-style expression. -# @keyparam options Values to add to the evaluation context. You -# can either use a dictionary, or one or more keyword arguments. -# @return The evaluated expression. This is usually an image object, -# but can also be an integer, a floating point value, or a pixel -# tuple, depending on the expression. def eval(expression, _dict={}, **kw): + """ + Evaluates an image expression. + + :param expression: A string containing a Python-style expression. + :param options: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :return: The evaluated expression. This is usually an image object, but can + also be an integer, a floating point value, or a pixel tuple, + depending on the expression. + """ # build execution namespace args = ops.copy() args.update(_dict) args.update(kw) - for k, v in args.items(): + for k, v in list(args.items()): if hasattr(v, "im"): args[k] = _Operand(v) - import __builtin__ - out =__builtin__.eval(expression, args) + out = builtins.eval(expression, args) try: return out.im except AttributeError: diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py index 1d5df1c6d..c3931b525 100644 --- a/PIL/ImageMode.py +++ b/PIL/ImageMode.py @@ -36,7 +36,7 @@ class ModeDescriptor: def getmode(mode): if not _modes: # initialize mode cache - import Image + from PIL import Image # core modes for m, (basemode, basetype, bands) in Image._MODEINFO.items(): _modes[m] = ModeDescriptor(m, bands, basemode, basetype) diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py index b51d78e25..0d22f8c64 100644 --- a/PIL/ImageOps.py +++ b/PIL/ImageOps.py @@ -17,22 +17,16 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image +from PIL._util import isStringType import operator - -## -# (New in 1.1.3) The ImageOps module contains a number of -# 'ready-made' image processing operations. This module is somewhat -# experimental, and most operators only work on L and RGB images. -# -# @since 1.1.3 -## +from functools import reduce # # helpers def _border(border): - if type(border) is type(()): + if isinstance(border, tuple): if len(border) == 2: left, top = right, bottom = border elif len(border) == 4: @@ -42,8 +36,8 @@ def _border(border): return left, top, right, bottom def _color(color, mode): - if Image.isStringType(color): - import ImageColor + if isStringType(color): + from PIL import ImageColor color = ImageColor.getcolor(color, mode) return color @@ -56,25 +50,25 @@ def _lut(image, lut): lut = lut + lut + lut return image.point(lut) else: - raise IOError, "not supported for this image mode" + raise IOError("not supported for this image mode") # # actions -## -# Maximize (normalize) image contrast. This function calculates a -# histogram of the input image, removes cutoff percent of the -# lightest and darkest pixels from the histogram, and remaps the image -# so that the darkest pixel becomes black (0), and the lightest -# becomes white (255). -# -# @param image The image to process. -# @param cutoff How many percent to cut off from the histogram. -# @param ignore The background pixel value (use None for no background). -# @return An image. def autocontrast(image, cutoff=0, ignore=None): - "Maximize image contrast, based on histogram" + """ + Maximize (normalize) image contrast. This function calculates a + histogram of the input image, removes **cutoff** percent of the + lightest and darkest pixels from the histogram, and remaps the image + so that the darkest pixel becomes black (0), and the lightest + becomes white (255). + + :param image: The image to process. + :param cutoff: How many percent to cut off from the histogram. + :param ignore: The background pixel value (use None for no background). + :return: An image. + """ histogram = image.histogram() lut = [] for layer in range(0, len(histogram), 256): @@ -94,7 +88,7 @@ def autocontrast(image, cutoff=0, ignore=None): for ix in range(256): n = n + h[ix] # remove cutoff% pixels from the low end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for lo in range(256): if cut > h[lo]: cut = cut - h[lo] @@ -105,7 +99,7 @@ def autocontrast(image, cutoff=0, ignore=None): if cut <= 0: break # remove cutoff% samples from the hi end - cut = n * cutoff / 100 + cut = n * cutoff // 100 for hi in range(255, -1, -1): if cut > h[hi]: cut = cut - h[hi] @@ -124,7 +118,7 @@ def autocontrast(image, cutoff=0, ignore=None): break if hi <= lo: # don't bother - lut.extend(range(256)) + lut.extend(list(range(256))) else: scale = 255.0 / (hi - lo) offset = -lo * scale @@ -137,101 +131,103 @@ def autocontrast(image, cutoff=0, ignore=None): lut.append(ix) return _lut(image, lut) -## -# Colorize grayscale image. The black and white -# arguments should be RGB tuples; this function calculates a colour -# wedge mapping all black pixels in the source image to the first -# colour, and all white pixels to the second colour. -# -# @param image The image to colourize. -# @param black The colour to use for black input pixels. -# @param white The colour to use for white input pixels. -# @return An image. def colorize(image, black, white): - "Colorize a grayscale image" + """ + Colorize grayscale image. The **black** and **white** + arguments should be RGB tuples; this function calculates a color + wedge mapping all black pixels in the source image to the first + color, and all white pixels to the second color. + + :param image: The image to colorize. + :param black: The color to use for black input pixels. + :param white: The color to use for white input pixels. + :return: An image. + """ assert image.mode == "L" black = _color(black, "RGB") white = _color(white, "RGB") red = []; green = []; blue = [] for i in range(256): - red.append(black[0]+i*(white[0]-black[0])/255) - green.append(black[1]+i*(white[1]-black[1])/255) - blue.append(black[2]+i*(white[2]-black[2])/255) + red.append(black[0]+i*(white[0]-black[0])//255) + green.append(black[1]+i*(white[1]-black[1])//255) + blue.append(black[2]+i*(white[2]-black[2])//255) image = image.convert("RGB") return _lut(image, red + green + blue) -## -# Remove border from image. The same amount of pixels are removed -# from all four sides. This function works on all image modes. -# -# @param image The image to crop. -# @param border The number of pixels to remove. -# @return An image. -# @see Image#Image.crop def crop(image, border=0): - "Crop border off image" + """ + Remove border from image. The same amount of pixels are removed + from all four sides. This function works on all image modes. + + .. seealso:: :py:meth:`~PIL.Image.Image.crop` + + :param image: The image to crop. + :param border: The number of pixels to remove. + :return: An image. + """ left, top, right, bottom = _border(border) return image.crop( (left, top, image.size[0]-right, image.size[1]-bottom) ) -## -# Deform the image. -# -# @param image The image to deform. -# @param deformer A deformer object. Any object that implements a -# getmesh method can be used. -# @param resample What resampling filter to use. -# @return An image. def deform(image, deformer, resample=Image.BILINEAR): - "Deform image using the given deformer" + """ + Deform the image. + + :param image: The image to deform. + :param deformer: A deformer object. Any object that implements a + **getmesh** method can be used. + :param resample: What resampling filter to use. + :return: An image. + """ return image.transform( image.size, Image.MESH, deformer.getmesh(image), resample ) -## -# Equalize the image histogram. This function applies a non-linear -# mapping to the input image, in order to create a uniform -# distribution of grayscale values in the output image. -# -# @param image The image to equalize. -# @param mask An optional mask. If given, only the pixels selected by -# the mask are included in the analysis. -# @return An image. def equalize(image, mask=None): - "Equalize image histogram" + """ + Equalize the image histogram. This function applies a non-linear + mapping to the input image, in order to create a uniform + distribution of grayscale values in the output image. + + :param image: The image to equalize. + :param mask: An optional mask. If given, only the pixels selected by + the mask are included in the analysis. + :return: An image. + """ if image.mode == "P": image = image.convert("RGB") h = image.histogram(mask) lut = [] for b in range(0, len(h), 256): - histo = filter(None, h[b:b+256]) + histo = [_f for _f in h[b:b+256] if _f] if len(histo) <= 1: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - step = (reduce(operator.add, histo) - histo[-1]) / 255 + step = (reduce(operator.add, histo) - histo[-1]) // 255 if not step: - lut.extend(range(256)) + lut.extend(list(range(256))) else: - n = step / 2 + n = step // 2 for i in range(256): - lut.append(n / step) + lut.append(n // step) n = n + h[i+b] return _lut(image, lut) -## -# Add border to the image -# -# @param image The image to expand. -# @param border Border width, in pixels. -# @param fill Pixel fill value (a colour value). Default is 0 (black). -# @return An image. def expand(image, border=0, fill=0): + """ + Add border to the image + + :param image: The image to expand. + :param border: Border width, in pixels. + :param fill: Pixel fill value (a color value). Default is 0 (black). + :return: An image. + """ "Add border to image" left, top, right, bottom = _border(border) width = left + image.size[0] + right @@ -240,33 +236,32 @@ def expand(image, border=0, fill=0): out.paste(image, (left, top)) return out -## -# Returns a sized and cropped version of the image, cropped to the -# requested aspect ratio and size. -#

-# The fit function was contributed by Kevin Cazabon. -# -# @param size The requested output size in pixels, given as a -# (width, height) tuple. -# @param method What resampling method to use. Default is Image.NEAREST. -# @param bleed Remove a border around the outside of the image (from all -# four edges. The value is a decimal percentage (use 0.01 for one -# percent). The default value is 0 (no border). -# @param centering Control the cropping position. Use (0.5, 0.5) for -# center cropping (e.g. if cropping the width, take 50% off of the -# left side, and therefore 50% off the right side). (0.0, 0.0) -# will crop from the top left corner (i.e. if cropping the width, -# take all of the crop off of the right side, and if cropping the -# height, take all of it off the bottom). (1.0, 0.0) will crop -# from the bottom left corner, etc. (i.e. if cropping the width, -# take all of the crop off the left side, and if cropping the height -# take none from the top, and therefore all off the bottom). -# @return An image. def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): """ - This method returns a sized and cropped version of the image, - cropped to the aspect ratio and size that you request. + Returns a sized and cropped version of the image, cropped to the + requested aspect ratio and size. + + This function was contributed by Kevin Cazabon. + + :param size: The requested output size in pixels, given as a + (width, height) tuple. + :param method: What resampling method to use. Default is + :py:attr:`PIL.Image.NEAREST`. + :param bleed: Remove a border around the outside of the image (from all + four edges. The value is a decimal percentage (use 0.01 for + one percent). The default value is 0 (no border). + :param centering: Control the cropping position. Use (0.5, 0.5) for + center cropping (e.g. if cropping the width, take 50% off + of the left side, and therefore 50% off the right side). + (0.0, 0.0) will crop from the top left corner (i.e. if + cropping the width, take all of the crop off of the right + side, and if cropping the height, take all of it off the + bottom). (1.0, 0.0) will crop from the bottom left + corner, etc. (i.e. if cropping the width, take all of the + crop off the left side, and if cropping the height take + none from the top, and therefore all off the bottom). + :return: An image. """ # by Kevin Cazabon, Feb 17/2000 @@ -274,7 +269,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # http://www.cazabon.com # ensure inputs are valid - if type(centering) != type([]): + if not isinstance(centering, list): centering = [centering[0], centering[1]] if centering[0] > 1.0 or centering[0] < 0.0: @@ -294,10 +289,12 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): int((float(bleed) * float(image.size[1])) + 0.5) ) - liveArea = ( - bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, - image.size[1] - bleedPixels[1] - 1 - ) + liveArea = (0, 0, image.size[0], image.size[1]) + if bleed > 0.0: + liveArea = ( + bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, + image.size[1] - bleedPixels[1] - 1 + ) liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1]) @@ -332,73 +329,73 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): # resize the image and return it return out.resize(size, method) -## -# Flip the image vertically (top to bottom). -# -# @param image The image to flip. -# @return An image. def flip(image): - "Flip image vertically" + """ + Flip the image vertically (top to bottom). + + :param image: The image to flip. + :return: An image. + """ return image.transpose(Image.FLIP_TOP_BOTTOM) -## -# Convert the image to grayscale. -# -# @param image The image to convert. -# @return An image. def grayscale(image): - "Convert to grayscale" + """ + Convert the image to grayscale. + + :param image: The image to convert. + :return: An image. + """ return image.convert("L") -## -# Invert (negate) the image. -# -# @param image The image to invert. -# @return An image. def invert(image): - "Invert image (negate)" + """ + Invert (negate) the image. + + :param image: The image to invert. + :return: An image. + """ lut = [] for i in range(256): lut.append(255-i) return _lut(image, lut) -## -# Flip image horizontally (left to right). -# -# @param image The image to mirror. -# @return An image. def mirror(image): - "Flip image horizontally" + """ + Flip image horizontally (left to right). + + :param image: The image to mirror. + :return: An image. + """ return image.transpose(Image.FLIP_LEFT_RIGHT) -## -# Reduce the number of bits for each colour channel. -# -# @param image The image to posterize. -# @param bits The number of bits to keep for each channel (1-8). -# @return An image. def posterize(image, bits): - "Reduce the number of bits per color channel" + """ + Reduce the number of bits for each color channel. + + :param image: The image to posterize. + :param bits: The number of bits to keep for each channel (1-8). + :return: An image. + """ lut = [] mask = ~(2**(8-bits)-1) for i in range(256): lut.append(i & mask) return _lut(image, lut) -## -# Invert all pixel values above a threshold. -# -# @param image The image to posterize. -# @param threshold All pixels above this greyscale level are inverted. -# @return An image. def solarize(image, threshold=128): - "Invert all values above threshold" + """ + Invert all pixel values above a threshold. + + :param image: The image to posterize. + :param threshold: All pixels above this greyscale level are inverted. + :return: An image. + """ lut = [] for i in range(256): if i < threshold: diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py index 6efee2998..61affdb19 100644 --- a/PIL/ImagePalette.py +++ b/PIL/ImagePalette.py @@ -17,49 +17,64 @@ # import array -import Image, ImageColor +from PIL import Image, ImageColor -## -# Colour palette wrapper for palette mapped images. class ImagePalette: - "Colour palette for palette mapped images" + "Color palette for palette mapped images" def __init__(self, mode = "RGB", palette = None): self.mode = mode self.rawmode = None # if set, palette contains raw data - self.palette = palette or range(256)*len(self.mode) + self.palette = palette or list(range(256))*len(self.mode) self.colors = {} self.dirty = None if len(self.mode)*256 != len(self.palette): - raise ValueError, "wrong palette size" + raise ValueError("wrong palette size") def getdata(self): - # experimental: get palette contents in format suitable - # for the low-level im.putpalette primitive + """ + Get palette contents in format suitable # for the low-level + ``im.putpalette`` primitive. + + .. warning:: This method is experimental. + """ if self.rawmode: return self.rawmode, self.palette - return self.mode + ";L", self.tostring() + return self.mode + ";L", self.tobytes() - def tostring(self): - # experimental: convert palette to string + def tobytes(self): + """Convert palette to bytes. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isStringType(self.palette): + if isinstance(self.palette, bytes): return self.palette - return array.array("B", self.palette).tostring() + arr = array.array("B", self.palette) + if hasattr(arr, 'tobytes'): + #py3k has a tobytes, tostring is deprecated. + return arr.tobytes() + return arr.tostring() + + # Declare tostring as an alias for tobytes + tostring = tobytes def getcolor(self, color): - # experimental: given an rgb tuple, allocate palette entry + """Given an rgb tuple, allocate palette entry. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") - if Image.isTupleType(color): + if isinstance(color, tuple): try: return self.colors[color] except KeyError: # allocate new color slot - if Image.isStringType(self.palette): - self.palette = map(int, self.palette) + if isinstance(self.palette, bytes): + self.palette = [int(x) for x in self.palette] index = len(self.colors) if index >= 256: raise ValueError("cannot allocate more than 256 colors") @@ -73,10 +88,13 @@ class ImagePalette: raise ValueError("unknown color specifier: %r" % color) def save(self, fp): - # (experimental) save palette to text file + """Save palette to text file. + + .. warning:: This method is experimental. + """ if self.rawmode: raise ValueError("palette contains raw palette data") - if type(fp) == type(""): + if isinstance(fp, str): fp = open(fp, "w") fp.write("# Palette\n") fp.write("# Mode: %s\n" % self.mode) @@ -104,7 +122,7 @@ def _make_linear_lut(black, white): lut = [] if black == 0: for i in range(256): - lut.append(white*i/255) + lut.append(white*i//255) else: raise NotImplementedError # FIXME return lut @@ -119,7 +137,7 @@ def new(mode, data): return Image.core.new_palette(mode, data) def negative(mode="RGB"): - palette = range(256) + palette = list(range(256)) palette.reverse() return ImagePalette(mode, palette * len(mode)) @@ -138,7 +156,7 @@ def sepia(white="#fff0c0"): return ImagePalette("RGB", r + g + b) def wedge(mode="RGB"): - return ImagePalette(mode, range(256) * len(mode)) + return ImagePalette(mode, list(range(256)) * len(mode)) def load(filename): @@ -150,35 +168,38 @@ def load(filename): if not lut: try: - import GimpPaletteFile + from PIL import GimpPaletteFile fp.seek(0) p = GimpPaletteFile.GimpPaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: try: - import GimpGradientFile + from PIL import GimpGradientFile fp.seek(0) p = GimpGradientFile.GimpGradientFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + #import traceback + #traceback.print_exc() pass if not lut: try: - import PaletteFile + from PIL import PaletteFile fp.seek(0) p = PaletteFile.PaletteFile(fp) lut = p.getpalette() except (SyntaxError, ValueError): + import traceback + traceback.print_exc() pass if not lut: - raise IOError, "cannot load palette" + raise IOError("cannot load palette") return lut # data, rawmode - - -# add some psuedocolour palettes as well diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py index 721fd9449..656d5ce61 100644 --- a/PIL/ImagePath.py +++ b/PIL/ImagePath.py @@ -14,19 +14,14 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image + + +# the Python class below is overridden by the C implementation. -## -# Path wrapper. class Path: - ## - # Creates a path object. - # - # @param xy Sequence. The sequence can contain 2-tuples [(x, y), ...] - # or a flat list of numbers [x, y, ...]. - def __init__(self, xy): pass diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py index 50ee07d6c..ca8b14b5c 100644 --- a/PIL/ImageQt.py +++ b/PIL/ImageQt.py @@ -8,6 +8,7 @@ # 2006-06-03 fl: created # 2006-06-04 fl: inherit from QImage instead of wrapping it # 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) # # Copyright (c) 2006 by Secret Labs AB # Copyright (c) 2006 by Fredrik Lundh @@ -15,17 +16,21 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image +from PIL._util import isPath -from PyQt4.QtGui import QImage, qRgb +try: + from PyQt5.QtGui import QImage, qRgba +except: + from PyQt4.QtGui import QImage, qRgba ## # (Internal) Turns an RGB color into a Qt compatible color integer. -def rgb(r, g, b): +def rgb(r, g, b, a=255): # use qRgb to pack the colors, and then turn the resulting long # into a negative integer with the same bitpattern. - return (qRgb(r, g, b) & 0xffffff) - 0x1000000 + return (qRgba(r, g, b, a) & 0xffffffff) ## # An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage @@ -45,7 +50,7 @@ class ImageQt(QImage): if hasattr(im, "toUtf8"): # FIXME - is this really the best way to do this? im = unicode(im.toUtf8(), "utf-8") - if Image.isStringType(im): + if isPath(im): im = Image.open(im) if im.mode == "1": @@ -62,11 +67,11 @@ class ImageQt(QImage): for i in range(0, len(palette), 3): colortable.append(rgb(*palette[i:i+3])) elif im.mode == "RGB": - data = im.tostring("raw", "BGRX") + data = im.tobytes("raw", "BGRX") format = QImage.Format_RGB32 elif im.mode == "RGBA": try: - data = im.tostring("raw", "BGRA") + data = im.tobytes("raw", "BGRA") except SystemError: # workaround for earlier versions r, g, b, a = im.split() @@ -76,7 +81,7 @@ class ImageQt(QImage): raise ValueError("unsupported image mode %r" % im.mode) # must keep a reference, or Qt will crash! - self.__data = data or im.tostring() + self.__data = data or im.tobytes() QImage.__init__(self, self.__data, im.size[0], im.size[1], format) diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py index e94ca0b1e..513c9247b 100644 --- a/PIL/ImageSequence.py +++ b/PIL/ImageSequence.py @@ -14,15 +14,18 @@ # ## -# This class implements an iterator object that can be used to loop -# over an image sequence. class Iterator: + """ + This class implements an iterator object that can be used to loop + over an image sequence. - ## - # Create an iterator. - # - # @param im An image object. + You can use the ``[]`` operator to access elements by index. This operator + will raise an :py:exc:`IndexError` if you try to access a nonexistent + frame. + + :param im: An image object. + """ def __init__(self, im): if not hasattr(im, "seek"): diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py index 05270f440..e81866bac 100644 --- a/PIL/ImageShow.py +++ b/PIL/ImageShow.py @@ -12,9 +12,16 @@ # See the README file for information on usage and redistribution. # -import Image +from __future__ import print_function + +from PIL import Image import os, sys +if(sys.version_info >= (3, 3)): + from shlex import quote +else: + from pipes import quote + _viewers = [] def register(viewer, order=1): @@ -63,7 +70,7 @@ class Viewer: if base != image.mode and image.mode != "1": image = image.convert(base) - self.show_image(image, **options) + return self.show_image(image, **options) # hook methods @@ -96,7 +103,9 @@ if sys.platform == "win32": class WindowsViewer(Viewer): format = "BMP" def get_command(self, file, **options): - return "start /wait %s && del /f %s" % (file, file) + return ('start "Pillow" /WAIT "%s" ' + '&& ping -n 2 127.0.0.1 >NUL ' + '&& del /f "%s"' % (file, file)) register(WindowsViewer) @@ -108,7 +117,7 @@ elif sys.platform == "darwin": # on darwin open returns immediately resulting in the temp # file removal while app is opening command = "open -a /Applications/Preview.app" - command = "(%s %s; sleep 20; rm -f %s)&" % (command, file, file) + command = "(%s %s; sleep 20; rm -f %s)&" % (command, quote(file), quote(file)) return command register(MacViewer) @@ -131,7 +140,7 @@ else: class UnixViewer(Viewer): def show_file(self, file, **options): command, executable = self.get_command_ex(file, **options) - command = "(%s %s; rm -f %s)&" % (command, file, file) + command = "(%s %s; rm -f %s)&" % (command, quote(file), quote(file)) os.system(command) return 1 @@ -151,8 +160,7 @@ else: # imagemagick's display command instead. command = executable = "xv" if title: - # FIXME: do full escaping - command = command + " -name \"%s\"" % title + command = command + " -name %s" % quote(title) return command, executable if which("xv"): @@ -160,4 +168,4 @@ else: if __name__ == "__main__": # usage: python ImageShow.py imagefile [title] - print show(Image.open(sys.argv[1]), *sys.argv[2:]) + print(show(Image.open(sys.argv[1]), *sys.argv[2:])) diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py index 9ebdab030..ef63b7857 100644 --- a/PIL/ImageStat.py +++ b/PIL/ImageStat.py @@ -21,28 +21,12 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image import operator, math +from functools import reduce -## -# The ImageStat module calculates global statistics for an -# image, or a region of an image. -## - -## -# Calculate statistics for the given image. If a mask is included, -# only the regions covered by that mask are included in the -# statistics. class Stat: - "Get image or feature statistics" - - ## - # Create a statistics object. - # - # @def __init__(image, mask=None) - # @param image A PIL image, or a precalculate histogram. - # @param mask An optional mask. def __init__(self, image_or_list, mask = None): try: @@ -52,14 +36,14 @@ class Stat: self.h = image_or_list.histogram() except AttributeError: self.h = image_or_list # assume it to be a histogram list - if type(self.h) != type([]): - raise TypeError, "first argument must be image or list" - self.bands = range(len(self.h) / 256) + if not isinstance(self.h, list): + raise TypeError("first argument must be image or list") + self.bands = list(range(len(self.h) // 256)) def __getattr__(self, id): "Calculate missing attribute" if id[:4] == "_get": - raise AttributeError, id + raise AttributeError(id) # calculate missing attribute v = getattr(self, "_get" + id)() setattr(self, id, v) @@ -126,7 +110,7 @@ class Stat: v = [] for i in self.bands: s = 0 - l = self.count[i]/2 + l = self.count[i]//2 b = i * 256 for j in range(256): s = s + self.h[b+j] diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py index 8618139a5..1e81d240e 100644 --- a/PIL/ImageTk.py +++ b/PIL/ImageTk.py @@ -25,15 +25,15 @@ # See the README file for information on usage and redistribution. # -import Tkinter, Image +try: + import tkinter +except ImportError: + import Tkinter + tkinter = Tkinter + del Tkinter + +from PIL import Image -## -# The ImageTk module contains support to create and modify -# Tkinter BitmapImage and PhotoImage objects. -#

-# For examples, see the demo programs in the Scripts -# directory. -## # -------------------------------------------------------------------- # Check for Tkinter interface hooks @@ -45,48 +45,45 @@ def _pilbitmap_check(): if _pilbitmap_ok is None: try: im = Image.new("1", (1,1)) - Tkinter.BitmapImage(data="PIL:%d" % im.im.id) + tkinter.BitmapImage(data="PIL:%d" % im.im.id) _pilbitmap_ok = 1 - except Tkinter.TclError: + except tkinter.TclError: _pilbitmap_ok = 0 return _pilbitmap_ok # -------------------------------------------------------------------- # PhotoImage -## -# Creates a Tkinter-compatible photo image. This can be used -# everywhere Tkinter expects an image object. If the image is an RGBA -# image, pixels having alpha 0 are treated as transparent. - class PhotoImage: + """ + A Tkinter-compatible photo image. This can be used + everywhere Tkinter expects an image object. If the image is an RGBA + image, pixels having alpha 0 are treated as transparent. - ## - # Create a photo image object. The constructor takes either - # a PIL image, or a mode and a size. Alternatively, you can - # use the file or data options to initialize - # the photo image object. - #

- # @def __init__(image=None, size=None, **options) - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. - # @param size If the first argument is a mode string, this - # defines the size of the image. - # @keyparam file A filename to load the image from (using - # Image.open(file)). - # @keyparam data An 8-bit string containing image data (as - # loaded from an image file). + The constructor takes either a PIL image, or a mode and a size. + Alternatively, you can use the **file** or **data** options to initialize + the photo image object. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. + :param size: If the first argument is a mode string, this defines the size + of the image. + :keyword file: A filename to load the image from (using + ``Image.open(file)``). + :keyword data: An 8-bit string containing image data (as loaded from an + image file). + """ def __init__(self, image=None, size=None, **kw): # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] if hasattr(image, "mode") and hasattr(image, "size"): @@ -110,7 +107,7 @@ class PhotoImage: self.__mode = mode self.__size = size - self.__photo = apply(Tkinter.PhotoImage, (), kw) + self.__photo = tkinter.PhotoImage(**kw) self.tk = self.__photo.tk if image: self.paste(image) @@ -123,44 +120,48 @@ class PhotoImage: except: pass # ignore internal errors - ## - # Get the Tkinter photo image identifier. This method is - # automatically called by Tkinter whenever a PhotoImage object is - # passed to a Tkinter method. - # - # @return A Tkinter photo image identifier (a string). def __str__(self): + """ + Get the Tkinter photo image identifier. This method is automatically + called by Tkinter whenever a PhotoImage object is passed to a Tkinter + method. + + :return: A Tkinter photo image identifier (a string). + """ return str(self.__photo) - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Paste a PIL image into the photo image. Note that this can - # be very slow if the photo image is displayed. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the photo image. Note that this can + be very slow if the photo image is displayed. + + :param im: A PIL image. The size must match the target region. If the + mode does not match, the image is converted to the mode of + the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and lower pixel + coordinate. If None is given instead of a tuple, all of + the image is assumed. + """ # convert to blittable im.load() @@ -175,50 +176,47 @@ class PhotoImage: try: tk.call("PyImagingPhoto", self.__photo, block.id) - except Tkinter.TclError, v: + except tkinter.TclError as v: # activate Tkinter hook try: - import _imagingtk + from PIL import _imagingtk try: _imagingtk.tkinit(tk.interpaddr(), 1) except AttributeError: _imagingtk.tkinit(id(tk), 0) tk.call("PyImagingPhoto", self.__photo, block.id) - except (ImportError, AttributeError, Tkinter.TclError): + except (ImportError, AttributeError, tkinter.TclError): raise # configuration problem; cannot attach to Tkinter # -------------------------------------------------------------------- # BitmapImage -## -# Create a Tkinter-compatible bitmap image. This can be used -# everywhere Tkinter expects an image object. class BitmapImage: + """ - ## - # Create a Tkinter-compatible bitmap image. - #

- # The given image must have mode "1". Pixels having value 0 are - # treated as transparent. Options, if any, are passed on to - # Tkinter. The most commonly used option is foreground, - # which is used to specify the colour for the non-transparent - # parts. See the Tkinter documentation for information on how to - # specify colours. - # - # @def __init__(image=None, **options) - # @param image A PIL image. + A Tkinter-compatible bitmap image. This can be used everywhere Tkinter + expects an image object. + + The given image must have mode "1". Pixels having value 0 are treated as + transparent. Options, if any, are passed on to Tkinter. The most commonly + used option is **foreground**, which is used to specify the color for the + non-transparent parts. See the Tkinter documentation for information on + how to specify colours. + + :param image: A PIL image. + """ def __init__(self, image=None, **kw): # Tk compatibility: file or data if image is None: - if kw.has_key("file"): + if "file" in kw: image = Image.open(kw["file"]) del kw["file"] - elif kw.has_key("data"): - from StringIO import StringIO - image = Image.open(StringIO(kw["data"])) + elif "data" in kw: + from io import BytesIO + image = Image.open(BytesIO(kw["data"])) del kw["data"] self.__mode = image.mode @@ -232,7 +230,7 @@ class BitmapImage: else: # slow but safe way kw["data"] = image.tobitmap() - self.__photo = apply(Tkinter.BitmapImage, (), kw) + self.__photo = tkinter.BitmapImage(**kw) def __del__(self): name = self.__photo.name @@ -242,36 +240,38 @@ class BitmapImage: except: pass # ignore internal errors - ## - # Get the width of the image. - # - # @return The width, in pixels. def width(self): + """ + Get the width of the image. + + :return: The width, in pixels. + """ return self.__size[0] - ## - # Get the height of the image. - # - # @return The height, in pixels. def height(self): + """ + Get the height of the image. + + :return: The height, in pixels. + """ return self.__size[1] - ## - # Get the Tkinter bitmap image identifier. This method is - # automatically called by Tkinter whenever a BitmapImage object - # is passed to a Tkinter method. - # - # @return A Tkinter bitmap image identifier (a string). def __str__(self): + """ + Get the Tkinter bitmap image identifier. This method is automatically + called by Tkinter whenever a BitmapImage object is passed to a Tkinter + method. + + :return: A Tkinter bitmap image identifier (a string). + """ return str(self.__photo) -## -# Copies the contents of a PhotoImage to a PIL image memory. def getimage(photo): + """Copies the contents of a PhotoImage to a PIL image memory.""" photo.tk.call("PyImagingPhotoGet", photo) # -------------------------------------------------------------------- @@ -279,18 +279,18 @@ def getimage(photo): def _show(image, title): - class UI(Tkinter.Label): + class UI(tkinter.Label): def __init__(self, master, im): if im.mode == "1": self.image = BitmapImage(im, foreground="white", master=master) else: self.image = PhotoImage(im, master=master) - Tkinter.Label.__init__(self, master, image=self.image, + tkinter.Label.__init__(self, master, image=self.image, bg="black", bd=0) - if not Tkinter._default_root: - raise IOError, "tkinter not initialized" - top = Tkinter.Toplevel() + if not tkinter._default_root: + raise IOError("tkinter not initialized") + top = tkinter.Toplevel() if title: top.title(title) UI(top, image).pack() diff --git a/PIL/ImageTransform.py b/PIL/ImageTransform.py index cc323d3b9..5a8f9e9ec 100644 --- a/PIL/ImageTransform.py +++ b/PIL/ImageTransform.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import Image +from PIL import Image class Transform(Image.ImageTransformHandler): def __init__(self, data): diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py index f98725f74..aa90b887b 100644 --- a/PIL/ImageWin.py +++ b/PIL/ImageWin.py @@ -17,46 +17,52 @@ # See the README file for information on usage and redistribution. # -import Image +import warnings +from PIL import Image -## -# The ImageWin module contains support to create and display -# images under Windows 95/98, NT, 2000 and later. class HDC: + """ + Wraps a HDC integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods. + """ def __init__(self, dc): self.dc = dc def __int__(self): return self.dc class HWND: + """ + Wraps a HWND integer. The resulting object can be passed to the + :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` + methods, instead of a DC. + """ def __init__(self, wnd): self.wnd = wnd def __int__(self): return self.wnd -## -# Create a Windows bitmap with the given mode and size. The mode can -# be one of "1", "L", "P", or "RGB". -# -# If the display requires a palette, this constructor creates a -# suitable palette and associates it with the image. For an "L" image, -# 128 greylevels are allocated. For an "RGB" image, a 6x6x6 colour -# cube is used, together with 20 greylevels. -# -# To make sure that palettes work properly under Windows, you must -# call the palette method upon certain events from Windows. class Dib: + """ + A Windows bitmap with the given mode and size. The mode can be one of "1", + "L", "P", or "RGB". - ## - # Create Windows bitmap. - # - # @param image Either a PIL image, or a mode string. If a - # mode string is used, a size must also be given. The - # mode can be one of "1", "L", "P", or "RGB". - # @param size If the first argument is a mode string, this - # defines the size of the image. + If the display requires a palette, this constructor creates a suitable + palette and associates it with the image. For an "L" image, 128 greylevels + are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together + with 20 greylevels. + + To make sure that palettes work properly under Windows, you must call the + **palette** method upon certain events from Windows. + + :param image: Either a PIL image, or a mode string. If a mode string is + used, a size must also be given. The mode can be one of "1", + "L", "P", or "RGB". + :param size: If the first argument is a mode string, this + defines the size of the image. + """ def __init__(self, image, size=None): if hasattr(image, "mode") and hasattr(image, "size"): @@ -73,15 +79,15 @@ class Dib: if image: self.paste(image) - ## - # Copy the bitmap contents to a device context. - # - # @param handle Device context (HDC), cast to a Python integer, - # or a HDC or HWND instance. In PythonWin, you can use the - # GetHandleAttrib method of the CDC class to get - # a suitable handle. def expose(self, handle): + """ + Copy the bitmap contents to a device context. + + :param handle: Device context (HDC), cast to a Python integer, or a HDC + or HWND instance. In PythonWin, you can use the + :py:meth:`CDC.GetHandleAttrib` to get a suitable handle. + """ if isinstance(handle, HWND): dc = self.image.getdc(handle) try: @@ -93,6 +99,15 @@ class Dib: return result def draw(self, handle, dst, src=None): + """ + Same as expose, but allows you to specify where to draw the image, and + what part of it to draw. + + The destination and source areas are given as 4-tuple rectangles. If + the source is omitted, the entire image is copied. If the source and + the destination have different sizes, the image is resized as + necessary. + """ if not src: src = (0,0) + self.size if isinstance(handle, HWND): @@ -105,22 +120,22 @@ class Dib: result = self.image.draw(handle, dst, src) return result - ## - # Installs the palette associated with the image in the - # given device context. - #

- # This method should be called upon QUERYNEWPALETTE - # and PALETTECHANGED events from Windows. If this - # method returns a non-zero value, one or more display - # palette entries were changed, and the image should be - # redrawn. - # - # @param handle Device context (HDC), cast to a Python integer, - # or an HDC or HWND instance. - # @return A true value if one or more entries were changed - # (this indicates that the image should be redrawn). def query_palette(self, handle): + """ + Installs the palette associated with the image in the given device + context. + + This method should be called upon **QUERYNEWPALETTE** and + **PALETTECHANGED** events from Windows. If this method returns a + non-zero value, one or more display palette entries were changed, and + the image should be redrawn. + + :param handle: Device context (HDC), cast to a Python integer, or an + HDC or HWND instance. + :return: A true value if one or more entries were changed (this + indicates that the image should be redrawn). + """ if isinstance(handle, HWND): handle = self.image.getdc(handle) try: @@ -131,17 +146,18 @@ class Dib: result = self.image.query_palette(handle) return result - ## - # Paste a PIL image into the bitmap image. - # - # @param im A PIL image. The size must match the target region. - # If the mode does not match, the image is converted to the - # mode of the bitmap image. - # @param box A 4-tuple defining the left, upper, right, and - # lower pixel coordinate. If None is given instead of a - # tuple, all of the image is assumed. def paste(self, im, box=None): + """ + Paste a PIL image into the bitmap image. + + :param im: A PIL image. The size must match the target region. + If the mode does not match, the image is converted to the + mode of the bitmap image. + :param box: A 4-tuple defining the left, upper, right, and + lower pixel coordinate. If None is given instead of a + tuple, all of the image is assumed. + """ im.load() if self.mode != im.mode: im = im.convert(self.mode) @@ -150,23 +166,43 @@ class Dib: else: self.image.paste(im.im) - ## - # Load display memory contents from string buffer. - # - # @param buffer A string buffer containing display data (usually - # data returned from tostring) - def fromstring(self, buffer): - return self.image.fromstring(buffer) + def frombytes(self, buffer): + """ + Load display memory contents from byte data. + + :param buffer: A buffer containing display data (usually + data returned from tobytes) + """ + return self.image.frombytes(buffer) + + + def tobytes(self): + """ + Copy display memory contents to bytes object. + + :return: A bytes object containing display data. + """ + return self.image.tobytes() ## - # Copy display memory contents to string buffer. - # - # @return A string buffer containing display data. + # Deprecated aliases to frombytes & tobytes. + + def fromstring(self, *args, **kw): + warnings.warn( + 'fromstring() is deprecated. Please call frombytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.frombytes(*args, **kw) def tostring(self): - return self.image.tostring() - + warnings.warn( + 'tostring() is deprecated. Please call tobytes() instead.', + DeprecationWarning, + stacklevel=2 + ) + return self.tobytes() ## # Create a Window with the given title size. @@ -179,7 +215,7 @@ class Window: ) def __dispatcher(self, action, *args): - return apply(getattr(self, "ui_handle_" + action), args) + return getattr(self, "ui_handle_" + action)(*args) def ui_handle_clear(self, dc, x0, y0, x1, y1): pass diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py index bf5611b8a..e68b00344 100644 --- a/PIL/ImtImagePlugin.py +++ b/PIL/ImtImagePlugin.py @@ -19,12 +19,12 @@ __version__ = "0.2" import re -import Image, ImageFile +from PIL import Image, ImageFile # # -------------------------------------------------------------------- -field = re.compile(r"([a-z]*) ([^ \r\n]*)") +field = re.compile(br"([a-z]*) ([^ \r\n]*)") ## # Image plugin for IM Tools images. @@ -39,19 +39,19 @@ class ImtImageFile(ImageFile.ImageFile): # Quick rejection: if there's not a LF among the first # 100 bytes, this is (probably) not a text header. - if not "\n" in self.fp.read(100): - raise SyntaxError, "not an IM file" + if not b"\n" in self.fp.read(100): + raise SyntaxError("not an IM file") self.fp.seek(0) xsize = ysize = 0 - while 1: + while True: s = self.fp.read(1) if not s: break - if s == chr(12): + if s == b'\x0C': # image data begins self.tile = [("raw", (0,0)+self.size, @@ -67,7 +67,7 @@ class ImtImageFile(ImageFile.ImageFile): s = s + self.fp.readline() if len(s) == 1 or len(s) > 100: break - if s[0] == "*": + if s[0] == b"*": continue # comment m = field.match(s) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py index acea5d18b..157b73509 100644 --- a/PIL/IptcImagePlugin.py +++ b/PIL/IptcImagePlugin.py @@ -15,37 +15,36 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function __version__ = "0.3" -import Image, ImageFile +from PIL import Image, ImageFile, _binary import os, tempfile +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be +o8 = _binary.o8 COMPRESSION = { 1: "raw", 5: "jpeg" } -PAD = chr(0) * 4 +PAD = o8(0) * 4 # # Helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - def i(c): return i32((PAD + c)[-4:]) def dump(c): for i in c: - print "%02x" % ord(i), - print + print("%02x" % i8(i), end=' ') + print() ## # Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields @@ -66,16 +65,16 @@ class IptcImageFile(ImageFile.ImageFile): if not len(s): return None, 0 - tag = ord(s[1]), ord(s[2]) + tag = i8(s[1]), i8(s[2]) # syntax - if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: - raise SyntaxError, "invalid IPTC/NAA file" + if i8(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError("invalid IPTC/NAA file") # field size - size = ord(s[3]) + size = i8(s[3]) if size > 132: - raise IOError, "illegal field length in IPTC/NAA file" + raise IOError("illegal field length in IPTC/NAA file") elif size == 128: size = 0 elif size > 128: @@ -97,7 +96,7 @@ class IptcImageFile(ImageFile.ImageFile): if sz != size[0]: return 0 y = 1 - while 1: + while True: self.fp.seek(sz, 1) t, s = self.field() if t != (8, 10): @@ -110,7 +109,7 @@ class IptcImageFile(ImageFile.ImageFile): def _open(self): # load descriptive fields - while 1: + while True: offset = self.fp.tell() tag, size = self.field() if not tag or tag == (8,10): @@ -119,7 +118,7 @@ class IptcImageFile(ImageFile.ImageFile): tagdata = self.fp.read(size) else: tagdata = None - if tag in self.info.keys(): + if tag in list(self.info.keys()): if isinstance(self.info[tag], list): self.info[tag].append(tagdata) else: @@ -130,10 +129,10 @@ class IptcImageFile(ImageFile.ImageFile): # print tag, self.info[tag] # mode - layers = ord(self.info[(3,60)][0]) - component = ord(self.info[(3,60)][1]) - if self.info.has_key((3,65)): - id = ord(self.info[(3,65)][0])-1 + layers = i8(self.info[(3,60)][0]) + component = i8(self.info[(3,60)][1]) + if (3,65) in self.info: + id = i8(self.info[(3,65)][0])-1 else: id = 0 if layers == 1 and not component: @@ -150,7 +149,7 @@ class IptcImageFile(ImageFile.ImageFile): try: compression = COMPRESSION[self.getint((3,120))] except KeyError: - raise IOError, "Unknown IPTC image compression" + raise IOError("Unknown IPTC image compression") # tile if tag == (8,10): @@ -179,7 +178,7 @@ class IptcImageFile(ImageFile.ImageFile): # To simplify access to the extracted file, # prepend a PPM header o.write("P5\n%d %d\n255\n" % self.size) - while 1: + while True: type, size = self.field() if type != (8, 10): break @@ -218,8 +217,8 @@ Image.register_extension("IPTC", ".iim") def getiptcinfo(im): - import TiffImagePlugin, JpegImagePlugin - import StringIO + from PIL import TiffImagePlugin, JpegImagePlugin + import io data = None @@ -241,7 +240,7 @@ def getiptcinfo(im): code = JpegImagePlugin.i16(app, offset) offset = offset + 2 # resource name (usually empty) - name_len = ord(app[offset]) + name_len = i8(app[offset]) name = app[offset+1:offset+1+name_len] offset = 1 + offset + name_len if offset & 1: @@ -263,7 +262,7 @@ def getiptcinfo(im): # get raw data from the IPTC/NAA tag (PhotoShop tags the data # as 4-byte integers, so we cannot use the get method...) try: - type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] except (AttributeError, KeyError): pass @@ -278,7 +277,7 @@ def getiptcinfo(im): # parse the IPTC information chunk im.info = {} - im.fp = StringIO.StringIO(data) + im.fp = io.BytesIO(data) try: im._open() diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py index 933abf3c2..90d3b5194 100644 --- a/PIL/JpegImagePlugin.py +++ b/PIL/JpegImagePlugin.py @@ -35,14 +35,14 @@ __version__ = "0.6" import array, struct -import string -import Image, ImageFile +from PIL import Image, ImageFile, _binary +from PIL.JpegPresets import presets +from PIL._util import isStringType -def i16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) - -def i32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) +i8 = _binary.i8 +o8 = _binary.o8 +i16 = _binary.i16be +i32 = _binary.i32be # # Parser @@ -64,13 +64,13 @@ def APP(self, marker): self.app[app] = s # compatibility self.applist.append((app, s)) - if marker == 0xFFE0 and s[:4] == "JFIF": + if marker == 0xFFE0 and s[:4] == b"JFIF": # extract JFIF information self.info["jfif"] = version = i16(s, 5) # version self.info["jfif_version"] = divmod(version, 256) # extract JFIF properties try: - jfif_unit = ord(s[7]) + jfif_unit = i8(s[7]) jfif_density = i16(s, 8), i16(s, 10) except: pass @@ -79,13 +79,13 @@ def APP(self, marker): self.info["dpi"] = jfif_density self.info["jfif_unit"] = jfif_unit self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s[:5] == "Exif\0": + elif marker == 0xFFE1 and s[:5] == b"Exif\0": # extract Exif information (incomplete) self.info["exif"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:5] == "FPXR\0": + elif marker == 0xFFE2 and s[:5] == b"FPXR\0": # extract FlashPix information (incomplete) self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:12] == "ICC_PROFILE\0": + elif marker == 0xFFE2 and s[:12] == b"ICC_PROFILE\0": # Since an ICC profile can be larger than the maximum size of # a JPEG marker (64K), we need provisions to split it into # multiple markers. The format defined by the ICC specifies @@ -98,11 +98,11 @@ def APP(self, marker): # reassemble the profile, rather than assuming that the APP2 # markers appear in the correct sequence. self.icclist.append(s) - elif marker == 0xFFEE and s[:5] == "Adobe": + elif marker == 0xFFEE and s[:5] == b"Adobe": self.info["adobe"] = i16(s, 5) # extract Adobe custom properties try: - adobe_transform = ord(s[1]) + adobe_transform = i8(s[1]) except: pass else: @@ -130,11 +130,11 @@ def SOF(self, marker): s = ImageFile._safe_read(self.fp, n) self.size = i16(s[3:]), i16(s[1:]) - self.bits = ord(s[0]) + self.bits = i8(s[0]) if self.bits != 8: raise SyntaxError("cannot handle %d-bit layers" % self.bits) - self.layers = ord(s[5]) + self.layers = i8(s[5]) if self.layers == 1: self.mode = "L" elif self.layers == 3: @@ -150,11 +150,11 @@ def SOF(self, marker): if self.icclist: # fixup icc profile self.icclist.sort() # sort by sequence number - if ord(self.icclist[0][13]) == len(self.icclist): + if i8(self.icclist[0][13]) == len(self.icclist): profile = [] for p in self.icclist: profile.append(p[14:]) - icc_profile = string.join(profile, "") + icc_profile = b"".join(profile) else: icc_profile = None # wrong number of fragments self.info["icc_profile"] = icc_profile @@ -163,7 +163,7 @@ def SOF(self, marker): for i in range(6, len(s), 3): t = s[i:i+3] # 4-tuples: id, vsamp, hsamp, qtable - self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2]))) + self.layer.append((t[0], i8(t[1])//16, i8(t[1])&15, i8(t[2]))) def DQT(self, marker): # @@ -179,8 +179,8 @@ def DQT(self, marker): while len(s): if len(s) < 65: raise SyntaxError("bad quantization table marker") - v = ord(s[0]) - if v/16 == 0: + v = i8(s[0]) + if v//16 == 0: self.quantization[v&15] = array.array("b", s[1:65]) s = s[65:] else: @@ -259,7 +259,7 @@ MARKER = { def _accept(prefix): - return prefix[0] == "\377" + return prefix[0:1] == b"\377" ## # Image plugin for JPEG and JFIF images. @@ -273,7 +273,7 @@ class JpegImageFile(ImageFile.ImageFile): s = self.fp.read(1) - if ord(s[0]) != 255: + if i8(s[0]) != 255: raise SyntaxError("not a JPEG file") # Create attributes @@ -288,13 +288,13 @@ class JpegImageFile(ImageFile.ImageFile): self.applist = [] self.icclist = [] - while 1: + while True: s = s + self.fp.read(1) i = i16(s) - if MARKER.has_key(i): + if i in MARKER: name, description, handler = MARKER[i] # print hex(i), name, description if handler is not None: @@ -326,12 +326,12 @@ class JpegImageFile(ImageFile.ImageFile): a = mode, "" if size: - scale = max(self.size[0] / size[0], self.size[1] / size[1]) + scale = max(self.size[0] // size[0], self.size[1] // size[1]) for s in [8, 4, 2, 1]: if scale >= s: break - e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] - self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + e = e[0], e[1], (e[2]-e[0]+s-1)//s+e[0], (e[3]-e[1]+s-1)//s+e[1] + self.size = ((self.size[0]+s-1)//s, (self.size[1]+s-1)//s) scale = s self.tile = [(d, e, o, a)] @@ -359,50 +359,55 @@ class JpegImageFile(ImageFile.ImageFile): self.tile = [] def _getexif(self): - # Extract EXIF information. This method is highly experimental, - # and is likely to be replaced with something better in a future - # version. - import TiffImagePlugin, StringIO - def fixup(value): - if len(value) == 1: - return value[0] - return value - # The EXIF record consists of a TIFF file embedded in a JPEG - # application marker (!). - try: - data = self.info["exif"] - except KeyError: - return None - file = StringIO.StringIO(data[6:]) - head = file.read(8) - exif = {} - # process dictionary + return _getexif(self) + + +def _getexif(self): + # Extract EXIF information. This method is highly experimental, + # and is likely to be replaced with something better in a future + # version. + from PIL import TiffImagePlugin + import io + def fixup(value): + if len(value) == 1: + return value[0] + return value + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + try: + data = self.info["exif"] + except KeyError: + return None + file = io.BytesIO(data[6:]) + head = file.read(8) + exif = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = fixup(value) + # get exif extension + try: + file.seek(exif[0x8769]) + except KeyError: + pass + else: info = TiffImagePlugin.ImageFileDirectory(head) info.load(file) for key, value in info.items(): exif[key] = fixup(value) - # get exif extension - try: - file.seek(exif[0x8769]) - except KeyError: - pass - else: - info = TiffImagePlugin.ImageFileDirectory(head) - info.load(file) - for key, value in info.items(): - exif[key] = fixup(value) - # get gpsinfo extension - try: - file.seek(exif[0x8825]) - except KeyError: - pass - else: - info = TiffImagePlugin.ImageFileDirectory(head) - info.load(file) - exif[0x8825] = gps = {} - for key, value in info.items(): - gps[key] = fixup(value) - return exif + # get gpsinfo extension + try: + file.seek(exif[0x8825]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + exif[0x8825] = gps = {} + for key, value in info.items(): + gps[key] = fixup(value) + return exif # -------------------------------------------------------------------- # stuff to save JPEG files @@ -417,6 +422,31 @@ RAWMODE = { "YCbCr": "YCbCr", } +zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63) + +samplings = { + (1, 1, 1, 1, 1, 1): 0, + (2, 1, 1, 1, 1, 1): 1, + (2, 2, 1, 1, 1, 1): 2, + } + +def convert_dict_qtables(qtables): + qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)] + for idx, table in enumerate(qtables): + qtables[idx] = [table[i] for i in zigzag_index] + return qtables + +def get_sampling(im): + sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3] + return samplings.get(sampling, -1) + def _save(im, fp, filename): try: @@ -428,15 +458,74 @@ def _save(im, fp, filename): dpi = info.get("dpi", (0, 0)) + quality = info.get("quality", 0) subsampling = info.get("subsampling", -1) + qtables = info.get("qtables") + + if quality == "keep": + quality = 0 + subsampling = "keep" + qtables = "keep" + elif quality in presets: + preset = presets[quality] + quality = 0 + subsampling = preset.get('subsampling', -1) + qtables = preset.get('quantization') + elif not isinstance(quality, int): + raise ValueError("Invalid quality setting") + else: + if subsampling in presets: + subsampling = presets[subsampling].get('subsampling', -1) + if qtables in presets: + qtables = presets[qtables].get('quantization') + if subsampling == "4:4:4": subsampling = 0 elif subsampling == "4:2:2": subsampling = 1 elif subsampling == "4:1:1": subsampling = 2 + elif subsampling == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + subsampling = get_sampling(im) - extra = "" + def validate_qtables(qtables): + if qtables is None: + return qtables + if isStringType(qtables): + try: + lines = [int(num) for line in qtables.splitlines() + for num in line.split('#', 1)[0].split()] + except ValueError: + raise ValueError("Invalid quantization table") + else: + qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)] + if isinstance(qtables, (tuple, list, dict)): + if isinstance(qtables, dict): + qtables = convert_dict_qtables(qtables) + elif isinstance(qtables, tuple): + qtables = list(qtables) + if not (0 < len(qtables) < 5): + raise ValueError("None or too many quantization tables") + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + raise + table = array.array('b', table) + except TypeError: + raise ValueError("Invalid quantization table") + else: + qtables[idx] = list(table) + return qtables + + if qtables == "keep": + if im.format != "JPEG": + raise ValueError("Cannot use 'keep' when original image is not a JPEG") + qtables = getattr(im, "quantization", None) + qtables = validate_qtables(qtables) + + extra = b"" icc_profile = info.get("icc_profile") if icc_profile: @@ -450,25 +539,40 @@ def _save(im, fp, filename): i = 1 for marker in markers: size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) - extra = extra + ("\xFF\xE2" + size + "ICC_PROFILE\0" + chr(i) + chr(len(markers)) + marker) + extra = extra + (b"\xFF\xE2" + size + b"ICC_PROFILE\0" + o8(i) + o8(len(markers)) + marker) i = i + 1 # get keyword arguments im.encoderconfig = ( - info.get("quality", 0), + quality, # "progressive" is the official name, but older documentation # says "progression" # FIXME: issue a warning if the wrong form is used (post-1.1.7) - info.has_key("progressive") or info.has_key("progression"), + "progressive" in info or "progression" in info, info.get("smooth", 0), - info.has_key("optimize"), + "optimize" in info, info.get("streamtype", 0), dpi[0], dpi[1], subsampling, + qtables, extra, + info.get("exif", b"") ) - ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)]) + + # if we optimize, libjpeg needs a buffer big enough to hold the whole image in a shot. + # Guessing on the size, at im.size bytes. (raw pizel size is channels*size, this + # is a value that's been used in a django patch. + # https://github.com/jdriscoll/django-imagekit/issues/50 + bufsize=0 + if "optimize" in info or "progressive" in info or "progression" in info: + bufsize = im.size[0]*im.size[1] + + # The exif info needs to be written as one block, + APP1, + one spare byte. + # Ensure that our buffer is big enough + bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif",b"")) + 5 ) + + ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)], bufsize) def _save_cjpeg(im, fp, filename): # ALTERNATIVE: handle JPEGs via the IJG command line utilities. diff --git a/PIL/JpegPresets.py b/PIL/JpegPresets.py new file mode 100644 index 000000000..e7bec148a --- /dev/null +++ b/PIL/JpegPresets.py @@ -0,0 +1,241 @@ +""" +JPEG quality settings equivalent to the Photoshop settings. + +More presets can be added to the presets dict if needed. + +Can be use when saving JPEG file. + +To apply the preset, specify:: + + quality="preset_name" + +To apply only the quantization table:: + + qtables="preset_name" + +To apply only the subsampling setting:: + + subsampling="preset_name" + +Example:: + + im.save("image_name.jpg", quality="web_high") + + +Subsampling +----------- + +Subsampling is the practice of encoding images by implementing less resolution +for chroma information than for luma information. +(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling) + +Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and +4:1:1 (or 4:2:0?). + +You can get the subsampling of a JPEG with the +`JpegImagePlugin.get_subsampling(im)` function. + + +Quantization tables +------------------- + +They are values use by the DCT (Discrete cosine transform) to remove +*unnecessary* information from the image (the lossy part of the compression). +(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices, +http://en.wikipedia.org/wiki/JPEG#Quantization) + +You can get the quantization tables of a JPEG with:: + + im.quantization + +This will return a dict with a number of arrays. You can pass this dict directly +as the qtables argument when saving a JPEG. + +The tables format between im.quantization and quantization in presets differ in +3 ways: + +1. The base container of the preset is a list with sublists instead of dict. + dict[0] -> list[0], dict[1] -> list[1], ... +2. Each table in a preset is a list instead of an array. +3. The zigzag order is remove in the preset (needed by libjpeg >= 6a). + +You can convert the dict format to the preset format with the +`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function. + +Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html + +""" + +presets = { + 'web_low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [20, 16, 25, 39, 50, 46, 62, 68, + 16, 18, 23, 38, 38, 53, 65, 68, + 25, 23, 31, 38, 53, 65, 68, 68, + 39, 38, 38, 53, 65, 68, 68, 68, + 50, 38, 53, 65, 68, 68, 68, 68, + 46, 53, 65, 68, 68, 68, 68, 68, + 62, 65, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68], + [21, 25, 32, 38, 54, 68, 68, 68, + 25, 28, 24, 38, 54, 68, 68, 68, + 32, 24, 32, 43, 66, 68, 68, 68, + 38, 38, 43, 53, 68, 68, 68, 68, + 54, 54, 66, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68] + ]}, + 'web_medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [16, 11, 11, 16, 23, 27, 31, 30, + 11, 12, 12, 15, 20, 23, 23, 30, + 11, 12, 13, 16, 23, 26, 35, 47, + 16, 15, 16, 23, 26, 37, 47, 64, + 23, 20, 23, 26, 39, 51, 64, 64, + 27, 23, 26, 37, 51, 64, 64, 64, + 31, 23, 35, 47, 64, 64, 64, 64, + 30, 30, 47, 64, 64, 64, 64, 64], + [17, 15, 17, 21, 20, 26, 38, 48, + 15, 19, 18, 17, 20, 26, 35, 43, + 17, 18, 20, 22, 26, 30, 46, 53, + 21, 17, 22, 28, 30, 39, 53, 64, + 20, 20, 26, 30, 39, 48, 64, 64, + 26, 26, 30, 39, 48, 63, 64, 64, + 38, 35, 46, 53, 64, 64, 64, 64, + 48, 43, 53, 64, 64, 64, 64, 64] + ]}, + 'web_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 14, 19, + 6, 6, 6, 11, 12, 15, 19, 28, + 9, 8, 10, 12, 16, 20, 27, 31, + 11, 10, 12, 15, 20, 27, 31, 31, + 12, 12, 14, 19, 27, 31, 31, 31, + 16, 12, 19, 28, 31, 31, 31, 31], + [ 7, 7, 13, 24, 26, 31, 31, 31, + 7, 12, 16, 21, 31, 31, 31, 31, + 13, 16, 17, 31, 31, 31, 31, 31, + 24, 21, 31, 31, 31, 31, 31, 31, + 26, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31] + ]}, + 'web_very_high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 11, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 11, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'web_maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 1, 1, 2, 2, 3, + 1, 1, 1, 1, 2, 2, 3, 3, + 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 2, 2, 3, 3, 3, 3], + [ 1, 1, 1, 2, 2, 3, 3, 3, + 1, 1, 1, 2, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, + 2, 2, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3] + ]}, + 'low': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [18, 14, 14, 21, 30, 35, 34, 17, + 14, 16, 16, 19, 26, 23, 12, 12, + 14, 16, 17, 21, 23, 12, 12, 12, + 21, 19, 21, 23, 12, 12, 12, 12, + 30, 26, 23, 12, 12, 12, 12, 12, + 35, 23, 12, 12, 12, 12, 12, 12, + 34, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [20, 19, 22, 27, 20, 20, 17, 17, + 19, 25, 23, 14, 14, 12, 12, 12, + 22, 23, 14, 14, 12, 12, 12, 12, + 27, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'medium': {'subsampling': 2, # "4:1:1" + 'quantization': [ + [12, 8, 8, 12, 17, 21, 24, 17, + 8, 9, 9, 11, 15, 19, 12, 12, + 8, 9, 10, 12, 19, 12, 12, 12, + 12, 11, 12, 21, 12, 12, 12, 12, + 17, 15, 19, 12, 12, 12, 12, 12, + 21, 19, 12, 12, 12, 12, 12, 12, + 24, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12], + [13, 11, 13, 16, 20, 20, 17, 17, + 11, 14, 14, 14, 14, 12, 12, 12, + 13, 14, 14, 14, 12, 12, 12, 12, + 16, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'high': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 6, 4, 4, 6, 9, 11, 12, 16, + 4, 5, 5, 6, 8, 10, 12, 12, + 4, 5, 5, 6, 10, 12, 12, 12, + 6, 6, 6, 11, 12, 12, 12, 12, + 9, 8, 10, 12, 12, 12, 12, 12, + 11, 10, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, + 16, 12, 12, 12, 12, 12, 12, 12], + [ 7, 7, 13, 24, 20, 20, 17, 17, + 7, 12, 16, 14, 14, 12, 12, 12, + 13, 16, 14, 14, 12, 12, 12, 12, + 24, 14, 14, 12, 12, 12, 12, 12, + 20, 14, 12, 12, 12, 12, 12, 12, + 20, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12, + 17, 12, 12, 12, 12, 12, 12, 12] + ]}, + 'maximum': {'subsampling': 0, # "4:4:4" + 'quantization': [ + [ 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 3, 4, 5, 6, + 2, 2, 2, 2, 4, 5, 7, 9, + 2, 2, 2, 4, 5, 7, 9, 12, + 3, 3, 4, 5, 8, 10, 12, 12, + 4, 4, 5, 7, 10, 12, 12, 12, + 5, 5, 7, 9, 12, 12, 12, 12, + 6, 6, 9, 12, 12, 12, 12, 12], + [ 3, 3, 5, 9, 13, 15, 15, 15, + 3, 4, 6, 10, 14, 12, 12, 12, + 5, 6, 9, 14, 12, 12, 12, 12, + 9, 10, 14, 12, 12, 12, 12, 12, + 13, 14, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12, + 15, 12, 12, 12, 12, 12, 12, 12] + ]}, +} \ No newline at end of file diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py index 62ee52cf8..3aef10ba8 100644 --- a/PIL/McIdasImagePlugin.py +++ b/PIL/McIdasImagePlugin.py @@ -19,10 +19,10 @@ __version__ = "0.2" import struct -import Image, ImageFile +from PIL import Image, ImageFile def _accept(s): - return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04" + return s[:8] == b"\x00\x00\x00\x00\x00\x00\x00\x04" ## # Image plugin for McIdas area images. diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index b1a3bba79..84e962860 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -20,8 +20,8 @@ __version__ = "0.1" -import Image, TiffImagePlugin -from OleFileIO import * +from PIL import Image, TiffImagePlugin +from PIL.OleFileIO import * # @@ -47,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: self.ole = OleFileIO(self.fp) except IOError: - raise SyntaxError, "not an MIC file; invalid OLE file" + raise SyntaxError("not an MIC file; invalid OLE file") # find ACI subfiles with Image members (maybe not the # best way to identify MIC files, but what the... ;-) @@ -60,7 +60,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): # if we didn't find any images, this is probably not # an MIC file. if not self.images: - raise SyntaxError, "not an MIC file; no image entries" + raise SyntaxError("not an MIC file; no image entries") self.__fp = self.fp self.frame = 0 @@ -75,7 +75,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): try: filename = self.images[frame] except IndexError: - raise EOFError, "no such frame" + raise EOFError("no such frame") self.fp = self.ole.openstream(filename) diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py index c02edee82..9d7a0ea7a 100644 --- a/PIL/MpegImagePlugin.py +++ b/PIL/MpegImagePlugin.py @@ -15,7 +15,8 @@ __version__ = "0.1" -import Image, ImageFile +from PIL import Image, ImageFile +from PIL._binary import i8 # # Bitstream parser @@ -28,7 +29,7 @@ class BitStream: self.bitbuffer = 0 def next(self): - return ord(self.fp.read(1)) + return i8(self.fp.read(1)) def peek(self, bits): while self.bits < bits: @@ -38,11 +39,11 @@ class BitStream: continue self.bitbuffer = (self.bitbuffer << 8) + c self.bits = self.bits + 8 - return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1 + return self.bitbuffer >> (self.bits - bits) & (1 << bits) - 1 def skip(self, bits): while self.bits < bits: - self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1)) + self.bitbuffer = (self.bitbuffer << 8) + i8(self.fp.read(1)) self.bits = self.bits + 8 self.bits = self.bits - bits @@ -65,7 +66,7 @@ class MpegImageFile(ImageFile.ImageFile): s = BitStream(self.fp) if s.read(32) != 0x1B3: - raise SyntaxError, "not an MPEG file" + raise SyntaxError("not an MPEG file") self.mode = "RGB" self.size = s.read(12), s.read(12) diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py index 9dac36b47..743ebe172 100644 --- a/PIL/MspImagePlugin.py +++ b/PIL/MspImagePlugin.py @@ -19,17 +19,16 @@ __version__ = "0.1" -import Image, ImageFile +from PIL import Image, ImageFile, _binary # # read MSP files -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) +i16 = _binary.i16le def _accept(prefix): - return prefix[:4] in ["DanM", "LinS"] + return prefix[:4] in [b"DanM", b"LinS"] ## # Image plugin for Windows MSP images. This plugin supports both @@ -44,20 +43,20 @@ class MspImageFile(ImageFile.ImageFile): # Header s = self.fp.read(32) - if s[:4] not in ["DanM", "LinS"]: - raise SyntaxError, "not an MSP file" + if s[:4] not in [b"DanM", b"LinS"]: + raise SyntaxError("not an MSP file") # Header checksum sum = 0 for i in range(0, 32, 2): sum = sum ^ i16(s[i:i+2]) if sum != 0: - raise SyntaxError, "bad MSP checksum" + raise SyntaxError("bad MSP checksum") self.mode = "1" self.size = i16(s[4:]), i16(s[6:]) - if s[:4] == "DanM": + if s[:4] == b"DanM": self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))] else: self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)] @@ -65,18 +64,17 @@ class MspImageFile(ImageFile.ImageFile): # # write MSP files (uncompressed only) -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as MSP" % im.mode + raise IOError("cannot write mode %s as MSP" % im.mode) # create MSP header header = [0] * 16 - header[0], header[1] = i16("Da"), i16("nM") # version 1 + header[0], header[1] = i16(b"Da"), i16(b"nM") # version 1 header[2], header[3] = im.size header[4], header[5] = 1, 1 header[6], header[7] = 1, 1 diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py index 631a8ed84..5007fd1d7 100644 --- a/PIL/OleFileIO.py +++ b/PIL/OleFileIO.py @@ -23,6 +23,8 @@ See source code and LICENSE.txt for information on usage and redistribution. WARNING: THIS IS (STILL) WORK IN PROGRESS. """ +from __future__ import print_function + __author__ = "Philippe Lagadec, Fredrik Lundh (Secret Labs AB)" __date__ = "2013-07-24" __version__ = '0.26' @@ -218,11 +220,18 @@ __version__ = '0.26' #------------------------------------------------------------------------------ -import string, StringIO, struct, array, os.path, sys, datetime +import io +import sys +from PIL import _binary +from PIL._util import isPath +import struct, array, os.path, sys, datetime #[PL] Define explicitly the public API to avoid private objects in pydoc: __all__ = ['OleFileIO', 'isOleFile'] +if str is not bytes: + long = int + #[PL] workaround to fix an issue with array item size on 64 bits systems: if array.array('L').itemsize == 4: # on 32 bits platforms, long integers in an array are 32 bits: @@ -280,7 +289,7 @@ def set_debug_mode(debug_mode): debug = debug_pass #TODO: convert this to hex -MAGIC = '\320\317\021\340\241\261\032\341' +MAGIC = b'\320\317\021\340\241\261\032\341' #[PL]: added constants for Sector IDs (from AAF specifications) MAXREGSECT = 0xFFFFFFFAL; # maximum SECT @@ -319,7 +328,7 @@ VT_VECTOR=0x1000; # map property id to name (for debugging purposes) VT = {} -for keyword, var in vars().items(): +for keyword, var in list(vars().items()): if keyword[:3] == "VT_": VT[var] = keyword @@ -360,26 +369,9 @@ def isOleFile (filename): return False -#TODO: replace i16 and i32 with more readable struct.unpack equivalent -def i16(c, o = 0): - """ - Converts a 2-bytes (16 bits) string to an integer. - - c: string containing bytes to convert - o: offset of bytes to convert in string - """ - return ord(c[o])+(ord(c[o+1])<<8) - - -def i32(c, o = 0): - """ - Converts a 4-bytes (32 bits) string to an integer. - - c: string containing bytes to convert - o: offset of bytes to convert in string - """ - return int(ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24)) - # [PL]: added int() because "<<" gives long int since Python 2.4 +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le def _clsid(clsid): @@ -392,7 +384,7 @@ def _clsid(clsid): return "" return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + - tuple(map(ord, clsid[8:16])))) + tuple(map(i8, clsid[8:16])))) @@ -416,7 +408,7 @@ try: # First the string is converted to plain Unicode: # (assuming it is encoded as UTF-16 little-endian) u = s.decode('UTF-16LE', errors) - if KEEP_UNICODE_NAMES: + if bytes is not str or KEEP_UNICODE_NAMES: return u else: # Second the unicode string is converted to Latin-1 @@ -608,7 +600,7 @@ class OleMetadata: #--- _OleStream --------------------------------------------------------------- -class _OleStream(StringIO.StringIO): +class _OleStream(io.BytesIO): """ OLE2 Stream @@ -718,7 +710,7 @@ class _OleStream(StringIO.StringIO): #[PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: raise IOError, 'incorrect last sector index in OLE stream' - data = string.join(data, "") + data = b"".join(data) # Data is truncated to the actual stream size: if len(data) >= size: data = data[:size] @@ -733,7 +725,7 @@ class _OleStream(StringIO.StringIO): debug('len(data)=%d, size=%d' % (len(data), size)) raise IOError, 'OLE stream size is less than declared' # when all data is read in memory, StringIO constructor is called - StringIO.StringIO.__init__(self, data) + io.BytesIO.__init__(self, data) # Then the _OleStream object can be used as a read-only file object. @@ -943,12 +935,12 @@ class _OleDirectoryEntry: "Dump this entry, and all its subentries (for debug purposes only)" TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", "(property)", "(root)"] - print " "*tab + repr(self.name), TYPES[self.entry_type], + print(" "*tab + repr(self.name), TYPES[self.entry_type], end=' ') if self.entry_type in (STGTY_STREAM, STGTY_ROOT): - print self.size, "bytes", - print + print(self.size, "bytes", end=' ') + print() if self.entry_type in (STGTY_STORAGE, STGTY_ROOT) and self.clsid: - print " "*tab + "{%s}" % self.clsid + print(" "*tab + "{%s}" % self.clsid) for kid in self.kids: kid.dump(tab + 2) @@ -994,7 +986,7 @@ class OleFileIO: Object names are given as a list of strings, one for each subentry level. The root entry should be omitted. For example, the following - code extracts all image streams from a Microsoft Image Composer file: + code extracts all image streams from a Microsoft Image Composer file:: ole = OleFileIO("fan.mic") @@ -1047,7 +1039,7 @@ class OleFileIO: """ # added by [PL] if defect_level >= self._raise_defects_level: - raise exception_type, message + raise exception_type(message) else: # just record the issue, no exception raised: self.parsing_issues.append((exception_type, message)) @@ -1070,7 +1062,7 @@ class OleFileIO: #TODO: if larger than 1024 bytes, this could be the actual data => StringIO self.fp = open(filename, "rb") # old code fails if filename is not a plain string: - #if type(filename) == type(""): + #if isPath(filename): # self.fp = open(filename, "rb") #else: # self.fp = filename @@ -1389,7 +1381,7 @@ class OleFileIO: ## if ix == 0xFFFFFFFEL or ix == 0xFFFFFFFFL: ## break ## s = self.getsect(ix) -## #fat = fat + map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) +## #fat = fat + [i32(s, i) for i in range(0, len(s), 4)] ## fat = fat + array.array(UINT32, s) if self.csectDif != 0: # [PL] There's a DIFAT because file is larger than 6.8MB @@ -1462,7 +1454,7 @@ class OleFileIO: # In any case, first read stream_size: s = self._open(self.minifatsect, stream_size, force_FAT=True).read() #[PL] Old code replaced by an array: - #self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + #self.minifat = [i32(s, i) for i in range(0, len(s), 4)] self.minifat = self.sect2array(s) # Then shrink the array to used size, to avoid indexes out of MiniStream: debug('MiniFAT shrunk from %d to %d sectors' % (len(self.minifat), nb_minisectors)) @@ -1660,7 +1652,7 @@ class OleFileIO: if kid.name.lower() == name.lower(): break else: - raise IOError, "file not found" + raise IOError("file not found") node = kid return node.sid @@ -1680,7 +1672,7 @@ class OleFileIO: sid = self._find(filename) entry = self.direntries[sid] if entry.entry_type != STGTY_STREAM: - raise IOError, "this file is not a stream" + raise IOError("this file is not a stream") return self._open(entry.isectStart, entry.size) @@ -1881,9 +1873,9 @@ class OleFileIO: else: # legacy code kept for backward compatibility: returns a # number of seconds since Jan 1,1601 - value = value / 10000000L # seconds + value = value // 10000000 # seconds elif type == VT_UI1: # 1-byte unsigned integer - value = ord(s[offset+4]) + value = i8(s[offset+4]) elif type == VT_CLSID: value = _clsid(s[offset+4:offset+20]) elif type == VT_CF: @@ -1972,16 +1964,15 @@ Options: continue ole = OleFileIO(filename)#, raise_defects=DEFECT_INCORRECT) - print "-" * 68 - print filename - print "-" * 68 + print("-" * 68) + print(filename) + print("-" * 68) ole.dumpdirectory() for streamname in ole.listdir(): if streamname[-1][0] == "\005": - print streamname, ": properties" + print(streamname, ": properties") props = ole.getproperties(streamname, convert_time=True) - props = props.items() - props.sort() + props = sorted(props.items()) for k, v in props: #[PL]: avoid to display too large or binary values: if isinstance(v, basestring): @@ -1993,7 +1984,7 @@ Options: if chr(c) in v: v = '(binary data)' break - print " ", k, v + print(" ", k, v) if check_streams: # Read all streams to check if there are errors: @@ -2044,5 +2035,5 @@ Options: print '- %s: %s' % (exctype.__name__, msg) else: print 'None' -## except IOError, v: -## print "***", "cannot read", file, "-", v +## except IOError as v: +## print("***", "cannot read", file, "-", v) diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py index 7309e17b3..88593bb9d 100644 --- a/PIL/PSDraw.py +++ b/PIL/PSDraw.py @@ -15,13 +15,18 @@ # See the README file for information on usage and redistribution. # -import EpsImagePlugin -import string +from __future__ import print_function + +from PIL import EpsImagePlugin ## # Simple Postscript graphics interface. class PSDraw: + """ + Sets up printing to the given file. If **file** is omitted, + :py:attr:`sys.stdout` is assumed. + """ def __init__(self, fp=None): if not fp: @@ -30,7 +35,7 @@ class PSDraw: self.fp = fp def begin_document(self, id = None): - "Write Postscript DSC header" + """Set up printing of a document. (Write Postscript DSC header.)""" # FIXME: incomplete self.fp.write("%!PS-Adobe-3.0\n" "save\n" @@ -44,7 +49,7 @@ class PSDraw: self.isofont = {} def end_document(self): - "Write Postscript DSC footer" + """Ends printing. (Write Postscript DSC footer.)""" self.fp.write("%%EndDocument\n" "restore showpage\n" "%%End\n") @@ -52,7 +57,13 @@ class PSDraw: self.fp.flush() def setfont(self, font, size): - if not self.isofont.has_key(font): + """ + Selects which font to use. + + :param font: A Postscript font name + :param size: Size in points. + """ + if font not in self.isofont: # reencode font self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ (font, font)) @@ -61,23 +72,49 @@ class PSDraw: self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) def setink(self, ink): - print "*** NOT YET IMPLEMENTED ***" + """ + .. warning:: + + This has been in the PIL API for ages but was never implemented. + """ + print("*** NOT YET IMPLEMENTED ***") def line(self, xy0, xy1): + """ + Draws a line between the two points. Coordinates are given in + Postscript point coordinates (72 points per inch, (0, 0) is the lower + left corner of the page). + """ xy = xy0 + xy1 self.fp.write("%d %d %d %d Vl\n" % xy) def rectangle(self, box): + """ + Draws a rectangle. + + :param box: A 4-tuple of integers whose order and function is currently + undocumented. + + Hint: the tuple is passed into this format string: + + .. code-block:: python + + %d %d M %d %d 0 Vr\n + """ self.fp.write("%d %d M %d %d 0 Vr\n" % box) def text(self, xy, text): - text = string.joinfields(string.splitfields(text, "("), "\\(") - text = string.joinfields(string.splitfields(text, ")"), "\\)") + """ + Draws text at the given position. You must use + :py:meth:`~PIL.PSDraw.PSDraw.setfont` before calling this method. + """ + text = "\\(".join(text.split("(")) + text = "\\)".join(text.split(")")) xy = xy + (text,) self.fp.write("%d %d M (%s) S\n" % xy) def image(self, box, im, dpi = None): - "Write an PIL image" + """Draw a PIL image, centered in the given box.""" # default resolution depends on mode if not dpi: if im.mode == "1": diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py index 3bbd91327..5627f7b86 100644 --- a/PIL/PaletteFile.py +++ b/PIL/PaletteFile.py @@ -13,7 +13,7 @@ # See the README file for information on usage and redistribution. # -import string +from PIL._binary import o8 ## # File handler for Teragon-style palette files. @@ -24,20 +24,20 @@ class PaletteFile: def __init__(self, fp): - self.palette = map(lambda i: (i, i, i), range(256)) + self.palette = [(i, i, i) for i in range(256)] - while 1: + while True: s = fp.readline() if not s: break - if s[0] == "#": + if s[0:1] == b"#": continue if len(s) > 100: - raise SyntaxError, "bad palette file" + raise SyntaxError("bad palette file") - v = map(int, string.split(s)) + v = [int(x) for x in s.split()] try: [i, r, g, b] = v except ValueError: @@ -45,9 +45,9 @@ class PaletteFile: g = b = r if 0 <= i <= 255: - self.palette[i] = chr(r) + chr(g) + chr(b) + self.palette[i] = o8(r) + o8(g) + o8(b) - self.palette = string.join(self.palette, "") + self.palette = b"".join(self.palette) def getpalette(self): diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py index 785023130..89f42bffc 100644 --- a/PIL/PalmImagePlugin.py +++ b/PIL/PalmImagePlugin.py @@ -9,7 +9,7 @@ __version__ = "1.0" -import Image, ImageFile +from PIL import Image, ImageFile, _binary _Palm8BitColormapValues = ( ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ), @@ -80,7 +80,7 @@ _Palm8BitColormapValues = ( # so build a prototype image to be used for palette resampling def build_prototype_image(): image = Image.new("L", (1,len(_Palm8BitColormapValues),)) - image.putdata(range(len(_Palm8BitColormapValues))) + image.putdata(list(range(len(_Palm8BitColormapValues)))) palettedata = () for i in range(len(_Palm8BitColormapValues)): palettedata = palettedata + _Palm8BitColormapValues[i] @@ -107,8 +107,8 @@ _COMPRESSION_TYPES = { "scanline": 0x00, } -def o16b(i): - return chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16b = _binary.o16be # # -------------------------------------------------------------------- @@ -127,7 +127,7 @@ def _save(im, fp, filename, check=0): bpp = 8 version = 1 - elif im.mode == "L" and im.encoderinfo.has_key("bpp") and im.encoderinfo["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.encoderinfo and im.encoderinfo["bpp"] in (1, 2, 4): # this is 8-bit grayscale, so we shift it to get the high-order bits, and invert it because # Palm does greyscale from white (0) to black (1) @@ -138,7 +138,7 @@ def _save(im, fp, filename, check=0): rawmode = "P;" + str(bpp) version = 1 - elif im.mode == "L" and im.info.has_key("bpp") and im.info["bpp"] in (1, 2, 4): + elif im.mode == "L" and "bpp" in im.info and im.info["bpp"] in (1, 2, 4): # here we assume that even though the inherent mode is 8-bit grayscale, only # the lower bpp bits are significant. We invert them to match the Palm. @@ -158,7 +158,7 @@ def _save(im, fp, filename, check=0): else: - raise IOError, "cannot write mode %s as Palm" % im.mode + raise IOError("cannot write mode %s as Palm" % im.mode) if check: return check @@ -172,12 +172,12 @@ def _save(im, fp, filename, check=0): cols = im.size[0] rows = im.size[1] - rowbytes = ((cols + (16/bpp - 1)) / (16 / bpp)) * 2; + rowbytes = ((cols + (16//bpp - 1)) / (16 // bpp)) * 2; transparent_index = 0 compression_type = _COMPRESSION_TYPES["none"] flags = 0; - if im.mode == "P" and im.info.has_key("custom-colormap"): + if im.mode == "P" and "custom-colormap" in im.info: flags = flags & _FLAGS["custom-colormap"] colormapsize = 4 * 256 + 2; colormapmode = im.palette.mode @@ -185,17 +185,17 @@ def _save(im, fp, filename, check=0): else: colormapsize = 0 - if im.info.has_key("offset"): - offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4; + if "offset" in im.info: + offset = (rowbytes * rows + 16 + 3 + colormapsize) // 4; else: offset = 0 fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) - fp.write(chr(bpp)) - fp.write(chr(version)) + fp.write(o8(bpp)) + fp.write(o8(version)) fp.write(o16b(offset)) - fp.write(chr(transparent_index)) - fp.write(chr(compression_type)) + fp.write(o8(transparent_index)) + fp.write(o8(compression_type)) fp.write(o16b(0)) # reserved by Palm # now write colormap if necessary @@ -203,11 +203,11 @@ def _save(im, fp, filename, check=0): if colormapsize > 0: fp.write(o16b(256)) for i in range(256): - fp.write(chr(i)) + fp.write(o8(i)) if colormapmode == 'RGB': - fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2])) + fp.write(o8(colormap[3 * i]) + o8(colormap[3 * i + 1]) + o8(colormap[3 * i + 2])) elif colormapmode == 'RGBA': - fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2])) + fp.write(o8(colormap[4 * i]) + o8(colormap[4 * i + 1]) + o8(colormap[4 * i + 2])) # now convert data to raw form ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))]) diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py index 07bd27e9a..70066e76b 100644 --- a/PIL/PcdImagePlugin.py +++ b/PIL/PcdImagePlugin.py @@ -18,7 +18,9 @@ __version__ = "0.1" -import Image, ImageFile +from PIL import Image, ImageFile, _binary + +i8 = _binary.i8 ## # Image plugin for PhotoCD images. This plugin only reads the 768x512 @@ -36,10 +38,10 @@ class PcdImageFile(ImageFile.ImageFile): self.fp.seek(2048) s = self.fp.read(2048) - if s[:4] != "PCD_": - raise SyntaxError, "not a PCD file" + if s[:4] != b"PCD_": + raise SyntaxError("not a PCD file") - orientation = ord(s[1538]) & 3 + orientation = i8(s[1538]) & 3 if orientation == 1: self.tile_post_rotate = 90 # hack elif orientation == 3: diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py index bec39e3a0..c40d3986d 100644 --- a/PIL/PcfFontFile.py +++ b/PIL/PcfFontFile.py @@ -16,10 +16,9 @@ # See the README file for information on usage and redistribution. # -import Image -import FontFile - -import string +from PIL import Image +from PIL import FontFile +from PIL import _binary # -------------------------------------------------------------------- # declarations @@ -43,19 +42,14 @@ BYTES_PER_ROW = [ lambda bits: ((bits+63) >> 3) & ~7, ] - -def l16(c): - return ord(c[0]) + (ord(c[1])<<8) -def l32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) - -def b16(c): - return ord(c[1]) + (ord(c[0])<<8) -def b32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +l16 = _binary.i16le +l32 = _binary.i32le +b16 = _binary.i16be +b32 = _binary.i32be def sz(s, o): - return s[o:string.index(s, "\0", o)] + return s[o:s.index(b"\0", o)] ## # Font file plugin for the X11 PCF format. @@ -68,7 +62,7 @@ class PcfFontFile(FontFile.FontFile): magic = l32(fp.read(4)) if magic != PCF_MAGIC: - raise SyntaxError, "not a PCF file" + raise SyntaxError("not a PCF file") FontFile.FontFile.__init__(self) @@ -126,7 +120,7 @@ class PcfFontFile(FontFile.FontFile): # read property description p = [] for i in range(nprops): - p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4)))) + p.append((i32(fp.read(4)), i8(fp.read(1)), i32(fp.read(4)))) if nprops & 3: fp.seek(4 - (nprops & 3), 1) # pad @@ -155,11 +149,11 @@ class PcfFontFile(FontFile.FontFile): # "compressed" metrics for i in range(i16(fp.read(2))): - left = ord(fp.read(1)) - 128 - right = ord(fp.read(1)) - 128 - width = ord(fp.read(1)) - 128 - ascent = ord(fp.read(1)) - 128 - descent = ord(fp.read(1)) - 128 + left = i8(fp.read(1)) - 128 + right = i8(fp.read(1)) - 128 + width = i8(fp.read(1)) - 128 + ascent = i8(fp.read(1)) - 128 + descent = i8(fp.read(1)) - 128 xsize = right - left ysize = ascent + descent append( @@ -198,7 +192,7 @@ class PcfFontFile(FontFile.FontFile): nbitmaps = i32(fp.read(4)) if nbitmaps != len(metrics): - raise IOError, "Wrong number of bitmaps" + raise IOError("Wrong number of bitmaps") offsets = [] for i in range(nbitmaps): @@ -226,7 +220,7 @@ class PcfFontFile(FontFile.FontFile): x, y, l, r, w, a, d, f = metrics[i] b, e = offsets[i], offsets[i+1] bitmaps.append( - Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x)) + Image.frombytes("1", (x, y), data[b:e], "raw", mode, pad(x)) ) return bitmaps diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py index 15012d51c..42dd9be0b 100644 --- a/PIL/PcxImagePlugin.py +++ b/PIL/PcxImagePlugin.py @@ -27,13 +27,14 @@ __version__ = "0.6" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary -def i16(c,o): - return ord(c[o]) + (ord(c[o+1])<<8) +i8 = _binary.i8 +i16 = _binary.i16le +o8 = _binary.o8 def _accept(prefix): - return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5] + return i8(prefix[0]) == 10 and i8(prefix[1]) in [0, 2, 3, 5] ## # Image plugin for Paintbrush images. @@ -48,17 +49,17 @@ class PcxImageFile(ImageFile.ImageFile): # header s = self.fp.read(128) if not _accept(s): - raise SyntaxError, "not a PCX file" + raise SyntaxError("not a PCX file") # image bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1 if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: - raise SyntaxError, "bad PCX image size" + raise SyntaxError("bad PCX image size") # format - version = ord(s[1]) - bits = ord(s[3]) - planes = ord(s[65]) + version = i8(s[1]) + bits = i8(s[3]) + planes = i8(s[65]) stride = i16(s,66) self.info["dpi"] = i16(s,12), i16(s,14) @@ -76,10 +77,10 @@ class PcxImageFile(ImageFile.ImageFile): # FIXME: hey, this doesn't work with the incremental loader !!! self.fp.seek(-769, 2) s = self.fp.read(769) - if len(s) == 769 and ord(s[0]) == 12: + if len(s) == 769 and i8(s[0]) == 12: # check if the palette is linear greyscale for i in range(256): - if s[i*3+1:i*3+4] != chr(i)*3: + if s[i*3+1:i*3+4] != o8(i)*3: mode = rawmode = "P" break if mode == "P": @@ -91,7 +92,7 @@ class PcxImageFile(ImageFile.ImageFile): rawmode = "RGB;L" else: - raise IOError, "unknown PCX mode" + raise IOError("unknown PCX mode") self.mode = mode self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] @@ -111,21 +112,20 @@ SAVE = { "RGB": (5, 8, 3, "RGB;L"), } -def o16(i): - return chr(i&255) + chr(i>>8&255) +o16 = _binary.o16le def _save(im, fp, filename, check=0): try: version, bits, planes, rawmode = SAVE[im.mode] except KeyError: - raise ValueError, "Cannot save %s images as PCX" % im.mode + raise ValueError("Cannot save %s images as PCX" % im.mode) if check: return check # bytes per plane - stride = (im.size[0] * bits + 7) / 8 + stride = (im.size[0] * bits + 7) // 8 # under windows, we could determine the current screen size with # "Image.core.display_mode()[1]", but I think that's overkill... @@ -136,11 +136,11 @@ def _save(im, fp, filename, check=0): # PCX header fp.write( - chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) + + o8(10) + o8(version) + o8(1) + o8(bits) + o16(0) + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + - o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) + + o16(dpi[1]) + b"\0"*24 + b"\xFF"*24 + b"\0" + o8(planes) + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + - chr(0)*54 + b"\0"*54 ) assert fp.tell() == 128 @@ -150,13 +150,13 @@ def _save(im, fp, filename, check=0): if im.mode == "P": # colour palette - fp.write(chr(12)) + fp.write(o8(12)) fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes elif im.mode == "L": # greyscale palette - fp.write(chr(12)) + fp.write(o8(12)) for i in range(256): - fp.write(chr(i)*3) + fp.write(o8(i)*3) # -------------------------------------------------------------------- # registry diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py index 4f957765c..725f22ecf 100644 --- a/PIL/PdfImagePlugin.py +++ b/PIL/PdfImagePlugin.py @@ -22,8 +22,9 @@ __version__ = "0.4" -import Image, ImageFile -import StringIO +from PIL import Image, ImageFile +from PIL._binary import i8 +import io # @@ -60,6 +61,16 @@ def _save(im, fp, filename): xref = [0]*(5+1) # placeholders + class TextWriter: + def __init__(self, fp): + self.fp = fp + def __getattr__(self, name): + return getattr(self.fp, name) + def write(self, value): + self.fp.write(value.encode('latin-1')) + + fp = TextWriter(fp) + fp.write("%PDF-1.2\n") fp.write("% created by PIL PDF driver " + __version__ + "\n") @@ -90,11 +101,11 @@ def _save(im, fp, filename): colorspace = "[ /Indexed /DeviceRGB 255 <" palette = im.im.getpalette("RGB") for i in range(256): - r = ord(palette[i*3]) - g = ord(palette[i*3+1]) - b = ord(palette[i*3+2]) + r = i8(palette[i*3]) + g = i8(palette[i*3+1]) + b = i8(palette[i*3+2]) colorspace = colorspace + "%02x%02x%02x " % (r, g, b) - colorspace = colorspace + "> ]" + colorspace = colorspace + b"> ]" procset = "/ImageI" # indexed color elif im.mode == "RGB": filter = "/DCTDecode" @@ -127,7 +138,7 @@ def _save(im, fp, filename): # # image - op = StringIO.StringIO() + op = io.BytesIO() if filter == "/ASCIIHexDecode": if bits == 1: @@ -138,7 +149,7 @@ def _save(im, fp, filename): im.putdata(data) ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) elif filter == "/DCTDecode": - ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)]) + Image.SAVE["JPEG"](im, op, filename) elif filter == "/FlateDecode": ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) elif filter == "/RunLengthDecode": @@ -158,7 +169,7 @@ def _save(im, fp, filename): ColorSpace = colorspace) fp.write("stream\n") - fp.write(op.getvalue()) + fp.fp.write(op.getvalue()) fp.write("\nendstream\n") _endobj(fp) @@ -178,15 +189,15 @@ def _save(im, fp, filename): # # page contents - op = StringIO.StringIO() + op = TextWriter(io.BytesIO()) op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) xref[5] = fp.tell() - _obj(fp, 5, Length = len(op.getvalue())) + _obj(fp, 5, Length = len(op.fp.getvalue())) fp.write("stream\n") - fp.write(op.getvalue()) + fp.fp.write(op.fp.getvalue()) fp.write("\nendstream\n") _endobj(fp) diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py index d4c597b61..a4c9032dc 100644 --- a/PIL/PixarImagePlugin.py +++ b/PIL/PixarImagePlugin.py @@ -21,16 +21,13 @@ __version__ = "0.1" -import Image, ImageFile +from PIL import Image, ImageFile, _binary # # helpers -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i16 = _binary.i16le +i32 = _binary.i32le ## # Image plugin for PIXAR raster images. @@ -44,8 +41,8 @@ class PixarImageFile(ImageFile.ImageFile): # assuming a 4-byte magic label (FIXME: add "_accept" hook) s = self.fp.read(4) - if s != "\200\350\000\000": - raise SyntaxError, "not a PIXAR file" + if s != b"\200\350\000\000": + raise SyntaxError("not a PIXAR file") # read rest of header s = s + self.fp.read(508) diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py index 0ee8589a5..7028083d7 100644 --- a/PIL/PngImagePlugin.py +++ b/PIL/PngImagePlugin.py @@ -31,22 +31,23 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "0.9" -import re, string +import re -import Image, ImageFile, ImagePalette, zlib +from PIL import Image, ImageFile, ImagePalette, _binary +import zlib + +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be + +is_cid = re.compile(b"\w\w\w\w").match -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) - -is_cid = re.compile("\w\w\w\w").match - - -_MAGIC = "\211PNG\r\n\032\n" +_MAGIC = b"\211PNG\r\n\032\n" _MODES = { @@ -69,6 +70,8 @@ _MODES = { } +_simple_palette = re.compile(b'^\xff+\x00\xff*$') + # -------------------------------------------------------------------- # Support classes. Suitable for PNG and related formats like MNG etc. @@ -96,7 +99,7 @@ class ChunkStream: len = i32(s) if not is_cid(cid): - raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid) + raise SyntaxError("broken PNG file (chunk %s)" % repr(cid)) return cid, pos, len @@ -111,8 +114,8 @@ class ChunkStream: "Call the appropriate chunk handler" if Image.DEBUG: - print "STREAM", cid, pos, len - return getattr(self, "chunk_" + cid)(pos, len) + print("STREAM", cid, pos, len) + return getattr(self, "chunk_" + cid.decode('ascii'))(pos, len) def crc(self, cid, data): "Read and verify checksum" @@ -120,22 +123,22 @@ class ChunkStream: crc1 = Image.core.crc32(data, Image.core.crc32(cid)) crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) if crc1 != crc2: - raise SyntaxError, "broken PNG file"\ - "(bad header checksum in %s)" % cid + raise SyntaxError("broken PNG file"\ + "(bad header checksum in %s)" % cid) def crc_skip(self, cid, data): "Read checksum. Used if the C module is not present" self.fp.read(4) - def verify(self, endchunk = "IEND"): + def verify(self, endchunk = b"IEND"): # Simple approach; just calculate checksum for all remaining # blocks. Must be called directly after open. cids = [] - while 1: + while True: cid, pos, len = self.read() if cid == endchunk: break @@ -157,11 +160,18 @@ class PngInfo: self.chunks.append((cid, data)) def add_text(self, key, value, zip=0): + # The tEXt chunk stores latin-1 text + if not isinstance(key, bytes): + key = key.encode('latin-1', 'strict') + + if not isinstance(value, bytes): + value = value.encode('latin-1', 'replace') + if zip: import zlib - self.add("zTXt", key + "\0\0" + zlib.compress(value)) + self.add(b"zTXt", key + b"\0\0" + zlib.compress(value)) else: - self.add("tEXt", key + "\0" + value) + self.add(b"tEXt", key + b"\0" + value) # -------------------------------------------------------------------- # PNG image stream (IHDR/IEND) @@ -189,11 +199,11 @@ class PngStream(ChunkStream): # Null separator 1 byte (null character) # Compression method 1 byte (0) # Compressed profile n bytes (zlib with deflate compression) - i = string.find(s, chr(0)) + i = s.find(b"\0") if Image.DEBUG: - print "iCCP profile name", s[:i] - print "Compression method", ord(s[i]) - comp_method = ord(s[i]) + print("iCCP profile name", s[:i]) + print("Compression method", i8(s[i])) + comp_method = i8(s[i]) if comp_method != 0: raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) try: @@ -209,13 +219,13 @@ class PngStream(ChunkStream): s = ImageFile._safe_read(self.fp, len) self.im_size = i32(s), i32(s[4:]) try: - self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))] + self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))] except: pass - if ord(s[12]): + if i8(s[12]): self.im_info["interlace"] = 1 - if ord(s[11]): - raise SyntaxError, "unknown filter category" + if i8(s[11]): + raise SyntaxError("unknown filter category") return s def chunk_IDAT(self, pos, len): @@ -243,9 +253,12 @@ class PngStream(ChunkStream): # transparency s = ImageFile._safe_read(self.fp, len) if self.im_mode == "P": - i = string.find(s, chr(0)) - if i >= 0: - self.im_info["transparency"] = i + if _simple_palette.match(s): + i = s.find(b"\0") + if i >= 0: + self.im_info["transparency"] = i + else: + self.im_info["transparency"] = s elif self.im_mode == "L": self.im_info["transparency"] = i16(s) elif self.im_mode == "RGB": @@ -264,7 +277,7 @@ class PngStream(ChunkStream): # pixels per unit s = ImageFile._safe_read(self.fp, len) px, py = i32(s), i32(s[4:]) - unit = ord(s[8]) + unit = i8(s[8]) if unit == 1: # meter dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) self.im_info["dpi"] = dpi @@ -277,10 +290,14 @@ class PngStream(ChunkStream): # text s = ImageFile._safe_read(self.fp, len) try: - k, v = string.split(s, "\0", 1) + k, v = s.split(b"\0", 1) except ValueError: - k = s; v = "" # fallback for broken tEXt tags + k = s; v = b"" # fallback for broken tEXt tags if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + self.im_info[k] = self.im_text[k] = v return s @@ -288,12 +305,28 @@ class PngStream(ChunkStream): # compressed text s = ImageFile._safe_read(self.fp, len) - k, v = string.split(s, "\0", 1) - comp_method = ord(v[0]) + try: + k, v = s.split(b"\0", 1) + except ValueError: + k = s; v = b"" + if v: + comp_method = i8(v[0]) + else: + comp_method = 0 if comp_method != 0: raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) import zlib - self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:]) + try: + v = zlib.decompress(v[1:]) + except zlib.error: + v = b"" + + if k: + if bytes is not str: + k = k.decode('latin-1', 'strict') + v = v.decode('latin-1', 'replace') + + self.im_info[k] = self.im_text[k] = v return s # -------------------------------------------------------------------- @@ -313,14 +346,14 @@ class PngImageFile(ImageFile.ImageFile): def _open(self): if self.fp.read(8) != _MAGIC: - raise SyntaxError, "not a PNG file" + raise SyntaxError("not a PNG file") # # Parse headers up to the first IDAT chunk self.png = PngStream(self.fp) - while 1: + while True: # # get next chunk @@ -333,7 +366,7 @@ class PngImageFile(ImageFile.ImageFile): break except AttributeError: if Image.DEBUG: - print cid, pos, len, "(unknown)" + print(cid, pos, len, "(unknown)") s = ImageFile._safe_read(self.fp, len) self.png.crc(cid, s) @@ -390,9 +423,9 @@ class PngImageFile(ImageFile.ImageFile): cid, pos, len = self.png.read() - if cid not in ["IDAT", "DDAT"]: + if cid not in [b"IDAT", b"DDAT"]: self.png.push(cid, pos, len) - return "" + return b"" self.__idat = len # empty chunks are allowed @@ -417,33 +450,31 @@ class PngImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # PNG writer -def o16(i): - return chr(i>>8&255) + chr(i&255) - -def o32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +o8 = _binary.o8 +o16 = _binary.o16be +o32 = _binary.o32be _OUTMODES = { # supported PIL modes, and corresponding rawmodes/bits/color combinations - "1": ("1", chr(1)+chr(0)), - "L;1": ("L;1", chr(1)+chr(0)), - "L;2": ("L;2", chr(2)+chr(0)), - "L;4": ("L;4", chr(4)+chr(0)), - "L": ("L", chr(8)+chr(0)), - "LA": ("LA", chr(8)+chr(4)), - "I": ("I;16B", chr(16)+chr(0)), - "P;1": ("P;1", chr(1)+chr(3)), - "P;2": ("P;2", chr(2)+chr(3)), - "P;4": ("P;4", chr(4)+chr(3)), - "P": ("P", chr(8)+chr(3)), - "RGB": ("RGB", chr(8)+chr(2)), - "RGBA":("RGBA", chr(8)+chr(6)), + "1": ("1", b'\x01\x00'), + "L;1": ("L;1", b'\x01\x00'), + "L;2": ("L;2", b'\x02\x00'), + "L;4": ("L;4", b'\x04\x00'), + "L": ("L", b'\x08\x00'), + "LA": ("LA", b'\x08\x04'), + "I": ("I;16B", b'\x10\x00'), + "P;1": ("P;1", b'\x01\x03'), + "P;2": ("P;2", b'\x02\x03'), + "P;4": ("P;4", b'\x04\x03'), + "P": ("P", b'\x08\x03'), + "RGB": ("RGB", b'\x08\x02'), + "RGBA":("RGBA", b'\x08\x06'), } def putchunk(fp, cid, *data): "Write a PNG chunk (including CRC field)" - data = string.join(data, "") + data = b"".join(data) fp.write(o32(len(data)) + cid) fp.write(data) @@ -457,7 +488,7 @@ class _idat: self.fp = fp self.chunk = chunk def write(self, data): - self.chunk(self.fp, "IDAT", data) + self.chunk(self.fp, b"IDAT", data) def _save(im, fp, filename, chunk=putchunk, check=0): # save an image to disk (called by the save method) @@ -468,42 +499,43 @@ def _save(im, fp, filename, chunk=putchunk, check=0): # # attempt to minimize storage requirements for palette images - - if im.encoderinfo.has_key("bits"): - + if "bits" in im.encoderinfo: # number of bits specified by user - n = 1 << im.encoderinfo["bits"] - + colors = 1 << im.encoderinfo["bits"] else: - # check palette contents - n = 256 # FIXME + if im.palette: + colors = len(im.palette.getdata()[1])//3 + else: + colors = 256 - if n <= 2: + if colors <= 2: bits = 1 - elif n <= 4: + elif colors <= 4: bits = 2 - elif n <= 16: + elif colors <= 16: bits = 4 else: bits = 8 - if bits != 8: mode = "%s;%d" % (mode, bits) # encoder options - if im.encoderinfo.has_key("dictionary"): + if "dictionary" in im.encoderinfo: dictionary = im.encoderinfo["dictionary"] else: - dictionary = "" + dictionary = b"" - im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary) + im.encoderconfig = ("optimize" in im.encoderinfo, + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + dictionary) # get the corresponding PNG mode try: rawmode, mode = _OUTMODES[mode] except KeyError: - raise IOError, "cannot write mode %s as PNG" % mode + raise IOError("cannot write mode %s as PNG" % mode) if check: return check @@ -513,39 +545,59 @@ def _save(im, fp, filename, chunk=putchunk, check=0): fp.write(_MAGIC) - chunk(fp, "IHDR", + chunk(fp, b"IHDR", o32(im.size[0]), o32(im.size[1]), # 0: size mode, # 8: depth/type - chr(0), # 10: compression - chr(0), # 11: filter category - chr(0)) # 12: interlace flag + b'\0', # 10: compression + b'\0', # 11: filter category + b'\0') # 12: interlace flag if im.mode == "P": - chunk(fp, "PLTE", im.im.getpalette("RGB")) + palette_byte_number = (2 ** bits) * 3 + palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + while len(palette_bytes) < palette_byte_number: + palette_bytes += b'\0' + chunk(fp, b"PLTE", palette_bytes) - if im.encoderinfo.has_key("transparency"): + transparency = im.encoderinfo.get('transparency',im.info.get('transparency', None)) + + if transparency: if im.mode == "P": - transparency = max(0, min(255, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", chr(255) * transparency + chr(0)) + # limit to actual palette size + alpha_bytes = 2**bits + if isinstance(transparency, bytes): + chunk(fp, b"tRNS", transparency[:alpha_bytes]) + else: + transparency = max(0, min(255, transparency)) + alpha = b'\xFF' * transparency + b'\0' + chunk(fp, b"tRNS", alpha[:alpha_bytes]) elif im.mode == "L": - transparency = max(0, min(65535, im.encoderinfo["transparency"])) - chunk(fp, "tRNS", o16(transparency)) + transparency = max(0, min(65535, transparency)) + chunk(fp, b"tRNS", o16(transparency)) elif im.mode == "RGB": - red, green, blue = im.encoderinfo["transparency"] - chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue)) + red, green, blue = transparency + chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue)) else: - raise IOError("cannot use transparency for this mode") + if "transparency" in im.encoderinfo: + # don't bother with transparency if it's an RGBA + # and it's in the info dict. It's probably just stale. + raise IOError("cannot use transparency for this mode") + else: + if im.mode == "P" and im.im.getpalettemode() == "RGBA": + alpha = im.im.getpalette("RGBA", "A") + alpha_bytes = 2**bits + chunk(fp, b"tRNS", alpha[:alpha_bytes]) if 0: # FIXME: to be supported some day - chunk(fp, "gAMA", o32(int(gamma * 100000.0))) + chunk(fp, b"gAMA", o32(int(gamma * 100000.0))) dpi = im.encoderinfo.get("dpi") if dpi: - chunk(fp, "pHYs", + chunk(fp, b"pHYs", o32(int(dpi[0] / 0.0254 + 0.5)), o32(int(dpi[1] / 0.0254 + 0.5)), - chr(1)) + b'\x01') info = im.encoderinfo.get("pnginfo") if info: @@ -553,7 +605,7 @@ def _save(im, fp, filename, chunk=putchunk, check=0): chunk(fp, cid, data) # ICC profile writing support -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: # ICC profile # according to PNG spec, the iCCP chunk contains: # Profile name 1-79 bytes (character string) @@ -565,13 +617,13 @@ def _save(im, fp, filename, chunk=putchunk, check=0): p = ICCProfile.ICCProfile(im.info["icc_profile"]) name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] except ImportError: - name = "ICC Profile" - data = name + "\0\0" + zlib.compress(im.info["icc_profile"]) - chunk(fp, "iCCP", data) + name = b"ICC Profile" + data = name + b"\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, b"iCCP", data) ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) - chunk(fp, "IEND", "") + chunk(fp, b"IEND", b"") try: fp.flush() @@ -593,7 +645,7 @@ def getchunks(im, **params): self.data.append(chunk) def append(fp, cid, *data): - data = string.join(data, "") + data = b"".join(data) hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) crc = o16(hi) + o16(lo) fp.append((cid, data, crc)) diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py index e86146c10..9aa5b1135 100644 --- a/PIL/PpmImagePlugin.py +++ b/PIL/PpmImagePlugin.py @@ -19,26 +19,36 @@ __version__ = "0.2" import string -import Image, ImageFile +from PIL import Image, ImageFile # # -------------------------------------------------------------------- +b_whitespace = string.whitespace +try: + import locale + locale_lang,locale_enc = locale.getlocale() + if locale_enc is None: + locale_lang,locale_enc = locale.getdefaultlocale() + b_whitespace = b_whitespace.decode(locale_enc) +except: pass +b_whitespace = b_whitespace.encode('ascii','ignore') + MODES = { # standard - "P4": "1", - "P5": "L", - "P6": "RGB", + b"P4": "1", + b"P5": "L", + b"P6": "RGB", # extensions - "P0CMYK": "CMYK", + b"P0CMYK": "CMYK", # PIL extensions (for test purposes only) - "PyP": "P", - "PyRGBA": "RGBA", - "PyCMYK": "CMYK" + b"PyP": "P", + b"PyRGBA": "RGBA", + b"PyCMYK": "CMYK" } def _accept(prefix): - return prefix[0] == "P" and prefix[1] in "0456y" + return prefix[0:1] == b"P" and prefix[1] in b"0456y" ## # Image plugin for PBM, PGM, and PPM images. @@ -48,10 +58,10 @@ class PpmImageFile(ImageFile.ImageFile): format = "PPM" format_description = "Pbmplus image" - def _token(self, s = ""): - while 1: # read until next whitespace + def _token(self, s = b""): + while True: # read until next whitespace c = self.fp.read(1) - if not c or c in string.whitespace: + if not c or c in b_whitespace: break s = s + c return s @@ -60,8 +70,8 @@ class PpmImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(1) - if s != "P": - raise SyntaxError, "not a PPM file" + if s != b"P": + raise SyntaxError("not a PPM file") mode = MODES[self._token(s)] if mode == "1": @@ -71,12 +81,12 @@ class PpmImageFile(ImageFile.ImageFile): self.mode = rawmode = mode for ix in range(3): - while 1: - while 1: + while True: + while True: s = self.fp.read(1) - if s not in string.whitespace: + if s not in b_whitespace: break - if s != "#": + if s != b"#": break s = self.fp.readline() s = int(self._token(s)) @@ -103,18 +113,18 @@ class PpmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode == "1": - rawmode, head = "1;I", "P4" + rawmode, head = "1;I", b"P4" elif im.mode == "L": - rawmode, head = "L", "P5" + rawmode, head = "L", b"P5" elif im.mode == "RGB": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" elif im.mode == "RGBA": - rawmode, head = "RGB", "P6" + rawmode, head = "RGB", b"P6" else: - raise IOError, "cannot write mode %s as PPM" % im.mode - fp.write(head + "\n%d %d\n" % im.size) - if head != "P4": - fp.write("255\n") + raise IOError("cannot write mode %s as PPM" % im.mode) + fp.write(head + ("\n%d %d\n" % im.size).encode('ascii')) + if head != b"P4": + fp.write(b"255\n") ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))]) # ALTERNATIVE: save via builtin debug function diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 9d1b17a36..f6aefe9c9 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -18,7 +18,7 @@ __version__ = "0.4" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary MODES = { # (photoshop mode, bits) -> (pil mode, required channels) @@ -36,17 +36,15 @@ MODES = { # # helpers -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be # --------------------------------------------------------------------. # read PSD images def _accept(prefix): - return prefix[:4] == "8BPS" + return prefix[:4] == b"8BPS" ## # Image plugin for Photoshop images. @@ -64,8 +62,8 @@ class PsdImageFile(ImageFile.ImageFile): # header s = read(26) - if s[:4] != "8BPS" or i16(s[4:]) != 1: - raise SyntaxError, "not a PSD file" + if s[:4] != b"8BPS" or i16(s[4:]) != 1: + raise SyntaxError("not a PSD file") psd_bits = i16(s[22:]) psd_channels = i16(s[12:]) @@ -74,7 +72,7 @@ class PsdImageFile(ImageFile.ImageFile): mode, channels = MODES[(psd_mode, psd_bits)] if channels > psd_channels: - raise IOError, "not enough channels" + raise IOError("not enough channels") self.mode = mode self.size = i32(s[18:]), i32(s[14:]) @@ -100,7 +98,7 @@ class PsdImageFile(ImageFile.ImageFile): while self.fp.tell() < end: signature = read(4) id = i16(read(2)) - name = read(ord(read(1))) + name = read(i8(read(1))) if not (len(name) & 1): read(1) # padding data = read(i32(read(4))) @@ -146,7 +144,7 @@ class PsdImageFile(ImageFile.ImageFile): self.fp = self._fp return name, bbox except IndexError: - raise EOFError, "no such layer" + raise EOFError("no such layer") def tell(self): # return layer number (0=image, 1..max=layers) @@ -165,7 +163,6 @@ def _layerinfo(file): # read layerinfo block layers = [] read = file.read - for i in range(abs(i16(read(2)))): # bounding box @@ -175,12 +172,18 @@ def _layerinfo(file): # image info info = [] mode = [] - for i in range(i16(read(2))): + types = list(range(i16(read(2)))) + if len(types) > 4: + continue + + for i in types: type = i16(read(2)) + if type == 65535: m = "A" else: - m = "RGB"[type] + m = "RGBA"[type] + mode.append(m) size = i32(read(4)) info.append((m, size)) @@ -214,9 +217,10 @@ def _layerinfo(file): file.seek(length, 1) combined += length + 4 - length = ord(read(1)) + length = i8(read(1)) if length: - name = read(length) + # Don't know the proper encoding, Latin-1 should be a good guess + name = read(length).decode('latin-1', 'replace') combined += length + 1 file.seek(size - combined, 1) diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py index c65b91aca..b60df473c 100644 --- a/PIL/SgiImagePlugin.py +++ b/PIL/SgiImagePlugin.py @@ -21,14 +21,11 @@ __version__ = "0.2" -import Image, ImageFile +from PIL import Image, ImageFile, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i8 = _binary.i8 +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -50,10 +47,10 @@ class SgiImageFile(ImageFile.ImageFile): raise SyntaxError("not an SGI image file") # relevant header entries - compression = ord(s[2]) + compression = i8(s[2]) # bytes, dimension, zsize - layout = ord(s[3]), i16(s[4:]), i16(s[10:]) + layout = i8(s[3]), i16(s[4:]), i16(s[10:]) # determine mode from bytes/zsize if layout == (1, 2, 1) or layout == (1, 1, 1): diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index e73d55918..9e814932d 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -33,7 +33,9 @@ # http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html # -import Image, ImageFile +from __future__ import print_function + +from PIL import Image, ImageFile import os, struct, sys def isInt(f): @@ -101,14 +103,14 @@ class SpiderImageFile(ImageFile.ImageFile): t = struct.unpack('<27f',f) # little-endian hdrlen = isSpiderHeader(t) if hdrlen == 0: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") except struct.error: - raise SyntaxError, "not a valid Spider file" + raise SyntaxError("not a valid Spider file") h = (99,) + t # add 1 value : spider header index starts at 1 iform = int(h[5]) if iform != 1: - raise SyntaxError, "not a Spider 2D image" + raise SyntaxError("not a Spider 2D image") self.size = int(h[12]), int(h[2]) # size in pixels (width, height) self.istack = int(h[24]) @@ -131,7 +133,7 @@ class SpiderImageFile(ImageFile.ImageFile): offset = hdrlen + self.stkoffset self.istack = 2 # So Image knows it's still a stack else: - raise SyntaxError, "inconsistent stack header values" + raise SyntaxError("inconsistent stack header values") if self.bigendian: self.rawmode = "F;32BF" @@ -154,7 +156,7 @@ class SpiderImageFile(ImageFile.ImageFile): if self.istack == 0: return if frame >= self.nimages: - raise EOFError, "attempt to seek past end of file" + raise EOFError("attempt to seek past end of file") self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp self.fp.seek(self.stkoffset) @@ -171,7 +173,7 @@ class SpiderImageFile(ImageFile.ImageFile): # returns a ImageTk.PhotoImage object, after rescaling to 0..255 def tkPhotoImage(self): - import ImageTk + from PIL import ImageTk return ImageTk.PhotoImage(self.convert2byte(), palette=256) # -------------------------------------------------------------------- @@ -186,13 +188,13 @@ def loadImageSeries(filelist=None): imglist = [] for img in filelist: if not os.path.exists(img): - print "unable to find %s" % img + print("unable to find %s" % img) continue try: im = Image.open(img).convert2byte() except: if not isSpiderImage(img): - print img + " is not a Spider image file" + print(img + " is not a Spider image file") continue im.info['filename'] = img imglist.append(im) @@ -239,13 +241,13 @@ def _save(im, fp, filename): hdr = makeSpiderHeader(im) if len(hdr) < 256: - raise IOError, "Error creating Spider header" + raise IOError("Error creating Spider header") # write the SPIDER header try: fp = open(filename, 'wb') except: - raise IOError, "Unable to open %s for writing" % filename + raise IOError("Unable to open %s for writing" % filename) fp.writelines(hdr) rawmode = "F;32NF" #32-bit native floating point @@ -267,12 +269,12 @@ Image.register_save("SPIDER", _save_spider) if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]" + print("Syntax: python SpiderImagePlugin.py Spiderimage [outfile]") sys.exit() filename = sys.argv[1] if not isSpiderImage(filename): - print "input image must be in Spider format" + print("input image must be in Spider format") sys.exit() outfile = "" @@ -280,15 +282,15 @@ if __name__ == "__main__": outfile = sys.argv[2] im = Image.open(filename) - print "image: " + str(im) - print "format: " + str(im.format) - print "size: " + str(im.size) - print "mode: " + str(im.mode) - print "max, min: ", - print im.getextrema() + print("image: " + str(im)) + print("format: " + str(im.format)) + print("size: " + str(im.size)) + print("mode: " + str(im.mode)) + print("max, min: ", end=' ') + print(im.getextrema()) if outfile != "": # perform some image operation im = im.transpose(Image.FLIP_LEFT_RIGHT) - print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile) + print("saving a flipped version of %s as %s " % (os.path.basename(filename), outfile)) im.save(outfile, "SPIDER") diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py index d189562a4..0db02ad25 100644 --- a/PIL/SunImagePlugin.py +++ b/PIL/SunImagePlugin.py @@ -20,14 +20,10 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary - -def i16(c): - return ord(c[1]) + (ord(c[0])<<8) - -def i32(c): - return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) +i16 = _binary.i16be +i32 = _binary.i32be def _accept(prefix): @@ -46,7 +42,7 @@ class SunImageFile(ImageFile.ImageFile): # HEAD s = self.fp.read(32) if i32(s) != 0x59a66a95: - raise SyntaxError, "not an SUN raster file" + raise SyntaxError("not an SUN raster file") offset = 32 @@ -60,7 +56,7 @@ class SunImageFile(ImageFile.ImageFile): elif depth == 24: self.mode, rawmode = "RGB", "BGR" else: - raise SyntaxError, "unsupported mode" + raise SyntaxError("unsupported mode") compression = i32(s[20:24]) @@ -71,7 +67,7 @@ class SunImageFile(ImageFile.ImageFile): if self.mode == "L": self.mode = rawmode = "P" - stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3) + stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3) if compression == 1: self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))] diff --git a/PIL/TarIO.py b/PIL/TarIO.py index d5de729cd..bba493e8f 100644 --- a/PIL/TarIO.py +++ b/PIL/TarIO.py @@ -14,8 +14,7 @@ # See the README file for information on usage and redistribution. # -import ContainerIO -import string +from PIL import ContainerIO ## # A file object that provides read access to a given member of a TAR @@ -33,20 +32,20 @@ class TarIO(ContainerIO.ContainerIO): fh = open(tarfile, "rb") - while 1: + while True: s = fh.read(512) if len(s) != 512: - raise IOError, "unexpected end of tar file" + raise IOError("unexpected end of tar file") - name = s[:100] - i = string.find(name, chr(0)) + name = s[:100].decode('utf-8') + i = name.find('\0') if i == 0: - raise IOError, "cannot find subfile" + raise IOError("cannot find subfile") if i > 0: name = name[:i] - size = string.atoi(s[124:136], 8) + size = int(s[124:135], 8) if file == name: break diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py index 3375991ca..55790db08 100644 --- a/PIL/TgaImagePlugin.py +++ b/PIL/TgaImagePlugin.py @@ -19,18 +19,16 @@ __version__ = "0.3" -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary # # -------------------------------------------------------------------- # Read RGA file -def i16(c): - return ord(c[0]) + (ord(c[1])<<8) - -def i32(c): - return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) +i8 = _binary.i8 +i16 = _binary.i16le +i32 = _binary.i32le MODES = { @@ -45,7 +43,7 @@ MODES = { def _accept(prefix): - return prefix[0] == "\0" + return prefix[0:1] == b"\0" ## # Image plugin for Targa files. @@ -60,14 +58,14 @@ class TgaImageFile(ImageFile.ImageFile): # process header s = self.fp.read(18) - id = ord(s[0]) + id = i8(s[0]) - colormaptype = ord(s[1]) - imagetype = ord(s[2]) + colormaptype = i8(s[1]) + imagetype = i8(s[2]) - depth = ord(s[16]) + depth = i8(s[16]) - flags = ord(s[17]) + flags = i8(s[17]) self.size = i16(s[12:]), i16(s[14:]) @@ -75,7 +73,7 @@ class TgaImageFile(ImageFile.ImageFile): if id != 0 or colormaptype not in (0, 1) or\ self.size[0] <= 0 or self.size[1] <= 0 or\ depth not in (1, 8, 16, 24, 32): - raise SyntaxError, "not a TGA file" + raise SyntaxError("not a TGA file") # image mode if imagetype in (3, 11): @@ -89,7 +87,7 @@ class TgaImageFile(ImageFile.ImageFile): if depth == 32: self.mode = "RGBA" else: - raise SyntaxError, "unknown TGA mode" + raise SyntaxError("unknown TGA mode") # orientation orientation = flags & 0x30 @@ -98,7 +96,7 @@ class TgaImageFile(ImageFile.ImageFile): elif not orientation: orientation = -1 else: - raise SyntaxError, "unknown TGA orientation" + raise SyntaxError("unknown TGA orientation") self.info["orientation"] = orientation @@ -110,13 +108,13 @@ class TgaImageFile(ImageFile.ImageFile): start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) if mapdepth == 16: self.palette = ImagePalette.raw("BGR;16", - "\0"*2*start + self.fp.read(2*size)) + b"\0"*2*start + self.fp.read(2*size)) elif mapdepth == 24: self.palette = ImagePalette.raw("BGR", - "\0"*3*start + self.fp.read(3*size)) + b"\0"*3*start + self.fp.read(3*size)) elif mapdepth == 32: self.palette = ImagePalette.raw("BGRA", - "\0"*4*start + self.fp.read(4*size)) + b"\0"*4*start + self.fp.read(4*size)) # setup tile descriptor try: @@ -135,11 +133,9 @@ class TgaImageFile(ImageFile.ImageFile): # -------------------------------------------------------------------- # Write TGA file -def o16(i): - return chr(i&255) + chr(i>>8&255) - -def o32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +o8 = _binary.o8 +o16 = _binary.o16le +o32 = _binary.o32le SAVE = { "1": ("1", 1, 0, 3), @@ -173,18 +169,18 @@ def _save(im, fp, filename, check=0): if orientation > 0: flags = flags | 0x20 - fp.write("\000" + - chr(colormaptype) + - chr(imagetype) + + fp.write(b"\000" + + o8(colormaptype) + + o8(imagetype) + o16(colormapfirst) + o16(colormaplength) + - chr(colormapentry) + + o8(colormapentry) + o16(0) + o16(0) + o16(im.size[0]) + o16(im.size[1]) + - chr(bits) + - chr(flags)) + o8(bits) + + o8(flags)) if colormaptype: fp.write(im.im.getpalette("RGB", "BGR")) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 8a0fde9cd..3c62fd2dd 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -39,48 +39,49 @@ # See the README file for information on usage and redistribution. # +from __future__ import print_function + __version__ = "1.3.5" -import Image, ImageFile -import ImagePalette +from PIL import Image, ImageFile +from PIL import ImagePalette +from PIL import _binary +from PIL._util import isStringType -import array, string, sys +import warnings +import array, sys +import collections +import itertools +import os -II = "II" # little-endian (intel-style) -MM = "MM" # big-endian (motorola-style) +# Set these to true to force use of libtiff for reading or writing. +READ_LIBTIFF = False +WRITE_LIBTIFF= False -try: - if sys.byteorder == "little": - native_prefix = II - else: - native_prefix = MM -except AttributeError: - if ord(array.array("i",[1]).tostring()[0]): - native_prefix = II - else: - native_prefix = MM +II = b"II" # little-endian (intel-style) +MM = b"MM" # big-endian (motorola-style) + +i8 = _binary.i8 +o8 = _binary.o8 + +if sys.byteorder == "little": + native_prefix = II +else: + native_prefix = MM # # -------------------------------------------------------------------- # Read TIFF files -def il16(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) -def il32(c,o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) -def ol16(i): - return chr(i&255) + chr(i>>8&255) -def ol32(i): - return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) +il16 = _binary.i16le +il32 = _binary.i32le +ol16 = _binary.o16le +ol32 = _binary.o32le -def ib16(c,o=0): - return ord(c[o+1]) + (ord(c[o])<<8) -def ib32(c,o=0): - return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) -def ob16(i): - return chr(i>>8&255) + chr(i&255) -def ob32(i): - return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) +ib16 = _binary.i16be +ib32 = _binary.i32be +ob16 = _binary.o16be +ob32 = _binary.o32be # a few tag names, just to make the code below a bit more readable IMAGEWIDTH = 256 @@ -114,6 +115,10 @@ ICCPROFILE = 34675 EXIFIFD = 34665 XMP = 700 +# https://github.com/fiji/ImageJA/blob/master/src/main/java/ij/io/TiffDecoder.java +IMAGEJ_META_DATA_BYTE_COUNTS = 50838 +IMAGEJ_META_DATA = 50839 + COMPRESSION_INFO = { # Compression => pil compression name 1: "raw", @@ -123,10 +128,17 @@ COMPRESSION_INFO = { 5: "tiff_lzw", 6: "tiff_jpeg", # obsolete 7: "jpeg", + 8: "tiff_adobe_deflate", 32771: "tiff_raw_16", # 16-bit padding - 32773: "packbits" + 32773: "packbits", + 32809: "tiff_thunderscan", + 32946: "tiff_deflate", + 34676: "tiff_sgilog", + 34677: "tiff_sgilog24", } +COMPRESSION_INFO_REV = dict([(v,k) for (k,v) in COMPRESSION_INFO.items()]) + OPEN_INFO = { # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, # ExtraSamples) => mode, rawmode @@ -139,12 +151,15 @@ OPEN_INFO = { (II, 1, 1, 1, (8,), ()): ("L", "L"), (II, 1, 1, 1, (8,8), (2,)): ("LA", "LA"), (II, 1, 1, 2, (8,), ()): ("L", "L;R"), + (II, 1, 1, 1, (12,), ()): ("I;16", "I;12"), (II, 1, 1, 1, (16,), ()): ("I;16", "I;16"), (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"), + (II, 1, 1, 1, (32,), ()): ("I", "I;32N"), (II, 1, 2, 1, (32,), ()): ("I", "I;32S"), (II, 1, 3, 1, (32,), ()): ("F", "F;32F"), (II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"), (II, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"), + (II, 2, 1, 1, (8,8,8,8), ()): ("RGBA", "RGBA"), # missing ExtraSamples (II, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"), (II, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"), (II, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"), @@ -174,7 +189,7 @@ OPEN_INFO = { (MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"), (MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"), (MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"), - (MM, 1, 3, 1, (32,), ()): ("F;32BF", "F;32BF"), + (MM, 1, 3, 1, (32,), ()): ("F", "F;32BF"), (MM, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"), (MM, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"), (MM, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"), @@ -196,7 +211,7 @@ OPEN_INFO = { } -PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"] +PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"] def _accept(prefix): return prefix[:4] in PREFIXES @@ -204,12 +219,46 @@ def _accept(prefix): ## # Wrapper for TIFF IFDs. -class ImageFileDirectory: +class ImageFileDirectory(collections.MutableMapping): + """ This class represents a TIFF tag directory. To speed things + up, we don't decode tags unless they're asked for. - # represents a TIFF tag directory. to speed things up, - # we don't decode tags unless they're asked for. + Exposes a dictionary interface of the tags in the directory + ImageFileDirectory[key] = value + value = ImageFileDirectory[key] - def __init__(self, prefix): + Also contains a dictionary of tag types as read from the tiff + image file, 'ImageFileDirectory.tagtype' + + + Data Structures: + 'public' + * self.tagtype = {} Key: numerical tiff tag number + Value: integer corresponding to the data type from + `TiffTags.TYPES` + + 'internal' + * self.tags = {} Key: numerical tiff tag number + Value: Decoded data, Generally a tuple. + * If set from __setval__ -- always a tuple + * Numeric types -- always a tuple + * String type -- not a tuple, returned as string + * Undefined data -- not a tuple, returned as bytes + * Byte -- not a tuple, returned as byte. + * self.tagdata = {} Key: numerical tiff tag number + Value: undecoded byte string from file + + + Tags will be found in either self.tags or self.tagdata, but + not both. The union of the two should contain all the tags + from the Tiff image file. External classes shouldn't + reference these unless they're really sure what they're doing. + """ + + def __init__(self, prefix=II): + """ + :prefix: 'II'|'MM' tiff endianness + """ self.prefix = prefix[:2] if self.prefix == MM: self.i16, self.i32 = ib16, ib32 @@ -222,21 +271,31 @@ class ImageFileDirectory: self.reset() def reset(self): + #: Tags is an incomplete dictionary of the tags of the image. + #: For a complete dictionary, use the as_dict method. self.tags = {} self.tagdata = {} self.tagtype = {} # added 2008-06-05 by Florian Hoech self.next = None - # dictionary API (sort of) + def __str__(self): + return str(self.as_dict()) - def keys(self): - return self.tagdata.keys() + self.tags.keys() + def as_dict(self): + """Return a dictionary of the image's tags.""" + return dict(self.items()) - def items(self): - items = self.tags.items() - for tag in self.tagdata.keys(): - items.append((tag, self[tag])) - return items + def named(self): + """Returns the complete tag dictionary, with named tags where posible.""" + from PIL import TiffTags + result = {} + for tag_code, value in self.items(): + tag_name = TiffTags.TAGS.get(tag_code, tag_code) + result[tag_name] = value + return result + + + # dictionary API def __len__(self): return len(self.tagdata) + len(self.tags) @@ -245,18 +304,13 @@ class ImageFileDirectory: try: return self.tags[tag] except KeyError: - type, data = self.tagdata[tag] # unpack on the fly + data = self.tagdata[tag] # unpack on the fly + type = self.tagtype[tag] size, handler = self.load_dispatch[type] self.tags[tag] = data = handler(self, data) del self.tagdata[tag] return data - def get(self, tag, default=None): - try: - return self[tag] - except KeyError: - return default - def getscalar(self, tag, default=None): try: value = self[tag] @@ -265,36 +319,51 @@ class ImageFileDirectory: # work around broken (?) matrox library # (from Ted Wright, via Bob Klimek) raise KeyError # use default - raise ValueError, "not a scalar" + raise ValueError("not a scalar") return value[0] except KeyError: if default is None: raise return default - def has_key(self, tag): - return self.tags.has_key(tag) or self.tagdata.has_key(tag) + def __contains__(self, tag): + return tag in self.tags or tag in self.tagdata + + if bytes is str: + def has_key(self, tag): + return tag in self def __setitem__(self, tag, value): - if type(value) is not type(()): + # tags are tuples for integers + # tags are not tuples for byte, string, and undefined data. + # see load_* + if not isinstance(value, tuple): value = (value,) self.tags[tag] = value + def __delitem__(self, tag): + self.tags.pop(tag, self.tagdata.pop(tag, None)) + + def __iter__(self): + return itertools.chain(self.tags.__iter__(), self.tagdata.__iter__()) + + def items(self): + keys = list(self.__iter__()) + values = [self[key] for key in keys] + return zip(keys, values) + # load primitives load_dispatch = {} def load_byte(self, data): - l = [] - for i in range(len(data)): - l.append(ord(data[i])) - return tuple(l) + return data load_dispatch[1] = (1, load_byte) def load_string(self, data): - if data[-1:] == '\0': + if data[-1:] == b'\0': data = data[:-1] - return data + return data.decode('latin-1', 'replace') load_dispatch[2] = (1, load_string) def load_short(self, data): @@ -352,17 +421,17 @@ class ImageFileDirectory: tag, typ = i16(ifd), i16(ifd, 2) if Image.DEBUG: - import TiffTags + from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "tag: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("tag: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') try: dispatch = self.load_dispatch[typ] except KeyError: if Image.DEBUG: - print "- unsupported type", typ + print("- unsupported type", typ) continue # ignore unsupported type size, handler = dispatch @@ -379,16 +448,17 @@ class ImageFileDirectory: data = ifd[8:8+size] if len(data) != size: - raise IOError, "not enough data" + warnings.warn("Possibly corrupt EXIF data. Expecting to read %d bytes but only got %d. Skipping tag %s" % (size, len(data), tag)) + continue - self.tagdata[tag] = typ, data + self.tagdata[tag] = data self.tagtype[tag] = typ if Image.DEBUG: if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): - print "- value: " % size + print("- value: " % size) else: - print "- value:", self[tag] + print("- value:", self[tag]) self.next = i32(fp.read(4)) @@ -402,8 +472,7 @@ class ImageFileDirectory: fp.write(o16(len(self.tags))) # always write in ascending tag order - tags = self.tags.items() - tags.sort() + tags = sorted(self.tags.items()) directory = [] append = directory.append @@ -417,58 +486,76 @@ class ImageFileDirectory: typ = None - if self.tagtype.has_key(tag): + if tag in self.tagtype: typ = self.tagtype[tag] - + + if Image.DEBUG: + print ("Tag %s, Type: %s, Value: %s" % (tag, typ, value)) + if typ == 1: # byte data - data = value = string.join(map(chr, value), "") + if isinstance(value, tuple): + data = value = value[-1] + else: + data = value elif typ == 7: # untyped data - data = value = string.join(value, "") - elif type(value[0]) is type(""): + data = value = b"".join(value) + elif isStringType(value[0]): # string data + if isinstance(value, tuple): + value = value[-1] typ = 2 - data = value = string.join(value, "\0") + "\0" + # was b'\0'.join(str), which led to \x00a\x00b sorts + # of strings which I don't see in in the wild tiffs + # and doesn't match the tiff spec: 8-bit byte that + # contains a 7-bit ASCII code; the last byte must be + # NUL (binary zero). Also, I don't think this was well + # excersized before. + data = value = b"" + value.encode('ascii', 'replace') + b"\0" else: # integer data if tag == STRIPOFFSETS: stripoffsets = len(directory) typ = 4 # to avoid catch-22 - elif tag in (X_RESOLUTION, Y_RESOLUTION): + elif tag in (X_RESOLUTION, Y_RESOLUTION) or typ==5: # identify rational data fields typ = 5 + if isinstance(value[0], tuple): + # long name for flatten + value = tuple(itertools.chain.from_iterable(value)) elif not typ: typ = 3 for v in value: if v >= 65536: typ = 4 if typ == 3: - data = string.join(map(o16, value), "") + data = b"".join(map(o16, value)) else: - data = string.join(map(o32, value), "") + data = b"".join(map(o32, value)) if Image.DEBUG: - import TiffTags + from PIL import TiffTags tagname = TiffTags.TAGS.get(tag, "unknown") typname = TiffTags.TYPES.get(typ, "unknown") - print "save: %s (%d)" % (tagname, tag), - print "- type: %s (%d)" % (typname, typ), + print("save: %s (%d)" % (tagname, tag), end=' ') + print("- type: %s (%d)" % (typname, typ), end=' ') if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): size = len(data) - print "- value: " % size + print("- value: " % size) else: - print "- value:", value + print("- value:", value) # figure out if data fits into the directory if len(data) == 4: - append((tag, typ, len(value), data, "")) + append((tag, typ, len(value), data, b"")) elif len(data) < 4: - append((tag, typ, len(value), data + (4-len(data))*"\0", "")) + append((tag, typ, len(value), data + (4-len(data))*b"\0", b"")) else: count = len(value) if typ == 5: - count = count / 2 # adjust for rational data field + count = count // 2 # adjust for rational data field + append((tag, typ, count, o32(offset), data)) offset = offset + len(data) if offset & 1: @@ -484,17 +571,17 @@ class ImageFileDirectory: # pass 2: write directory to file for tag, typ, count, value, data in directory: if Image.DEBUG > 1: - print tag, typ, count, repr(value), repr(data) + print(tag, typ, count, repr(value), repr(data)) fp.write(o16(tag) + o16(typ) + o32(count) + value) # -- overwrite here for multi-page -- - fp.write("\0\0\0\0") # end of directory + fp.write(b"\0\0\0\0") # end of directory # pass 3: write auxiliary data to file for tag, typ, count, value, data in directory: fp.write(data) if len(data) & 1: - fp.write("\0") + fp.write(b"\0") return offset @@ -513,7 +600,7 @@ class TiffImageFile(ImageFile.ImageFile): ifh = self.fp.read(8) if ifh[:4] not in PREFIXES: - raise SyntaxError, "not a TIFF file" + raise SyntaxError("not a TIFF file") # image file directory (tag dictionary) self.tag = self.ifd = ImageFileDirectory(ifh[:2]) @@ -523,7 +610,12 @@ class TiffImageFile(ImageFile.ImageFile): self.__frame = -1 self.__fp = self.fp - # and load the first frame + if Image.DEBUG: + print ("*** TiffImageFile._open ***") + print ("- __first:", self.__first) + print ("- ifh: ", ifh) + + # and load the first frame self._seek(0) def seek(self, frame): @@ -547,7 +639,7 @@ class TiffImageFile(ImageFile.ImageFile): self.__next = self.__first while self.__frame < frame: if not self.__next: - raise EOFError, "no more images in TIFF file" + raise EOFError("no more images in TIFF file") self.fp.seek(self.__next) self.tag.load(self.fp) self.__next = self.tag.next @@ -558,7 +650,7 @@ class TiffImageFile(ImageFile.ImageFile): return self.__frame - def _decoder(self, rawmode, layer): + def _decoder(self, rawmode, layer, tile=None): "Setup decoder contexts" args = None @@ -569,27 +661,88 @@ class TiffImageFile(ImageFile.ImageFile): args = (rawmode, 0, 1) elif compression == "jpeg": args = rawmode, "" - if self.tag.has_key(JPEGTABLES): + if JPEGTABLES in self.tag: # Hack to handle abbreviated JPEG headers self.tile_prefix = self.tag[JPEGTABLES] elif compression == "packbits": args = rawmode elif compression == "tiff_lzw": args = rawmode - if self.tag.has_key(317): + if 317 in self.tag: # Section 14: Differencing Predictor self.decoderconfig = (self.tag[PREDICTOR][0],) - if self.tag.has_key(ICCPROFILE): + if ICCPROFILE in self.tag: self.info['icc_profile'] = self.tag[ICCPROFILE] return args + def _load_libtiff(self): + """ Overload method triggered when we detect a compressed tiff + Calls out to libtiff """ + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.load_prepare() + + if not len(self.tile) == 1: + raise IOError("Not exactly one tile") + + d, e, o, a = self.tile[0] + d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig) + try: + d.setimage(self.im, e) + except ValueError: + raise IOError("Couldn't set the image") + + if hasattr(self.fp, "getvalue"): + # We've got a stringio like thing passed in. Yay for all in memory. + # The decoder needs the entire file in one shot, so there's not + # a lot we can do here other than give it the entire file. + # unless we could do something like get the address of the underlying + # string for stringio. + # + # Rearranging for supporting byteio items, since they have a fileno + # that returns an IOError if there's no underlying fp. Easier to deal + # with here by reordering. + if Image.DEBUG: + print ("have getvalue. just sending in a string from getvalue") + n,e = d.decode(self.fp.getvalue()) + elif hasattr(self.fp, "fileno"): + # we've got a actual file on disk, pass in the fp. + if Image.DEBUG: + print ("have fileno, calling fileno version of the decoder.") + self.fp.seek(0) + n,e = d.decode(b"fpfp") # 4 bytes, otherwise the trace might error out + else: + # we have something else. + if Image.DEBUG: + print ("don't have fileno or getvalue. just reading") + # UNDONE -- so much for that buffer size thing. + n, e = d.decode(self.fp.read()) + + + self.tile = [] + self.readonly = 0 + self.fp = None # might be shared + + if e < 0: + raise IOError(e) + + self.load_end() + + return Image.Image.load(self) + def _setup(self): "Setup this image object based on current tags" - if self.tag.has_key(0xBC01): - raise IOError, "Windows Media Photo files not yet supported" + if 0xBC01 in self.tag: + raise IOError("Windows Media Photo files not yet supported") getscalar = self.tag.getscalar @@ -604,11 +757,11 @@ class TiffImageFile(ImageFile.ImageFile): fillorder = getscalar(FILLORDER, 1) if Image.DEBUG: - print "*** Summary ***" - print "- compression:", self._compression - print "- photometric_interpretation:", photo - print "- planar_configuration:", self._planar_configuration - print "- fill_order:", fillorder + print("*** Summary ***") + print("- compression:", self._compression) + print("- photometric_interpretation:", photo) + print("- planar_configuration:", self._planar_configuration) + print("- fill_order:", fillorder) # size xsize = getscalar(IMAGEWIDTH) @@ -616,7 +769,7 @@ class TiffImageFile(ImageFile.ImageFile): self.size = xsize, ysize if Image.DEBUG: - print "- size:", self.size + print("- size:", self.size) format = getscalar(SAMPLEFORMAT, 1) @@ -627,23 +780,27 @@ class TiffImageFile(ImageFile.ImageFile): self.tag.get(EXTRASAMPLES, ()) ) if Image.DEBUG: - print "format key:", key + print("format key:", key) try: self.mode, rawmode = OPEN_INFO[key] except KeyError: if Image.DEBUG: - print "- unsupported format" - raise SyntaxError, "unknown pixel mode" + print("- unsupported format") + raise SyntaxError("unknown pixel mode") if Image.DEBUG: - print "- raw mode:", rawmode - print "- pil mode:", self.mode + print("- raw mode:", rawmode) + print("- pil mode:", self.mode) self.info["compression"] = self._compression xres = getscalar(X_RESOLUTION, (1, 1)) yres = getscalar(Y_RESOLUTION, (1, 1)) + if xres and not isinstance(xres, tuple): + xres = (xres, 1.) + if yres and not isinstance(yres, tuple): + yres = (yres, 1.) if xres and yres: xres = xres[0] / (xres[1] or 1) yres = yres[0] / (yres[1] or 1) @@ -658,24 +815,89 @@ class TiffImageFile(ImageFile.ImageFile): # build tile descriptors x = y = l = 0 self.tile = [] - if self.tag.has_key(STRIPOFFSETS): + if STRIPOFFSETS in self.tag: # striped image + offsets = self.tag[STRIPOFFSETS] h = getscalar(ROWSPERSTRIP, ysize) w = self.size[0] - a = None - for o in self.tag[STRIPOFFSETS]: - if not a: - a = self._decoder(rawmode, l) + if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4", + "tiff_jpeg", "tiff_adobe_deflate", + "tiff_thunderscan", "tiff_deflate", + "tiff_sgilog", "tiff_sgilog24", + "tiff_raw_16"]: + ## if Image.DEBUG: + ## print "Activating g4 compression for whole file" + + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # replace the existing load function with our + # _load_libtiff function. + + self.load = self._load_libtiff + + # To be nice on memory footprint, if there's a + # file descriptor, use that instead of reading + # into a string in python. + + # libtiff closes the file descriptor, so pass in a dup. + try: + fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno()) + except IOError: + # io.BytesIO have a fileno, but returns an IOError if + # it doesn't use a file descriptor. + fp = False + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-imaging/Pillow/issues/279 + if fillorder == 2: + key = ( + self.tag.prefix, photo, format, 1, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print("format key:", key) + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode: + rawmode = 'I;16N' + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, fp ) self.tile.append( (self._compression, - (0, min(y, ysize), w, min(y+h, ysize)), - o, a)) - y = y + h - if y >= self.size[1]: - x = y = 0 - l = l + 1 + (0, 0, w, ysize), + 0, a)) + a = None + + else: + for i in range(len(offsets)): + a = self._decoder(rawmode, l, i) + self.tile.append( + (self._compression, + (0, min(y, ysize), w, min(y+h, ysize)), + offsets[i], a)) + if Image.DEBUG: + print ("tiles: ", self.tile) + y = y + h + if y >= self.size[1]: + x = y = 0 + l = l + 1 a = None - elif self.tag.has_key(TILEOFFSETS): + elif TILEOFFSETS in self.tag: # tiled image w = getscalar(322) h = getscalar(323) @@ -698,14 +920,14 @@ class TiffImageFile(ImageFile.ImageFile): a = None else: if Image.DEBUG: - print "- unsupported data organization" + print("- unsupported data organization") raise SyntaxError("unknown data organization") # fixup palette descriptor if self.mode == "P": - palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) - self.palette = ImagePalette.raw("RGB;L", string.join(palette, "")) + palette = [o8(a // 256) for a in self.tag[COLORMAP]] + self.palette = ImagePalette.raw("RGB;L", b"".join(palette)) # # -------------------------------------------------------------------- # Write TIFF files @@ -738,10 +960,10 @@ SAVE_INFO = { def _cvt_res(value): # convert value to TIFF rational number -- (numerator, denominator) - if type(value) in (type([]), type(())): + if isinstance(value, collections.Sequence): assert(len(value) % 2 == 0) return value - if type(value) == type(1): + if isinstance(value, int): return (value, 1) value = float(value) return (int(value * 65536), 65536) @@ -751,12 +973,23 @@ def _save(im, fp, filename): try: rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] except KeyError: - raise IOError, "cannot write mode %s as TIFF" % im.mode + raise IOError("cannot write mode %s as TIFF" % im.mode) ifd = ImageFileDirectory(prefix) + compression = im.encoderinfo.get('compression',im.info.get('compression','raw')) + + libtiff = WRITE_LIBTIFF or compression in ["tiff_ccitt", "group3", "group4", + "tiff_jpeg", "tiff_adobe_deflate", + "tiff_thunderscan", "tiff_deflate", + "tiff_sgilog", "tiff_sgilog24", + "tiff_raw_16"] + + # required for color libtiff images + ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1) + # -- multi-page -- skip TIFF header on subsequent pages - if fp.tell() == 0: + if not libtiff and fp.tell() == 0: # tiff header (write via IFD to get everything right) # PIL always starts the first IFD at offset 8 fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) @@ -764,33 +997,44 @@ def _save(im, fp, filename): ifd[IMAGEWIDTH] = im.size[0] ifd[IMAGELENGTH] = im.size[1] + # write any arbitrary tags passed in as an ImageFileDirectory + info = im.encoderinfo.get("tiffinfo",{}) + if Image.DEBUG: + print ("Tiffinfo Keys: %s"% info.keys) + keys = list(info.keys()) + for key in keys: + ifd[key] = info.get(key) + try: + ifd.tagtype[key] = info.tagtype[key] + except: + pass # might not be an IFD, Might not have populated type + + # additions written by Greg Couch, gregc@cgl.ucsf.edu # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com if hasattr(im, 'tag'): # preserve tags from original TIFF image file - for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): - if im.tag.tagdata.has_key(key): - ifd[key] = im.tag.tagdata.get(key) - # preserve some more tags from original TIFF image file - # -- 2008-06-06 Florian Hoech - ifd.tagtype = im.tag.tagtype - for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): - if im.tag.has_key(key): + for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION, + IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): + if key in im.tag: ifd[key] = im.tag[key] + ifd.tagtype[key] = im.tag.tagtype.get(key, None) + # preserve ICC profile (should also work when saving other formats # which support profiles as TIFF) -- 2008-06-06 Florian Hoech - if im.info.has_key("icc_profile"): + if "icc_profile" in im.info: ifd[ICCPROFILE] = im.info["icc_profile"] - if im.encoderinfo.has_key("description"): + + if "description" in im.encoderinfo: ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] - if im.encoderinfo.has_key("resolution"): + if "resolution" in im.encoderinfo: ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ = _cvt_res(im.encoderinfo["resolution"]) - if im.encoderinfo.has_key("x resolution"): + if "x resolution" in im.encoderinfo: ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) - if im.encoderinfo.has_key("y resolution"): + if "y resolution" in im.encoderinfo: ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) - if im.encoderinfo.has_key("resolution unit"): + if "resolution unit" in im.encoderinfo: unit = im.encoderinfo["resolution unit"] if unit == "inch": ifd[RESOLUTION_UNIT] = 2 @@ -798,13 +1042,13 @@ def _save(im, fp, filename): ifd[RESOLUTION_UNIT] = 3 else: ifd[RESOLUTION_UNIT] = 1 - if im.encoderinfo.has_key("software"): + if "software" in im.encoderinfo: ifd[SOFTWARE] = im.encoderinfo["software"] - if im.encoderinfo.has_key("date time"): + if "date time" in im.encoderinfo: ifd[DATE_TIME] = im.encoderinfo["date time"] - if im.encoderinfo.has_key("artist"): + if "artist" in im.encoderinfo: ifd[ARTIST] = im.encoderinfo["artist"] - if im.encoderinfo.has_key("copyright"): + if "copyright" in im.encoderinfo: ifd[COPYRIGHT] = im.encoderinfo["copyright"] dpi = im.encoderinfo.get("dpi") @@ -826,24 +1070,96 @@ def _save(im, fp, filename): if im.mode == "P": lut = im.im.getpalette("RGB", "RGB;L") - ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut)) + ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut) # data orientation - stride = len(bits) * ((im.size[0]*bits[0]+7)/8) + stride = len(bits) * ((im.size[0]*bits[0]+7)//8) ifd[ROWSPERSTRIP] = im.size[1] ifd[STRIPBYTECOUNTS] = stride * im.size[1] ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer - ifd[COMPRESSION] = 1 # no compression + ifd[COMPRESSION] = COMPRESSION_INFO_REV.get(compression,1) # no compression by default - offset = ifd.save(fp) + if libtiff: + if Image.DEBUG: + print ("Saving using libtiff encoder") + print (ifd.items()) + _fp = 0 + if hasattr(fp, "fileno"): + fp.seek(0) + _fp = os.dup(fp.fileno()) - ImageFile._save(im, fp, [ - ("raw", (0,0)+im.size, offset, (rawmode, stride, 1)) - ]) + blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ROWSPERSTRIP, ICCPROFILE] # ICC Profile crashes. + atts={} + # bits per sample is a single short in the tiff directory, not a list. + atts[BITSPERSAMPLE] = bits[0] + # Merge the ones that we have with (optional) more bits from + # the original file, e.g x,y resolution so that we can + # save(load('')) == original file. + for k,v in itertools.chain(ifd.items(), getattr(im, 'ifd', {}).items()): + if k not in atts and k not in blocklist: + if type(v[0]) == tuple and len(v) > 1: + # A tuple of more than one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = [float(elt[0])/float(elt[1]) for elt in v] + continue + if type(v[0]) == tuple and len(v) == 1: + # A tuple of one rational tuples + # flatten to floats, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0][0])/float(v[0][1]) + continue + if type(v) == tuple and len(v) > 2: + # List of ints? + if type(v[0]) in (int, float): + atts[k] = list(v) + continue + if type(v) == tuple and len(v) == 2: + # one rational tuple + # flatten to float, following tiffcp.c->cpTag->TIFF_RATIONAL + atts[k] = float(v[0])/float(v[1]) + continue + if type(v) == tuple and len(v) == 1: + v = v[0] + # drop through + if isStringType(v): + atts[k] = bytes(v.encode('ascii', 'replace')) + b"\0" + continue + else: + # int or similar + atts[k] = v + + if Image.DEBUG: + print (atts) + + # libtiff always expects the bytes in native order. + # we're storing image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if im.mode in ('I;16B', 'I;16'): + rawmode = 'I;16N' + + a = (rawmode, compression, _fp, filename, atts) + # print (im.mode, compression, a, im.encoderconfig) + e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig) + e.setimage(im.im, (0,0)+im.size) + while 1: + l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock + if not _fp: + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + + else: + offset = ifd.save(fp) + + ImageFile._save(im, fp, [ + ("raw", (0,0)+im.size, offset, (rawmode, stride, 1)) + ]) # -- helper for multi-page save -- - if im.encoderinfo.has_key("_debug_multipage"): + if "_debug_multipage" in im.encoderinfo: #just to access o32 and o16 (using correct byte order) im._debug_multipage = ifd diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py index 33fd20941..9d4530051 100644 --- a/PIL/TiffTags.py +++ b/PIL/TiffTags.py @@ -186,6 +186,10 @@ TAGS = { 50738: "AntiAliasStrength", 50740: "DNGPrivateData", 50741: "MakerNoteSafety", + + #ImageJ + 50838: "ImageJMetaDataByteCounts", # private tag registered with Adobe + 50839: "ImageJMetaData", # private tag registered with Adobe } ## diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py index fb8c38ab4..a962e8a99 100644 --- a/PIL/WalImageFile.py +++ b/PIL/WalImageFile.py @@ -21,10 +21,17 @@ # http://www.flipcode.com/tutorials/tut_q2levels.shtml # and has been tested with a few sample files found using google. -import Image +from __future__ import print_function -def i32(c, o=0): - return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) +from PIL import Image, _binary + +try: + import builtins +except ImportError: + import __builtin__ + builtins = __builtin__ + +i32 = _binary.i32le ## # Load texture from a Quake2 WAL texture file. @@ -42,8 +49,7 @@ def open(filename): if hasattr(filename, "read"): fp = filename else: - import __builtin__ - fp = __builtin__.open(filename, "rb") + fp = builtins.open(filename, "rb") # read header fields header = fp.read(32+24+32+12) @@ -53,15 +59,15 @@ def open(filename): # load pixel data fp.seek(offset) - im = Image.fromstring("P", size, fp.read(size[0] * size[1])) + im = Image.frombytes("P", size, fp.read(size[0] * size[1])) im.putpalette(quake2palette) im.format = "WAL" im.format_description = "Quake2 Texture" # strings are null-terminated - im.info["name"] = header[:32].split("\0", 1)[0] - next_name = header[56:56+32].split("\0", 1)[0] + im.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56:56+32].split(b"\0", 1)[0] if next_name: im.info["next_name"] = next_name @@ -70,57 +76,57 @@ def open(filename): quake2palette = ( # default palette taken from piffo 0.93 by Hans Hggstrm - "\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" - "\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" - "\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" - "\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" - "\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" - "\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" - "\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" - "\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" - "\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" - "\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" - "\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" - "\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" - "\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" - "\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" - "\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" - "\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" - "\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" - "\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" - "\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" - "\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" - "\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" - "\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" - "\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" - "\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" - "\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" - "\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" - "\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" - "\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" - "\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" - "\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" - "\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" - "\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" - "\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" - "\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" - "\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" - "\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" - "\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" - "\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" - "\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" - "\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" - "\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" - "\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" - "\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" - "\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" - "\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" - "\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" - "\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" - "\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" + b"\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + b"\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + b"\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + b"\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + b"\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + b"\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + b"\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + b"\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + b"\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + b"\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + b"\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + b"\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + b"\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + b"\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + b"\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + b"\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + b"\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + b"\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + b"\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + b"\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + b"\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + b"\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + b"\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + b"\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + b"\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + b"\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + b"\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + b"\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + b"\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + b"\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + b"\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + b"\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + b"\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + b"\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + b"\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + b"\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + b"\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + b"\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + b"\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + b"\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + b"\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + b"\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + b"\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + b"\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + b"\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + b"\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + b"\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + b"\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" ) if __name__ == "__main__": im = open("../hacks/sample.wal") - print im.info, im.mode, im.size + print(im.info, im.mode, im.size) im.save("../out.png") diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py new file mode 100644 index 000000000..ab60c8dfa --- /dev/null +++ b/PIL/WebPImagePlugin.py @@ -0,0 +1,79 @@ +from PIL import Image +from PIL import ImageFile +from io import BytesIO +from PIL import _webp + + +_VALID_WEBP_MODES = { + "RGB": True, + "RGBA": True, + } + +_VP8_MODES_BY_IDENTIFIER = { + b"VP8 ": "RGB", + b"VP8X": "RGBA", + b"VP8L": "RGBA", # lossless + } + + +def _accept(prefix): + is_riff_file_format = prefix[:4] == b"RIFF" + is_webp_file = prefix[8:12] == b"WEBP" + is_valid_vp8_mode = prefix[12:16] in _VP8_MODES_BY_IDENTIFIER + + return is_riff_file_format and is_webp_file and is_valid_vp8_mode + + +class WebPImageFile(ImageFile.ImageFile): + + format = "WEBP" + format_description = "WebP image" + + def _open(self): + data, width, height, self.mode, icc_profile, exif = _webp.WebPDecode(self.fp.read()) + + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + + self.size = width, height + self.fp = BytesIO(data) + self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] + + def _getexif(self): + from PIL.JpegImagePlugin import _getexif + return _getexif(self) + + +def _save(im, fp, filename): + image_mode = im.mode + if im.mode not in _VALID_WEBP_MODES: + raise IOError("cannot write mode %s as WEBP" % image_mode) + + lossless = im.encoderinfo.get("lossless", False) + quality = im.encoderinfo.get("quality", 80) + icc_profile = im.encoderinfo.get("icc_profile", "") + exif = im.encoderinfo.get("exif", "") + + data = _webp.WebPEncode( + im.tobytes(), + im.size[0], + im.size[1], + lossless, + float(quality), + im.mode, + icc_profile, + exif + ) + if data is None: + raise IOError("cannot write file as WEBP (encoder returned None)") + + fp.write(data) + + +Image.register_open("WEBP", WebPImageFile, _accept) +Image.register_save("WEBP", _save) + +Image.register_extension("WEBP", ".webp") +Image.register_mime("WEBP", "image/webp") diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py index 5191f1e51..9a95a0713 100644 --- a/PIL/WmfImagePlugin.py +++ b/PIL/WmfImagePlugin.py @@ -17,10 +17,13 @@ __version__ = "0.2" -import Image, ImageFile +from PIL import Image, ImageFile, _binary _handler = None +if str != bytes: + long = int + ## # Install application-specific WMF image handler. # @@ -41,7 +44,7 @@ if hasattr(Image.core, "drawwmf"): def load(self, im): im.fp.seek(0) # rewind - return Image.fromstring( + return Image.frombytes( "RGB", im.size, Image.core.drawwmf(im.fp.read(), im.size, self.bbox), "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 @@ -51,20 +54,15 @@ if hasattr(Image.core, "drawwmf"): # -------------------------------------------------------------------- -def word(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) +word = _binary.i16le def short(c, o=0): - v = ord(c[o]) + (ord(c[o+1])<<8) + v = word(c, o) if v >= 32768: v = v - 65536 return v -def dword(c, o=0): - return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) - -def long(c, o=0): - return dword(c, o) +dword = _binary.i32le # # -------------------------------------------------------------------- @@ -72,8 +70,8 @@ def long(c, o=0): def _accept(prefix): return ( - prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or - prefix[:4] == "\x01\x00\x00\x00" + prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == b"\x01\x00\x00\x00" ) ## @@ -89,7 +87,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): # check placable header s = self.fp.read(80) - if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00": + if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": # placeable windows metafile @@ -101,7 +99,7 @@ class WmfStubImageFile(ImageFile.StubImageFile): x1 = short(s, 10); y1 = short(s, 12) # normalize size to 72 dots per inch - size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch + size = (x1 - x0) * 72 // inch, (y1 - y0) * 72 // inch self.info["wmf_bbox"] = x0, y0, x1, y1 @@ -110,25 +108,25 @@ class WmfStubImageFile(ImageFile.StubImageFile): # print self.mode, self.size, self.info # sanity check (standard metafile header) - if s[22:26] != "\x01\x00\t\x00": + if s[22:26] != b"\x01\x00\t\x00": raise SyntaxError("Unsupported WMF file format") - elif long(s) == 1 and s[40:44] == " EMF": + elif dword(s) == 1 and s[40:44] == b" EMF": # enhanced metafile # get bounding box - x0 = long(s, 8); y0 = long(s, 12) - x1 = long(s, 16); y1 = long(s, 20) + x0 = dword(s, 8); y0 = dword(s, 12) + x1 = dword(s, 16); y1 = dword(s, 20) # get frame (in 0.01 millimeter units) - frame = long(s, 24), long(s, 28), long(s, 32), long(s, 36) + frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36) # normalize size to 72 dots per inch size = x1 - x0, y1 - y0 # calculate dots per inch from bbox and frame - xdpi = 2540 * (x1 - y0) / (frame[2] - frame[0]) - ydpi = 2540 * (y1 - y0) / (frame[3] - frame[1]) + xdpi = 2540 * (x1 - y0) // (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) // (frame[3] - frame[1]) self.info["wmf_bbox"] = x0, y0, x1, y1 diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py index ab1fb5d7a..e5bf55acf 100644 --- a/PIL/XVThumbImagePlugin.py +++ b/PIL/XVThumbImagePlugin.py @@ -19,15 +19,16 @@ __version__ = "0.1" -import string -import Image, ImageFile, ImagePalette +from PIL import Image, ImageFile, ImagePalette, _binary + +o8 = _binary.o8 # standard color palette for thumbnails (RGB332) -PALETTE = "" +PALETTE = b"" for r in range(8): for g in range(8): for b in range(4): - PALETTE = PALETTE + (chr((r*255)/7)+chr((g*255)/7)+chr((b*255)/3)) + PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3)) ## # Image plugin for XV thumbnail images. @@ -41,25 +42,25 @@ class XVThumbImageFile(ImageFile.ImageFile): # check magic s = self.fp.read(6) - if s != "P7 332": - raise SyntaxError, "not an XV thumbnail file" + if s != b"P7 332": + raise SyntaxError("not an XV thumbnail file") # Skip to beginning of next line self.fp.readline() # skip info comments - while 1: + while True: s = self.fp.readline() if not s: - raise SyntaxError, "Unexpected EOF reading XV thumbnail file" - if s[0] != '#': + raise SyntaxError("Unexpected EOF reading XV thumbnail file") + if s[0] != b'#': break # parse header line (already read) - s = string.split(s.strip()) + s = s.strip().split() self.mode = "P" - self.size = int(s[0]), int(s[1]) + self.size = int(s[0:1]), int(s[1:2]) self.palette = ImagePalette.raw("RGB", PALETTE) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py index a8cf1026d..799d727a0 100644 --- a/PIL/XbmImagePlugin.py +++ b/PIL/XbmImagePlugin.py @@ -21,22 +21,22 @@ __version__ = "0.6" -import re, string -import Image, ImageFile +import re +from PIL import Image, ImageFile # XBM header xbm_head = re.compile( - "\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" - "(?P" - "#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - "#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - ")?" - "[\\000-\\377]*_bits\\[\\]" + b"\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + b"(?P" + b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + b")?" + b"[\\000-\\377]*_bits\\[\\]" ) def _accept(prefix): - return string.lstrip(prefix)[:7] == "#define" + return prefix.lstrip()[:7] == b"#define" ## # Image plugin for X11 bitmaps. @@ -69,21 +69,21 @@ class XbmImageFile(ImageFile.ImageFile): def _save(im, fp, filename): if im.mode != "1": - raise IOError, "cannot write mode %s as XBM" % im.mode + raise IOError("cannot write mode %s as XBM" % im.mode) - fp.write("#define im_width %d\n" % im.size[0]) - fp.write("#define im_height %d\n" % im.size[1]) + fp.write(("#define im_width %d\n" % im.size[0]).encode('ascii')) + fp.write(("#define im_height %d\n" % im.size[1]).encode('ascii')) hotspot = im.encoderinfo.get("hotspot") if hotspot: - fp.write("#define im_x_hot %d\n" % hotspot[0]) - fp.write("#define im_y_hot %d\n" % hotspot[1]) + fp.write(("#define im_x_hot %d\n" % hotspot[0]).encode('ascii')) + fp.write(("#define im_y_hot %d\n" % hotspot[1]).encode('ascii')) - fp.write("static char im_bits[] = {\n") + fp.write(b"static char im_bits[] = {\n") ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)]) - fp.write("};\n") + fp.write(b"};\n") Image.register_open("XBM", XbmImageFile, _accept) diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py index a3f40f02c..701a23b64 100644 --- a/PIL/XpmImagePlugin.py +++ b/PIL/XpmImagePlugin.py @@ -18,15 +18,16 @@ __version__ = "0.2" -import re, string -import Image, ImageFile, ImagePalette +import re +from PIL import Image, ImageFile, ImagePalette +from PIL._binary import i8, o8 # XPM header -xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") +xpm_head = re.compile(b"\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") def _accept(prefix): - return prefix[:9] == "/* XPM */" + return prefix[:9] == b"/* XPM */" ## # Image plugin for X11 pixel maps. @@ -39,13 +40,13 @@ class XpmImageFile(ImageFile.ImageFile): def _open(self): if not _accept(self.fp.read(9)): - raise SyntaxError, "not an XPM file" + raise SyntaxError("not an XPM file") # skip forward to next string - while 1: + while True: s = self.fp.readline() if not s: - raise SyntaxError, "broken XPM file" + raise SyntaxError("broken XPM file") m = xpm_head.match(s) if m: break @@ -56,50 +57,50 @@ class XpmImageFile(ImageFile.ImageFile): bpp = int(m.group(4)) if pal > 256 or bpp != 1: - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") # # load palette description - palette = ["\0\0\0"] * 256 + palette = [b"\0\0\0"] * 256 for i in range(pal): s = self.fp.readline() - if s[-2:] == '\r\n': + if s[-2:] == b'\r\n': s = s[:-2] - elif s[-1:] in '\r\n': + elif s[-1:] in b'\r\n': s = s[:-1] - c = ord(s[1]) - s = string.split(s[2:-2]) + c = i8(s[1]) + s = s[2:-2].split() for i in range(0, len(s), 2): - if s[i] == "c": + if s[i] == b"c": # process colour key rgb = s[i+1] - if rgb == "None": + if rgb == b"None": self.info["transparency"] = c - elif rgb[0] == "#": + elif rgb[0:1] == b"#": # FIXME: handle colour names (see ImagePalette.py) - rgb = string.atoi(rgb[1:], 16) - palette[c] = chr((rgb >> 16) & 255) +\ - chr((rgb >> 8) & 255) +\ - chr(rgb & 255) + rgb = int(rgb[1:], 16) + palette[c] = o8((rgb >> 16) & 255) +\ + o8((rgb >> 8) & 255) +\ + o8(rgb & 255) else: # unknown colour - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") break else: # missing colour key - raise ValueError, "cannot read this XPM file" + raise ValueError("cannot read this XPM file") self.mode = "P" - self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + self.palette = ImagePalette.raw("RGB", b"".join(palette)) self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] @@ -113,11 +114,11 @@ class XpmImageFile(ImageFile.ImageFile): s = [None] * ysize for i in range(ysize): - s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize) + s[i] = self.fp.readline()[1:xsize+1].ljust(xsize) self.fp = None - return string.join(s, "") + return b"".join(s) # # Registry diff --git a/PIL/__init__.py b/PIL/__init__.py index ed54d26f6..18bd42a5f 100644 --- a/PIL/__init__.py +++ b/PIL/__init__.py @@ -10,3 +10,48 @@ # # ;-) + +VERSION = '1.1.7' # PIL version +PILLOW_VERSION = '2.3.0' # Pillow + +_plugins = ['ArgImagePlugin', + 'BmpImagePlugin', + 'BufrStubImagePlugin', + 'CurImagePlugin', + 'DcxImagePlugin', + 'EpsImagePlugin', + 'FitsStubImagePlugin', + 'FliImagePlugin', + 'FpxImagePlugin', + 'GbrImagePlugin', + 'GifImagePlugin', + 'GribStubImagePlugin', + 'Hdf5StubImagePlugin', + 'IcnsImagePlugin', + 'IcoImagePlugin', + 'ImImagePlugin', + 'ImtImagePlugin', + 'IptcImagePlugin', + 'JpegImagePlugin', + 'McIdasImagePlugin', + 'MicImagePlugin', + 'MpegImagePlugin', + 'MspImagePlugin', + 'PalmImagePlugin', + 'PcdImagePlugin', + 'PcxImagePlugin', + 'PdfImagePlugin', + 'PixarImagePlugin', + 'PngImagePlugin', + 'PpmImagePlugin', + 'PsdImagePlugin', + 'SgiImagePlugin', + 'SpiderImagePlugin', + 'SunImagePlugin', + 'TgaImagePlugin', + 'TiffImagePlugin', + 'WebPImagePlugin', + 'WmfImagePlugin', + 'XbmImagePlugin', + 'XpmImagePlugin', + 'XVThumbImagePlugin'] diff --git a/PIL/_binary.py b/PIL/_binary.py new file mode 100644 index 000000000..71b2b78c9 --- /dev/null +++ b/PIL/_binary.py @@ -0,0 +1,65 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Binary input/output support routines. +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# Copyright (c) 2012 by Brian Crowell +# +# See the README file for information on usage and redistribution. +# + +if bytes is str: + def i8(c): + return ord(c) + + def o8(i): + return chr(i&255) +else: + def i8(c): + return c if c.__class__ is int else c[0] + + def o8(i): + return bytes((i&255,)) + +# Input, le = little endian, be = big endian +#TODO: replace with more readable struct.unpack equivalent +def i16le(c, o=0): + """ + Converts a 2-bytes (16 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1])<<8) + +def i32le(c, o=0): + """ + Converts a 4-bytes (32 bits) string to an integer. + + c: string containing bytes to convert + o: offset of bytes to convert in string + """ + return i8(c[o]) | (i8(c[o+1])<<8) | (i8(c[o+2])<<16) | (i8(c[o+3])<<24) + +def i16be(c, o=0): + return (i8(c[o])<<8) | i8(c[o+1]) + +def i32be(c, o=0): + return (i8(c[o])<<24) | (i8(c[o+1])<<16) | (i8(c[o+2])<<8) | i8(c[o+3]) + +# Output, le = little endian, be = big endian +def o16le(i): + return o8(i) + o8(i>>8) + +def o32le(i): + return o8(i) + o8(i>>8) + o8(i>>16) + o8(i>>24) + +def o16be(i): + return o8(i>>8) + o8(i) + +def o32be(i): + return o8(i>>24) + o8(i>>16) + o8(i>>8) + o8(i) + diff --git a/PIL/_util.py b/PIL/_util.py new file mode 100644 index 000000000..220ac6c52 --- /dev/null +++ b/PIL/_util.py @@ -0,0 +1,16 @@ +import os + +if bytes is str: + def isStringType(t): + return isinstance(t, basestring) + def isPath(f): + return isinstance(f, basestring) +else: + def isStringType(t): + return isinstance(t, str) + def isPath(f): + return isinstance(f, (bytes, str)) + +# Checks if an object is a string, and that it points to a directory. +def isDirectory(f): + return isPath(f) and os.path.isdir(f) diff --git a/PIL/tests.py b/PIL/tests.py new file mode 100644 index 000000000..eb4a8342d --- /dev/null +++ b/PIL/tests.py @@ -0,0 +1,17 @@ +import unittest + + +class PillowTests(unittest.TestCase): + """ + Can we start moving the test suite here? + """ + + def test_suite_should_move_here(self): + """ + Great idea! + """ + assert True is True + + +if __name__ == '__main__': + unittest.main() diff --git a/PKG-INFO b/PKG-INFO deleted file mode 100644 index c27993fb2..000000000 --- a/PKG-INFO +++ /dev/null @@ -1,18 +0,0 @@ -Metadata-Version: 1.0 -Name: PIL -Version: 1.1.7 -Summary: Python Imaging Library -Home-page: http://www.pythonware.com/products/pil -Author: Secret Labs AB (PythonWare) -Author-email: info@pythonware.com -License: Python (MIT style) -Download-URL: http://effbot.org/downloads/PIL-1.1.7.tar.gz -Description: Python Imaging Library -Platform: Python 1.5.2 and later. -Classifier: Development Status :: 6 - Mature -Classifier: Topic :: Multimedia :: Graphics -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners -Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture -Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion -Classifier: Topic :: Multimedia :: Graphics :: Viewers diff --git a/README b/README deleted file mode 100644 index 458975b3b..000000000 --- a/README +++ /dev/null @@ -1,300 +0,0 @@ -The Python Imaging Library -$Id$ - -Release 1.1.7 (November 15, 2009) - -==================================================================== -The Python Imaging Library 1.1.7 -==================================================================== - -Contents --------- - -+ Introduction -+ Support Options - - Commercial support - - Free support -+ Software License -+ Build instructions (all platforms) - - Additional notes for Mac OS X - - Additional notes for Windows - --------------------------------------------------------------------- -Introduction --------------------------------------------------------------------- - -The Python Imaging Library (PIL) adds image processing capabilities -to your Python environment. This library provides extensive file -format support, an efficient internal representation, and powerful -image processing capabilities. - -This source kit has been built and tested with Python 2.0 and newer, -on Windows, Mac OS X, and major Unix platforms. Large parts of the -library also work on 1.5.2 and 1.6. - -The main distribution site for this software is: - - http://www.pythonware.com/products/pil/ - -That site also contains information about free and commercial support -options, PIL add-ons, answers to frequently asked questions, and more. - - -Development versions (alphas, betas) are available here: - - http://effbot.org/downloads/ - - -The PIL handbook is not included in this distribution; to get the -latest version, check: - - http://www.pythonware.com/library/ - http://effbot.org/books/imagingbook/ (drafts) - - -For installation and licensing details, see below. - - --------------------------------------------------------------------- -Support Options --------------------------------------------------------------------- - -+ Commercial Support - -Secret Labs (PythonWare) offers support contracts for companies using -the Python Imaging Library in commercial applications, and in mission- -critical environments. The support contract includes technical support, -bug fixes, extensions to the PIL library, sample applications, and more. - -For the full story, check: - - http://www.pythonware.com/products/pil/support.htm - - -+ Free Support - -For support and general questions on the Python Imaging Library, send -e-mail to the Image SIG mailing list: - - image-sig@python.org - -You can join the Image SIG by sending a mail to: - - image-sig-request@python.org - -Put "subscribe" in the message body to automatically subscribe to the -list, or "help" to get additional information. Alternatively, you can -send your questions to the Python mailing list, python-list@python.org, -or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT -QUESTIONS TO PYTHONWARE ADDRESSES. - - --------------------------------------------------------------------- -Software License --------------------------------------------------------------------- - -The Python Imaging Library is - -Copyright (c) 1997-2009 by Secret Labs AB -Copyright (c) 1995-2009 by Fredrik Lundh - -By obtaining, using, and/or copying this software and/or its -associated documentation, you agree that you have read, understood, -and will comply with the following terms and conditions: - -Permission to use, copy, modify, and distribute this software and its -associated documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies, and that both that copyright notice and this permission notice -appear in supporting documentation, and that the name of Secret Labs -AB or the author not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR -ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - --------------------------------------------------------------------- -Build instructions (all platforms) --------------------------------------------------------------------- - -For a list of changes in this release, see the CHANGES document. - -0. If you're in a hurry, try this: - - $ tar xvfz Imaging-1.1.7.tar.gz - $ cd Imaging-1.1.7 - $ python setup.py install - - If you prefer to know what you're doing, read on. - - -1. Prerequisites. - - If you need any of the features described below, make sure you - have the necessary libraries before building PIL. - - feature library - ----------------------------------------------------------------- - JPEG support libjpeg (6a or 6b) - - http://www.ijg.org - http://www.ijg.org/files/jpegsrc.v6b.tar.gz - ftp://ftp.uu.net/graphics/jpeg/ - - PNG support zlib (1.2.3 or later is recommended) - - http://www.gzip.org/zlib/ - - OpenType/TrueType freetype2 (2.3.9 or later is recommended) - support - http://www.freetype.org - http://freetype.sourceforge.net - - CMS support littleCMS (1.1.5 or later is recommended) - support - http://www.littlecms.com/ - - If you have a recent Linux version, the libraries provided with the - operating system usually work just fine. If some library is - missing, installing a prebuilt version (jpeg-devel, zlib-devel, - etc) is usually easier than building from source. For example, for - Ubuntu 9.10 (karmic), you can install the following libraries: - - sudo apt-get install libjpeg62-dev - sudo apt-get install zlib1g-dev - sudo apt-get install libfreetype6-dev - sudo apt-get install liblcms1-dev - - If you're using Mac OS X, you can use the 'fink' tool to install - missing libraries (also see the Mac OS X section below). - - Similar tools are available for many other platforms. - - -2. To build under Python 1.5.2, you need to install the stand-alone - version of the distutils library: - - http://www.python.org/sigs/distutils-sig/download.html - - You can fetch distutils 1.0.2 from the Python source repository: - - svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ - - For newer releases, the distutils library is included in the - Python standard library. - - NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some - more recent additions to the library may not work, but the core - functionality is available. - - -3. If you didn't build Python from sources, make sure you have - Python's build support files on your machine. If you've down- - loaded a prebuilt package (e.g. a Linux RPM), you probably - need additional developer packages. Look for packages named - "python-dev", "python-devel", or similar. For example, for - Ubuntu 9.10 (karmic), use the following command: - - sudo apt-get install python-dev - - -4. When you have everything you need, unpack the PIL distribution - (the file Imaging-1.1.7.tar.gz) in a suitable work directory: - - $ cd MyExtensions # example - $ gunzip Imaging-1.1.7.tar.gz - $ tar xvf Imaging-1.1.7.tar - - -5. Build the library. We recommend that you do an in-place build, - and run the self test before installing. - - $ cd Imaging-1.1.7 - $ python setup.py build_ext -i - $ python selftest.py - - During the build process, the setup.py will display a summary - report that lists what external components it found. The self- - test will display a similar report, with what external components - the tests found in the actual build files: - - ---------------------------------------------------------------- - PIL 1.1.7 SETUP SUMMARY - ---------------------------------------------------------------- - *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) - --- JPEG support available - --- ZLIB (PNG/ZIP) support available - --- FREETYPE support available - ---------------------------------------------------------------- - - Make sure that the optional components you need are included. - - If the build script won't find a given component, you can edit the - setup.py file and set the appropriate ROOT variable. For details, - see instructions in the file. - - If the build script finds the component, but the tests cannot - identify it, try rebuilding *all* modules: - - $ python setup.py clean - $ python setup.py build_ext -i - - -6. If the setup.py and selftest.py commands finish without any - errors, you're ready to install the library: - - $ python setup.py install - - (depending on how Python has been installed on your machine, - you might have to log in as a superuser to run the 'install' - command, or use the 'sudo' command to run 'install'.) - - --------------------------------------------------------------------- -Additional notes for Mac OS X --------------------------------------------------------------------- - -On Mac OS X you will usually install additional software such as -libjpeg or freetype with the "fink" tool, and then it ends up in -"/sw". If you have installed the libraries elsewhere, you may have -to tweak the "setup.py" file before building. - - --------------------------------------------------------------------- -Additional notes for Windows --------------------------------------------------------------------- - -On Windows, you need to tweak the ROOT settings in the "setup.py" -file, to make it find the external libraries. See comments in the -file for details. - -Make sure to build PIL and the external libraries with the same -runtime linking options as was used for the Python interpreter -(usually /MD, under Visual Studio). - - -Note that most Python distributions for Windows include libraries -compiled for Microsoft Visual Studio. You can get the free Express -edition of Visual Studio from: - - http://www.microsoft.com/Express/ - -To build extensions using other tool chains, see the "Using -non-Microsoft compilers on Windows" section in the distutils handbook: - - http://www.python.org/doc/current/inst/non-ms-compilers.html - -For additional information on how to build extensions using the -popular MinGW compiler, see: - - http://mingw.org (compiler) - http://sebsauvage.net/python/mingw.html (build instructions) - http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) - diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..78a256c0d --- /dev/null +++ b/README.rst @@ -0,0 +1,19 @@ +Pillow +====== + +*Python Imaging Library (Fork)* + +Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the Python Imaging Library by Fredrik Lundh and Contributors. + +.. image:: https://travis-ci.org/python-imaging/Pillow.png + :target: https://travis-ci.org/python-imaging/Pillow + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +The documentation is hosted at http://pillow.readthedocs.org/. It contains installation instructions, tutorials, reference, compatibility details, and more. diff --git a/README.txt b/README.txt deleted file mode 100644 index 85882da91..000000000 --- a/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -Pillow -====== - -Pillow is a fork of the Python Imaging Library diff --git a/Sane/CHANGES b/Sane/CHANGES index 95c14697e..47fb96cf1 100644 --- a/Sane/CHANGES +++ b/Sane/CHANGES @@ -12,7 +12,7 @@ _sane.c: because it is only set when SANE_ACTION_SET_VALUE is used. - scanDev.get_parameters() now returns the scanner mode as 'format', no more the typical PIL codes. So 'L' became 'gray', 'RGB' is now - 'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches + 'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches the way scanDev.mode is set. This should be the only incompatibility vs. version 1.0. diff --git a/Sane/README b/Sane/README index ddff0cf24..fa6c8a05f 100644 --- a/Sane/README +++ b/Sane/README @@ -3,13 +3,13 @@ Python SANE module V1.1 (30 Sep. 2004) The SANE module provides an interface to the SANE scanner and frame grabber interface for Linux. This module was contributed by Andrew -Kuchling and is extended and currently maintained by Ralph Heinkel +Kuchling and is extended and currently maintained by Ralph Heinkel (rheinkel-at-email.de). If you write to me please make sure to have the word 'SANE' or 'sane' in the subject of your mail, otherwise it might be classified as spam in the future. -To build this module, type (in the Sane directory): +To build this module, type (in the Sane directory): python setup.py build diff --git a/Sane/_sane.c b/Sane/_sane.c index 21e542fa5..1c62610be 100644 --- a/Sane/_sane.c +++ b/Sane/_sane.c @@ -7,10 +7,10 @@ documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of A.M. Kuchling and -Ralph Heinkel not be used in advertising or publicity pertaining to +Ralph Heinkel not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. -A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF @@ -28,6 +28,12 @@ PERFORMANCE OF THIS SOFTWARE. #include +#if PY_MAJOR_VERSION >= 3 + #define PyInt_AsLong PyLong_AsLong + #define PyInt_FromLong PyLong_FromLong + #define PyInt_Check PyLong_Check +#endif + static PyObject *ErrorObject; typedef struct { @@ -40,25 +46,29 @@ PyThreadState *_save; #endif /* Raise a SANE exception */ -PyObject * +PyObject * PySane_Error(SANE_Status st) { const char *string; - + if (st==SANE_STATUS_GOOD) {Py_INCREF(Py_None); return (Py_None);} string=sane_strstatus(st); PyErr_SetString(ErrorObject, string); return NULL; } -staticforward PyTypeObject SaneDev_Type; +static PyTypeObject SaneDev_Type; -#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type) +#define SaneDevObject_Check(v) (Py_TYPE(v) == &SaneDev_Type) static SaneDevObject * newSaneDevObject(void) { SaneDevObject *self; + + if (PyType_Ready(&SaneDev_Type) < 0) + return NULL; + self = PyObject_NEW(SaneDevObject, &SaneDev_Type); if (self == NULL) return NULL; @@ -93,7 +103,7 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args) SANE_Status st; SANE_Parameters p; char *format="unknown format"; - + if (!PyArg_ParseTuple(args, "")) return NULL; if (self->h==NULL) @@ -104,7 +114,7 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args) Py_BEGIN_ALLOW_THREADS st=sane_get_parameters(self->h, &p); Py_END_ALLOW_THREADS - + if (st) return PySane_Error(st); switch (p.format) { @@ -114,8 +124,8 @@ SaneDev_get_parameters(SaneDevObject *self, PyObject *args) case(SANE_FRAME_GREEN): format="green"; break; case(SANE_FRAME_BLUE): format="blue"; break; } - - return Py_BuildValue("si(ii)ii", format, p.last_frame, p.pixels_per_line, + + return Py_BuildValue("si(ii)ii", format, p.last_frame, p.pixels_per_line, p.lines, p.depth, p.bytes_per_line); } @@ -125,7 +135,7 @@ SaneDev_fileno(SaneDevObject *self, PyObject *args) { SANE_Status st; SANE_Int fd; - + if (!PyArg_ParseTuple(args, "")) return NULL; if (self->h==NULL) @@ -142,7 +152,7 @@ static PyObject * SaneDev_start(SaneDevObject *self, PyObject *args) { SANE_Status st; - + if (!PyArg_ParseTuple(args, "")) return NULL; if (self->h==NULL) @@ -184,7 +194,7 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args) const SANE_Option_Descriptor *d; PyObject *list, *value; int i=1; - + if (!PyArg_ParseTuple(args, "")) return NULL; if (self->h==NULL) @@ -195,48 +205,52 @@ SaneDev_get_options(SaneDevObject *self, PyObject *args) if (!(list = PyList_New(0))) return NULL; - do + do { d=sane_get_option_descriptor(self->h, i); - if (d!=NULL) + if (d!=NULL) { PyObject *constraint=NULL; int j; - + switch (d->constraint_type) { - case(SANE_CONSTRAINT_NONE): + case(SANE_CONSTRAINT_NONE): Py_INCREF(Py_None); constraint=Py_None; break; - case(SANE_CONSTRAINT_RANGE): + case(SANE_CONSTRAINT_RANGE): if (d->type == SANE_TYPE_INT) - constraint=Py_BuildValue("iii", d->constraint.range->min, - d->constraint.range->max, + constraint=Py_BuildValue("iii", d->constraint.range->min, + d->constraint.range->max, d->constraint.range->quant); else - constraint=Py_BuildValue("ddd", - SANE_UNFIX(d->constraint.range->min), - SANE_UNFIX(d->constraint.range->max), + constraint=Py_BuildValue("ddd", + SANE_UNFIX(d->constraint.range->min), + SANE_UNFIX(d->constraint.range->max), SANE_UNFIX(d->constraint.range->quant)); break; - case(SANE_CONSTRAINT_WORD_LIST): + case(SANE_CONSTRAINT_WORD_LIST): constraint=PyList_New(d->constraint.word_list[0]); if (d->type == SANE_TYPE_INT) for (j=1; j<=d->constraint.word_list[0]; j++) - PyList_SetItem(constraint, j-1, + PyList_SetItem(constraint, j-1, PyInt_FromLong(d->constraint.word_list[j])); else for (j=1; j<=d->constraint.word_list[0]; j++) - PyList_SetItem(constraint, j-1, + PyList_SetItem(constraint, j-1, PyFloat_FromDouble(SANE_UNFIX(d->constraint.word_list[j]))); break; - case(SANE_CONSTRAINT_STRING_LIST): + case(SANE_CONSTRAINT_STRING_LIST): constraint=PyList_New(0); for(j=0; d->constraint.string_list[j]!=NULL; j++) - PyList_Append(constraint, + PyList_Append(constraint, +#if PY_MAJOR_VERSION >= 3 + PyUnicode_DecodeLatin1(d->constraint.string_list[j], strlen(d->constraint.string_list[j]), NULL)); +#else PyString_FromString(d->constraint.string_list[j])); +#endif break; } - value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, + value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, d->type, d->unit, d->size, d->cap, constraint); PyList_Append(list, value); } @@ -253,7 +267,7 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args) PyObject *value=NULL; int n; void *v; - + if (!PyArg_ParseTuple(args, "i", &n)) { return NULL; @@ -268,12 +282,12 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args) st=sane_control_option(self->h, n, SANE_ACTION_GET_VALUE, v, NULL); - if (st) + if (st) { - free(v); + free(v); return PySane_Error(st); } - + switch(d->type) { case(SANE_TYPE_BOOL): @@ -284,14 +298,18 @@ SaneDev_get_option(SaneDevObject *self, PyObject *args) value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) ); break; case(SANE_TYPE_STRING): +#if PY_MAJOR_VERSION >= 3 + value=PyUnicode_DecodeLatin1((const char *) v, strlen((const char *) v), NULL); +#else value=Py_BuildValue("s", v); +#endif break; case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): value=Py_BuildValue("O", Py_None); break; } - + free(v); return value; } @@ -305,7 +323,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) PyObject *value; int n; void *v; - + if (!PyArg_ParseTuple(args, "iO", &n, &value)) return NULL; if (self->h==NULL) @@ -319,7 +337,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) switch(d->type) { case(SANE_TYPE_BOOL): - if (!PyInt_Check(value)) + if (!PyInt_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_BOOL requires an integer"); free(v); @@ -327,7 +345,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) } /* fall through */ case(SANE_TYPE_INT): - if (!PyInt_Check(value)) + if (!PyInt_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_INT requires an integer"); free(v); @@ -336,7 +354,7 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) *( (SANE_Int*)v) = PyInt_AsLong(value); break; case(SANE_TYPE_FIXED): - if (!PyFloat_Check(value)) + if (!PyFloat_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_FIXED requires a floating point number"); free(v); @@ -345,7 +363,25 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value)); break; case(SANE_TYPE_STRING): - if (!PyString_Check(value)) +#if PY_MAJOR_VERSION >= 3 + if (!PyUnicode_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); + free(v); + return NULL; + } + { + PyObject *encoded = PyUnicode_AsLatin1String(value); + + if (!encoded) + return NULL; + + strncpy(v, PyBytes_AsString(encoded), d->size-1); + ((char*)v)[d->size-1] = 0; + Py_DECREF(encoded); + } +#else + if (!PyString_Check(value)) { PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); free(v); @@ -353,16 +389,17 @@ SaneDev_set_option(SaneDevObject *self, PyObject *args) } strncpy(v, PyString_AsString(value), d->size-1); ((char*)v)[d->size-1] = 0; +#endif break; - case(SANE_TYPE_BUTTON): + case(SANE_TYPE_BUTTON): case(SANE_TYPE_GROUP): break; } - + st=sane_control_option(self->h, n, SANE_ACTION_SET_VALUE, v, &i); if (st) {free(v); return PySane_Error(st);} - + free(v); return Py_BuildValue("i", i); } @@ -371,10 +408,9 @@ static PyObject * SaneDev_set_auto_option(SaneDevObject *self, PyObject *args) { SANE_Status st; - const SANE_Option_Descriptor *d; SANE_Int i; int n; - + if (!PyArg_ParseTuple(args, "i", &n)) return NULL; if (self->h==NULL) @@ -382,11 +418,10 @@ SaneDev_set_auto_option(SaneDevObject *self, PyObject *args) PyErr_SetString(ErrorObject, "SaneDev object is closed"); return NULL; } - d=sane_get_option_descriptor(self->h, n); st=sane_control_option(self->h, n, SANE_ACTION_SET_AUTO, NULL, &i); if (st) {return PySane_Error(st);} - + return Py_BuildValue("i", i); } @@ -395,7 +430,7 @@ SaneDev_set_auto_option(SaneDevObject *self, PyObject *args) static PyObject * SaneDev_snap(SaneDevObject *self, PyObject *args) { - SANE_Status st; + SANE_Status st; /* The buffer should be a multiple of 3 in size, so each sane_read operation will return an integral number of RGB triples. */ SANE_Byte buffer[READSIZE]; /* XXX how big should the buffer be? */ @@ -405,16 +440,16 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) int px, py, remain, cplen, bufpos, padbytes; long L; char errmsg[80]; - union + union { char c[2]; INT16 i16; - } + } endian; PyObject *pyNoCancel = NULL; int noCancel = 0; - + endian.i16 = 1; - + if (!PyArg_ParseTuple(args, "l|O", &L, &pyNoCancel)) return NULL; if (self->h==NULL) @@ -423,7 +458,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) return NULL; } im=(Imaging)L; - + if (pyNoCancel) noCancel = PyObject_IsTrue(pyNoCancel); @@ -435,14 +470,14 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) we need to call sane_get_parameters here, and we can create the result Image object here. */ - + Py_UNBLOCK_THREADS sane_get_parameters(self->h, &p); if (p.format == SANE_FRAME_GRAY) { switch (p.depth) { - case 1: + case 1: remain = p.bytes_per_line * im->ysize; padbytes = p.bytes_per_line - (im->xsize+7)/8; bufpos = 0; @@ -468,7 +503,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) px = 0; } } - st=sane_read(self->h, buffer, + st=sane_read(self->h, buffer, remainh, buffer, + st=sane_read(self->h, buffer, remainh, buffer, + st=sane_read(self->h, buffer, remainysize; padbytes = p.bytes_per_line - ((im->xsize+7)/8) * 3; bufpos = 0; @@ -586,7 +621,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) { while (len <= 0 && st == SANE_STATUS_GOOD) { - st=sane_read(self->h, buffer, + st=sane_read(self->h, buffer, remainxsize; bufpos = endian.c[0]; @@ -654,9 +689,9 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) - we may have padding bytes at the end of a scan line - the number of bytes read with sane_read may be smaller than the number of pad bytes - - the buffer may become empty after setting any of the + - the buffer may become empty after setting any of the red/green/blue pixel values - + */ while (st != SANE_STATUS_EOF && py < im->ysize) { @@ -685,7 +720,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) len -= bufpos; } if (st == SANE_STATUS_EOF) break; - ((UINT8**)(im->image32))[py][px++] = buffer[bufpos]; + ((UINT8**)(im->image32))[py][px++] = buffer[bufpos]; bufpos += incr; len -= incr; } @@ -709,13 +744,13 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) PyErr_SetString(ErrorObject, errmsg); return NULL; } - + } else /* should be SANE_FRAME_RED, GREEN or BLUE */ { int lastlen, pxa, pxmax, offset, incr, frame_count = 0; - /* at least the Sane test backend behaves a bit weird, if - it returns "premature EOF" for sane_read, i.e., if the + /* at least the Sane test backend behaves a bit weird, if + it returns "premature EOF" for sane_read, i.e., if the option "return value of sane_read" is set to SANE_STATUS_EOF. In this case, the test backend does not advance to the next frame, so p.last_frame will never be set... @@ -737,7 +772,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) lastlen = 0; py = 0; switch (p.format) - { + { case SANE_FRAME_RED: offset = 0; break; @@ -771,14 +806,14 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) mask = 0x80; for (bit = 0; bit < 8 && px < pxmax; bit++) { - ((UINT8**)(im->image32))[py][px] + ((UINT8**)(im->image32))[py][px] = val&mask ? 0xFF : 0; ((UINT8**)(im->image32))[py][pxa] = 0; px += 4; pxa += 4; mask = mask >> 1; } - + if (px >= pxmax) { px = offset; @@ -891,9 +926,9 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) sane_cancel(self->h); return PySane_Error(st); } - + st = sane_start(self->h); - if (st) + if (st) { Py_BLOCK_THREADS return PySane_Error(st); @@ -912,7 +947,7 @@ SaneDev_snap(SaneDevObject *self, PyObject *args) Py_BLOCK_THREADS return PySane_Error(st); } - + if (!noCancel) sane_cancel(self->h); Py_BLOCK_THREADS @@ -932,7 +967,7 @@ int NUMARRAY_IMPORTED = 0; static PyObject * SaneDev_arr_snap(SaneDevObject *self, PyObject *args) { - SANE_Status st; + SANE_Status st; SANE_Byte buffer[READSIZE]; SANE_Int len; SANE_Parameters p; @@ -1000,24 +1035,24 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args) PyErr_SetString(ErrorObject, "failed to create NumArray object"); return NULL; } - + arr_bytes_per_line = pixels_per_line * bpp; st=SANE_STATUS_GOOD; #ifdef WRITE_PGM FILE *fp; fp = fopen("sane_p5.pgm", "w"); - fprintf(fp, "P5\n%d %d\n%d\n", p.pixels_per_line, + fprintf(fp, "P5\n%d %d\n%d\n", p.pixels_per_line, p.lines, (int) pow(2.0, (double) p.depth)-1); #endif line_index = line = 0; remain_bytes_line = arr_bytes_per_line; total_remain = p.bytes_per_line * p.lines; num_pad_bytes = p.bytes_per_line - arr_bytes_per_line; - + while (st!=SANE_STATUS_EOF) { Py_BEGIN_ALLOW_THREADS - st = sane_read(self->h, buffer, + st = sane_read(self->h, buffer, READSIZE < total_remain ? READSIZE : total_remain, &len); Py_END_ALLOW_THREADS #ifdef WRITE_PGM @@ -1037,7 +1072,7 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args) len -= cp_num_bytes; #ifdef DEBUG printf("copying %d bytes from b_idx %d to d_idx %d\n", - cp_num_bytes, buffer_index, + cp_num_bytes, buffer_index, line * arr_bytes_per_line + line_index); printf("len is now %d\n", len); #endif @@ -1055,7 +1090,7 @@ SaneDev_arr_snap(SaneDevObject *self, PyObject *args) remain_bytes_line = arr_bytes_per_line; line++; line_index = 0; - /* Skip the number of bytes in the input stream which + /* Skip the number of bytes in the input stream which are not used: */ len -= num_pad_bytes; buffer_index += num_pad_bytes; @@ -1095,29 +1130,38 @@ static PyMethodDef SaneDev_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject * -SaneDev_getattr(SaneDevObject *self, char *name) -{ - return Py_FindMethod(SaneDev_methods, (PyObject *)self, name); -} - -staticforward PyTypeObject SaneDev_Type = { - PyObject_HEAD_INIT(&PyType_Type) - 0, /*ob_size*/ +static PyTypeObject SaneDev_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "SaneDev", /*tp_name*/ sizeof(SaneDevObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)SaneDev_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)SaneDev_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SaneDev_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* --------------------------------------------------------------------- */ @@ -1127,12 +1171,12 @@ PySane_init(PyObject *self, PyObject *args) { SANE_Status st; SANE_Int version; - + if (!PyArg_ParseTuple(args, "")) return NULL; /* XXX Authorization is not yet supported */ - st=sane_init(&version, NULL); + st=sane_init(&version, NULL); if (st) return PySane_Error(st); return Py_BuildValue("iiii", version, SANE_VERSION_MAJOR(version), SANE_VERSION_MINOR(version), SANE_VERSION_BUILD(version)); @@ -1152,28 +1196,30 @@ PySane_exit(PyObject *self, PyObject *args) static PyObject * PySane_get_devices(PyObject *self, PyObject *args) { - SANE_Device **devlist; - SANE_Device *dev; + const SANE_Device **devlist; + const SANE_Device *dev; SANE_Status st; PyObject *list; - int local_only, i; - + int local_only = 0, i; + if (!PyArg_ParseTuple(args, "|i", &local_only)) { return NULL; } - + + Py_BEGIN_ALLOW_THREADS st=sane_get_devices(&devlist, local_only); + Py_END_ALLOW_THREADS if (st) return PySane_Error(st); if (!(list = PyList_New(0))) return NULL; for(i=0; devlist[i]!=NULL; i++) { dev=devlist[i]; - PyList_Append(list, Py_BuildValue("ssss", dev->name, dev->vendor, + PyList_Append(list, Py_BuildValue("ssss", dev->name, dev->vendor, dev->model, dev->type)); } - + return list; } @@ -1191,8 +1237,10 @@ PySane_open(PyObject *self, PyObject *args) rv = newSaneDevObject(); if ( rv == NULL ) return NULL; + Py_BEGIN_ALLOW_THREADS st = sane_open(name, &(rv->h)); - if (st) + Py_END_ALLOW_THREADS + if (st) { Py_DECREF(rv); return PySane_Error(st); @@ -1205,7 +1253,7 @@ PySane_OPTION_IS_ACTIVE(PyObject *self, PyObject *args) { SANE_Int cap; long lg; - + if (!PyArg_ParseTuple(args, "l", &lg)) return NULL; cap=lg; @@ -1217,7 +1265,7 @@ PySane_OPTION_IS_SETTABLE(PyObject *self, PyObject *args) { SANE_Int cap; long lg; - + if (!PyArg_ParseTuple(args, "l", &lg)) return NULL; cap=lg; @@ -1248,17 +1296,40 @@ insint(PyObject *d, char *name, int value) Py_DECREF(v); } -void +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef PySane_moduledef = { + PyModuleDef_HEAD_INIT, + "_sane", + NULL, + 0, + PySane_methods, + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit__sane(void) +{ + /* Create the module and add the functions */ + PyObject *m = PyModule_Create(&PySane_moduledef); + if(!m) + return NULL; +#else /* if PY_MAJOR_VERSION < 3 */ + +PyMODINIT_FUNC init_sane(void) { - PyObject *m, *d; - - /* Create the module and add the functions */ - m = Py_InitModule("_sane", PySane_methods); + /* Create the module and add the functions */ + PyObject *m = Py_InitModule("_sane", PySane_methods); + if(!m) + return; +#endif /* Add some symbolic constants to the module */ - d = PyModule_GetDict(m); - ErrorObject = PyString_FromString("_sane.error"); + PyObject *d = PyModule_GetDict(m); + ErrorObject = PyErr_NewException("_sane.error", NULL, NULL); PyDict_SetItemString(d, "error", ErrorObject); insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); @@ -1306,7 +1377,7 @@ init_sane(void) insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); insint(d, "INFO_RELOAD_OPTIONS", SANE_INFO_RELOAD_OPTIONS); insint(d, "INFO_RELOAD_PARAMS", SANE_INFO_RELOAD_PARAMS); - + /* Check for errors */ if (PyErr_Occurred()) Py_FatalError("can't initialize module _sane"); @@ -1316,10 +1387,13 @@ init_sane(void) if (PyErr_Occurred()) PyErr_Clear(); else - /* this global variable is declared just in front of the + /* this global variable is declared just in front of the arr_snap() function and should be set to 1 after successfully importing the numarray module. */ NUMARRAY_IMPORTED = 1; #endif /* WITH_NUMARRAY */ +#if PY_MAJOR_VERSION >= 3 + return m; +#endif } diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py index 0104af2d5..57fcc4407 100644 --- a/Sane/demo_numarray.py +++ b/Sane/demo_numarray.py @@ -4,6 +4,8 @@ # Shows how to scan a 16 bit grayscale image into a numarray object # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') @@ -14,17 +16,17 @@ import Image def toImage(arr): if arr.type().bytes == 1: # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) else: arr_c = arr - arr.min() arr_c *= (255./arr_c.max()) arr = arr_c.astype(UInt8) # need to swap coordinates btw array and image (with [::-1]) - im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + im = Image.frombytes('L', arr.shape[::-1], arr.tostring()) return im -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) @@ -32,7 +34,7 @@ s = sane.open(sane.get_devices()[0][0]) s.mode = 'gray' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) s.depth=16 arr16 = s.arr_scan() diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py index 016361f8a..490f33158 100644 --- a/Sane/demo_pil.py +++ b/Sane/demo_pil.py @@ -4,19 +4,21 @@ # Shows how to scan a color image into a PIL rgb-image # +from __future__ import print_function + # Get the path set up to find PIL modules if not installed yet: import sys ; sys.path.append('../PIL') import sane -print 'SANE version:', sane.init() -print 'Available devices=', sane.get_devices() +print('SANE version:', sane.init()) +print('Available devices=', sane.get_devices()) s = sane.open(sane.get_devices()[0][0]) s.mode = 'color' s.br_x=320. ; s.br_y=240. -print 'Device parameters:', s.get_parameters() +print('Device parameters:', s.get_parameters()) # Initiate the scan s.start() diff --git a/Sane/sane.py b/Sane/sane.py index 27be5a259..331776f95 100644 --- a/Sane/sane.py +++ b/Sane/sane.py @@ -46,7 +46,6 @@ class Option: """ def __init__(self, args, scanDev): - import string self.scanDev = scanDev # needed to get current value of this option self.index, self.name = args[0], args[1] self.title, self.desc = args[2], args[3] @@ -56,8 +55,8 @@ class Option: def f(x): if x=='-': return '_' else: return x - if type(self.name)!=type(''): self.py_name=str(self.name) - else: self.py_name=string.join(map(f, self.name), '') + if not isinstance(self.name, str): self.py_name=str(self.name) + else: self.py_name=''.join(map(f, self.name)) def is_active(self): return _sane.OPTION_IS_ACTIVE(self.cap) @@ -86,7 +85,7 @@ active: %s settable: %s\n""" % (self.py_name, curValue, self.index, self.title, self.desc, TYPE_STR[self.type], UNIT_STR[self.unit], - `self.constraint`, active, settable) + repr(self.constraint), active, settable) return s @@ -106,7 +105,7 @@ class _SaneIterator: def next(self): try: self.device.start() - except error, v: + except error as v: if v == 'Document feeder out of documents': raise StopIteration else: @@ -166,16 +165,16 @@ class SaneDev: def __setattr__(self, key, value): dev=self.__dict__['dev'] optdict=self.__dict__['opt'] - if not optdict.has_key(key): + if key not in optdict: self.__dict__[key]=value ; return opt=optdict[key] if opt.type==TYPE_GROUP: - raise AttributeError, "Groups can't be set: "+key + raise AttributeError("Groups can't be set: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) if not _sane.OPTION_IS_SETTABLE(opt.cap): - raise AttributeError, "Option can't be set by software: "+key - if type(value) == int and opt.type == TYPE_FIXED: + raise AttributeError("Option can't be set by software: "+key) + if isinstance(value, int) and opt.type == TYPE_FIXED: # avoid annoying errors of backend if int is given instead float: value = float(value) self.last_opt = dev.set_option(opt.index, value) @@ -187,18 +186,18 @@ class SaneDev: dev=self.__dict__['dev'] optdict=self.__dict__['opt'] if key=='optlist': - return self.opt.keys() + return list(self.opt.keys()) if key=='area': return (self.tl_x, self.tl_y),(self.br_x, self.br_y) - if not optdict.has_key(key): - raise AttributeError, 'No such attribute: '+key + if key not in optdict: + raise AttributeError('No such attribute: '+key) opt=optdict[key] if opt.type==TYPE_BUTTON: - raise AttributeError, "Buttons don't have values: "+key + raise AttributeError("Buttons don't have values: "+key) if opt.type==TYPE_GROUP: - raise AttributeError, "Groups don't have values: "+key + raise AttributeError("Groups don't have values: "+key) if not _sane.OPTION_IS_ACTIVE(opt.cap): - raise AttributeError, 'Inactive option: '+key + raise AttributeError('Inactive option: '+key) value = dev.get_option(opt.index) return value diff --git a/Sane/sanedoc.txt b/Sane/sanedoc.txt index db86938e3..f23000122 100644 --- a/Sane/sanedoc.txt +++ b/Sane/sanedoc.txt @@ -9,13 +9,13 @@ understanding. This module has been originally developed by A.M. Kuchling (amk1@erols.com), now development has been taken over by Ralph Heinkel (rheinkel-at-email.de). -If you write to me please make sure to have the word 'SANE' or 'sane' in +If you write to me please make sure to have the word 'SANE' or 'sane' in the subject of your mail, otherwise it might be classified as spam in the future. The module exports two object types, a bunch of constants, and two -functions. +functions. get_devices() Return a list of 4-tuples containing the available scanning @@ -73,7 +73,7 @@ get_parameters() start() Start a scan. This function must be called before the _snap()_ method can be used. - + cancel() Cancel a scan already in progress. @@ -81,7 +81,7 @@ snap(no_cancel=0) Snap a single frame of data, returning a PIL Image object containing the data. If no_cancel is false, the Sane library function sane_cancel is called after the scan. This is reasonable in most cases, - but may cause backends for duplex ADF scanners to drop the backside image, + but may cause backends for duplex ADF scanners to drop the backside image, when snap() is called for the front side image. If no_cancel is true, cancel() should be called manually, after all scans are finished. @@ -90,13 +90,13 @@ scan() Returns a PIL image multi_scan() - This method returns an iterator. It is intended to be used for - scanning with an automatic document feeder. The next() method of the + This method returns an iterator. It is intended to be used for + scanning with an automatic document feeder. The next() method of the iterator tries to start a scan. If this is successful, it returns a - PIL Image object, like scan(); if the document feeder runs out of + PIL Image object, like scan(); if the document feeder runs out of paper, it raises StopIteration, thereby signaling that the sequence is ran out of items. - + arr_snap(multipleOf=1) same as snap, but the result is a NumArray object. (Not that num_array must be installed already at compilation time, otherwise @@ -123,14 +123,14 @@ Attributes: SaneDev objects have a few fixed attributes which are always available, and a larger collection of attributes which vary depending on the device. An Epson 1660 photo scanner has attributes like -'mode', 'depth', etc. +'mode', 'depth', etc. Another (pseudo scanner), the _pnm:0_ device, takes a PNM file and simulates a scanner using the image data; a SaneDev object representing the _pnm:0_ device therefore has a _filename_ attribute which can be changed to specify the filename, _contrast_ and _brightness_ attributes to modify the returned image, and so forth. -The values of the scanner options may be an integer, floating-point +The values of the scanner options may be an integer, floating-point value, or string, depending on the nature of the option. sane_signature @@ -183,13 +183,13 @@ In order to change 'mode' to 'gray', just type: >>> s.mode = 'gray' -With the attributes and methods of sane-option objects it is possible +With the attributes and methods of sane-option objects it is possible to access individual option values: -is_active() +is_active() Returns true if the option is active. -is_settable() +is_settable() Returns true if the option can be set under software control. @@ -216,21 +216,21 @@ index An integer giving the option's index in the option list. name - A short name for the option, as it comes from the sane-backend. + A short name for the option, as it comes from the sane-backend. -py_name +py_name The option's name, as a legal Python identifier. The name attribute may contain the '-' character, so it will be converted to '_' for the py_name attribute. size - For a string-valued option, this is the maximum length allowed. + For a string-valued option, this is the maximum length allowed. title A single-line string that can be used as a title string. - + type - A constant giving the type of this option: will be one of the following + A constant giving the type of this option: will be one of the following constants found in the SANE module: TYPE_BOOL TYPE_INT @@ -274,7 +274,7 @@ Device parameters: ('L', 1, (424, 585), 1, 53) ## In order to obtain a 16-bit grayscale image at 100DPI in a numarray object ## with bottom-right coordinates set to (160, 120) [in millimeter] : >>> s.mode = 'gray' ->>> s.br_x=160. ; s.br_y=120. +>>> s.br_x=160. ; s.br_y=120. >>> s.resolution = 100 >>> s.depth=16 >>> s.start() diff --git a/Scripts/README b/Scripts/README index a09b0621a..befa7e7be 100644 --- a/Scripts/README +++ b/Scripts/README @@ -14,7 +14,7 @@ pildriver.py (by Eric S. Raymond) A class implementing an image-processing calculator for scripts. Parses lists of commnds (or, called interactively, command-line -arguments) into image loads, transformations, and saves. +arguments) into image loads, transformations, and saves. -------------------------------------------------------------------- viewer.py diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py index 957b51c8d..fe250c9f8 100644 --- a/Scripts/enhancer.py +++ b/Scripts/enhancer.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -6,7 +7,11 @@ # drag the slider to modify the image. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk, ImageEnhance import sys diff --git a/Scripts/explode.py b/Scripts/explode.py index a336f0699..90084a464 100644 --- a/Scripts/explode.py +++ b/Scripts/explode.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -5,8 +6,10 @@ # split an animation into a number of frame files # +from __future__ import print_function + from PIL import Image -import os, string, sys +import os, sys class Interval: @@ -18,23 +21,23 @@ class Interval: self.hilo = [] - for s in string.split(interval, ","): - if not string.strip(s): + for s in interval.split(","): + if not s.strip(): continue try: - v = string.atoi(s) + v = int(s) if v < 0: lo, hi = 0, -v else: lo = hi = v except ValueError: - i = string.find(s, "-") - lo, hi = string.atoi(s[:i]), string.atoi(s[i+1:]) + i = s.find("-") + lo, hi = int(s[:i]), int(s[i+1:]) self.hilo.append((hi, lo)) if not self.hilo: - self.hilo = [(sys.maxint, 0)] + self.hilo = [(sys.maxsize, 0)] def __getitem__(self, index): @@ -53,23 +56,23 @@ if sys.argv[1:2] == ["-h"]: del sys.argv[1] if not sys.argv[2:]: - print - print "Syntax: python explode.py infile template [range]" - print - print "The template argument is used to construct the names of the" - print "individual frame files. The frames are numbered file001.ext," - print "file002.ext, etc. You can insert %d to control the placement" - print "and syntax of the frame number." - print - print "The optional range argument specifies which frames to extract." - print "You can give one or more ranges like 1-10, 5, -15 etc. If" - print "omitted, all frames are extracted." + print() + print("Syntax: python explode.py infile template [range]") + print() + print("The template argument is used to construct the names of the") + print("individual frame files. The frames are numbered file001.ext,") + print("file002.ext, etc. You can insert %d to control the placement") + print("and syntax of the frame number.") + print() + print("The optional range argument specifies which frames to extract.") + print("You can give one or more ranges like 1-10, 5, -15 etc. If") + print("omitted, all frames are extracted.") sys.exit(1) infile = sys.argv[1] outfile = sys.argv[2] -frames = Interval(string.join(sys.argv[3:], ",")) +frames = Interval(",".join(sys.argv[3:])) try: # check if outfile contains a placeholder @@ -87,11 +90,11 @@ if html: html = open(file+".html", "w") html.write("\n\n") -while 1: +while True: if frames[ix]: im.save(outfile % ix) - print outfile % ix + print(outfile % ix) if html: html.write("
\n" % outfile % ix) diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py index 95524eacc..9964f77b1 100644 --- a/Scripts/gifmaker.py +++ b/Scripts/gifmaker.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -39,8 +40,9 @@ # write data directly to a socket. Or something... # +from __future__ import print_function + from PIL import Image, ImageChops -import string from PIL.GifImagePlugin import getheader, getdata @@ -128,8 +130,8 @@ if __name__ == "__main__": import sys if len(sys.argv) < 3: - print "GIFMAKER -- create GIF animations" - print "Usage: gifmaker infile outfile" + print("GIFMAKER -- create GIF animations") + print("Usage: gifmaker infile outfile") sys.exit(1) compress(sys.argv[1], sys.argv[2]) diff --git a/Scripts/painter.py b/Scripts/painter.py index efe307386..80c4db6a0 100644 --- a/Scripts/painter.py +++ b/Scripts/painter.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -8,7 +9,11 @@ # the image into a set of tiles. # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py index 1c688f7c9..934167351 100644 --- a/Scripts/pilconvert.py +++ b/Scripts/pilconvert.py @@ -1,4 +1,4 @@ -#! /usr/local/bin/python +#!/usr/bin/env python # # The Python Imaging Library. # $Id$ @@ -13,27 +13,29 @@ # 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) # +from __future__ import print_function + import site import getopt, string, sys from PIL import Image def usage(): - print "PIL Convert 0.5/1998-12-30 -- convert image files" - print "Usage: pilconvert [option] infile outfile" - print - print "Options:" - print - print " -c convert to format (default is given by extension)" - print - print " -g convert to greyscale" - print " -p convert to palette image (using standard palette)" - print " -r convert to rgb" - print - print " -o optimize output (trade speed for size)" - print " -q set compression quality (0-100, JPEG only)" - print - print " -f list supported file formats" + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") sys.exit(1) if len(sys.argv) == 1: @@ -41,8 +43,8 @@ if len(sys.argv) == 1: try: opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) format = None @@ -54,14 +56,13 @@ for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() - print "Supported formats (* indicates output format):" + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") for i in id: - if Image.SAVE.has_key(i): - print i+"*", + if i in Image.SAVE: + print(i+"*", end=' ') else: - print i, + print(i, end=' ') sys.exit(1) elif o == "-c": @@ -88,9 +89,9 @@ try: im.draft(convert, im.size) im = im.convert(convert) if format: - apply(im.save, (argv[1], format), options) + im.save(argv[1], format, **options) else: - apply(im.save, (argv[1],), options) + im.save(argv[1], **options) except: - print "cannot convert image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py index 5dd575a3f..98708c897 100644 --- a/Scripts/pildriver.py +++ b/Scripts/pildriver.py @@ -48,8 +48,9 @@ of its upper-left-hand corner and displays the cropped portion. # 3. Add support for composing and decomposing multiple-image files. # +from __future__ import print_function + from PIL import Image -import string class PILDriver: @@ -60,7 +61,7 @@ class PILDriver: Set verbosity flag from top of stack. """ - self.verbose = self.do_pop() + self.verbose = int(self.do_pop()) # The evaluation stack (internal only) @@ -205,8 +206,8 @@ class PILDriver: Process the top image with the given filter. """ - import ImageFilter - filter = eval("ImageFilter." + string.upper(self.do_pop())) + from PIL import ImageFilter + filter = eval("ImageFilter." + self.do_pop().upper()) image = self.do_pop() self.push(image.filter(filter)) @@ -314,7 +315,7 @@ class PILDriver: Transpose the top image. """ - transpose = string.upper(self.do_pop()) + transpose = self.do_pop().upper() image = self.do_pop() self.push(image.transpose(transpose)) @@ -325,21 +326,21 @@ class PILDriver: Push the format of the top image onto the stack. """ - self.push(self.pop().format) + self.push(self.do_pop().format) def do_mode(self): """usage: mode Push the mode of the top image onto the stack. """ - self.push(self.pop().mode) + self.push(self.do_pop().mode) def do_size(self): """usage: size Push the image size on the stack as (y, x). """ - size = self.pop().size + size = self.do_pop().size self.push(size[0]) self.push(size[1]) @@ -350,7 +351,7 @@ class PILDriver: Invert the top image. """ - import ImageChops + from PIL import ImageChops self.push(ImageChops.invert(self.do_pop())) def do_lighter(self): @@ -358,7 +359,7 @@ class PILDriver: Pop the two top images, push an image of the lighter pixels of both. """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.lighter(image1, image2)) @@ -368,7 +369,7 @@ class PILDriver: Pop the two top images, push an image of the darker pixels of both. """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.darker(image1, image2)) @@ -378,7 +379,7 @@ class PILDriver: Pop the two top images, push the difference image """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.difference(image1, image2)) @@ -388,7 +389,7 @@ class PILDriver: Pop the two top images, push the multiplication image. """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() self.push(ImageChops.multiply(image1, image2)) @@ -398,7 +399,7 @@ class PILDriver: Pop the two top images, superimpose their inverted versions. """ - import ImageChops + from PIL import ImageChops image2 = self.do_pop() image1 = self.do_pop() self.push(ImageChops.screen(image1, image2)) @@ -408,7 +409,7 @@ class PILDriver: Pop the two top images, produce the scaled sum with offset. """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() scale = float(self.do_pop()) @@ -420,7 +421,7 @@ class PILDriver: Pop the two top images, produce the scaled difference with offset. """ - import ImageChops + from PIL import ImageChops image1 = self.do_pop() image2 = self.do_pop() scale = float(self.do_pop()) @@ -434,7 +435,7 @@ class PILDriver: Enhance color in the top image. """ - import ImageEnhance + from PIL import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() enhancer = ImageEnhance.Color(image) @@ -445,10 +446,10 @@ class PILDriver: Enhance contrast in the top image. """ - import ImageEnhance + from PIL import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() - enhancer = ImageEnhance.Color(image) + enhancer = ImageEnhance.Contrast(image) self.push(enhancer.enhance(factor)) def do_brightness(self): @@ -456,10 +457,10 @@ class PILDriver: Enhance brightness in the top image. """ - import ImageEnhance + from PIL import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() - enhancer = ImageEnhance.Color(image) + enhancer = ImageEnhance.Brightness(image) self.push(enhancer.enhance(factor)) def do_sharpness(self): @@ -467,10 +468,10 @@ class PILDriver: Enhance sharpness in the top image. """ - import ImageEnhance + from PIL import ImageEnhance factor = float(self.do_pop()) image = self.do_pop() - enhancer = ImageEnhance.Color(image) + enhancer = ImageEnhance.Sharpness(image) self.push(enhancer.enhance(factor)) # The interpreter loop @@ -482,9 +483,9 @@ class PILDriver: self.push(list[0]) list = list[1:] if self.verbose: - print "Stack: " + `self.stack` + print("Stack: " + repr(self.stack)) top = self.top() - if type(top) != type(""): + if not isinstance(top, str): continue; funcname = "do_" + top if not hasattr(self, funcname): @@ -508,15 +509,18 @@ if __name__ == '__main__': if len(sys.argv[1:]) > 0: driver.execute(sys.argv[1:]) else: - print "PILDriver says hello." - while 1: + print("PILDriver says hello.") + while True: try: - line = raw_input('pildriver> '); + if sys.version_info[0] >= 3: + line = input('pildriver> '); + else: + line = raw_input('pildriver> '); except EOFError: - print "\nPILDriver says goodbye." + print("\nPILDriver says goodbye.") break - driver.execute(string.split(line)) - print driver.stack + driver.execute(line.split()) + print(driver.stack) # The following sets edit modes for GNU EMACS # Local Variables: diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py index 695725796..48514e88b 100644 --- a/Scripts/pilfile.py +++ b/Scripts/pilfile.py @@ -1,4 +1,4 @@ -#! /usr/local/bin/python +#!/usr/bin/env python # # The Python Imaging Library. # $Id$ @@ -17,25 +17,27 @@ # 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks # +from __future__ import print_function + import site import getopt, glob, sys from PIL import Image if len(sys.argv) == 1: - print "PIL File 0.4/2003-09-30 -- identify image files" - print "Usage: pilfile [option] files..." - print "Options:" - print " -f list supported file formats" - print " -i show associated info and tile data" - print " -v verify file headers" - print " -q quiet, don't warn for unidentified/missing/broken files" + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") sys.exit(1) try: opt, args = getopt.getopt(sys.argv[1:], "fqivD") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) verbose = quiet = verify = 0 @@ -43,11 +45,10 @@ verbose = quiet = verify = 0 for o, a in opt: if o == "-f": Image.init() - id = Image.ID[:] - id.sort() - print "Supported formats:" + id = sorted(Image.ID) + print("Supported formats:") for i in id: - print i, + print(i, end=' ') sys.exit(1) elif o == "-i": verbose = 1 @@ -73,22 +74,22 @@ def globfix(files): for file in globfix(args): try: im = Image.open(file) - print "%s:" % file, im.format, "%dx%d" % im.size, im.mode, + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') if verbose: - print im.info, im.tile, - print + print(im.info, im.tile, end=' ') + print() if verify: try: im.verify() except: if not quiet: - print "failed to verify image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) - except IOError, v: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: if not quiet: - print file, "failed:", v + print(file, "failed:", v) except: import traceback if not quiet: - print file, "failed:", "unexpected error" + print(file, "failed:", "unexpected error") traceback.print_exc(file=sys.stdout) diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py index df08d4c08..ec25e7a71 100644 --- a/Scripts/pilfont.py +++ b/Scripts/pilfont.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -9,22 +10,23 @@ # 2002-03-10 fl use "from PIL import" # +from __future__ import print_function + VERSION = "0.4" -import site -import glob, os, sys +import glob, sys # drivers from PIL import BdfFontFile from PIL import PcfFontFile if len(sys.argv) <= 1: - print "PILFONT", VERSION, "-- PIL font compiler." - print - print "Usage: pilfont fontfiles..." - print - print "Convert given font files to the PIL raster font format." - print "This version of pilfont supports X BDF and PCF fonts." + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") sys.exit(1) files = [] @@ -33,7 +35,7 @@ for f in sys.argv[1:]: for f in files: - print f + "...", + print(f + "...", end=' ') try: @@ -48,7 +50,7 @@ for f in files: p.save(f) except (SyntaxError, IOError): - print "failed" + print("failed") else: - print "OK" + print("OK") diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py index a98b39f7d..be42e0a75 100644 --- a/Scripts/pilprint.py +++ b/Scripts/pilprint.py @@ -1,4 +1,4 @@ -#! /usr/local/bin/python +#!/usr/bin/env python # # The Python Imaging Library. # $Id$ @@ -11,6 +11,8 @@ # 0.3 2003-05-06 fl Fixed a typo or two. # +from __future__ import print_function + VERSION = "pilprint 0.3/2003-05-05" from PIL import Image @@ -29,18 +31,18 @@ def description(file, image): import getopt, os, sys if len(sys.argv) == 1: - print "PIL Print 0.2a1/96-10-04 -- print image files" - print "Usage: pilprint files..." - print "Options:" - print " -c colour printer (default is monochrome)" - print " -p print via lpr (default is stdout)" - print " -P same as -p but use given printer" + print("PIL Print 0.2a1/96-10-04 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") sys.exit(1) try: opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") -except getopt.error, v: - print v +except getopt.error as v: + print(v) sys.exit(1) printer = None # print to stdout @@ -50,7 +52,7 @@ for o, a in opt: if o == "-d": # debug: show available drivers Image.init() - print Image.ID + print(Image.ID) sys.exit(1) elif o == "-c": # colour printer @@ -89,5 +91,5 @@ for file in argv: ps.end_document() except: - print "cannot print image", - print "(%s:%s)" % (sys.exc_type, sys.exc_value) + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/Scripts/player.py b/Scripts/player.py index 9ca4500bf..84b636668 100644 --- a/Scripts/player.py +++ b/Scripts/player.py @@ -1,9 +1,16 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ # -from Tkinter import * +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys @@ -36,7 +43,7 @@ class AppletDisplay: class UI(Label): def __init__(self, master, im): - if type(im) == type([]): + if isinstance(im, list): # list of images self.im = im[1:] im = self.im[0] @@ -65,7 +72,7 @@ class UI(Label): def next(self): - if type(self.im) == type([]): + if isinstance(self.im, list): try: im = self.im[0] @@ -98,7 +105,7 @@ class UI(Label): if __name__ == "__main__": if not sys.argv[1:]: - print "Syntax: python player.py imagefile(s)" + print("Syntax: python player.py imagefile(s)") sys.exit(1) filename = sys.argv[1] @@ -108,7 +115,7 @@ if __name__ == "__main__": if len(sys.argv) > 2: # list of images - print "loading..." + print("loading...") im = [] for filename in sys.argv[1:]: im.append(Image.open(filename)) diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py index eb5109872..29d4592d9 100644 --- a/Scripts/thresholder.py +++ b/Scripts/thresholder.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ @@ -6,7 +7,11 @@ # as a dynamically updated overlay # -from Tkinter import * +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk import sys diff --git a/Scripts/viewer.py b/Scripts/viewer.py index 6e4dc8b67..86b2526cd 100644 --- a/Scripts/viewer.py +++ b/Scripts/viewer.py @@ -1,9 +1,16 @@ +#!/usr/bin/env python # # The Python Imaging Library # $Id$ # -from Tkinter import * +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + from PIL import Image, ImageTk # @@ -31,7 +38,7 @@ if __name__ == "__main__": import sys if not sys.argv[1:]: - print "Syntax: python viewer.py imagefile" + print("Syntax: python viewer.py imagefile") sys.exit(1) filename = sys.argv[1] diff --git a/Tests/README.txt b/Tests/README.txt new file mode 100644 index 000000000..169bc4da5 --- /dev/null +++ b/Tests/README.txt @@ -0,0 +1,14 @@ +Minimalistic PIL test framework. + +Test scripts are named "test_xxx" and are supposed to output "ok". That's it. To run the tests:: + + python setup.py develop + +Run the tests from the root of the Pillow source distribution: + + python selftest.py + python Tests/run.py --installed + +To run an individual test: + + python Tests/test_image.py diff --git a/Tests/bench_get.py b/Tests/bench_get.py new file mode 100644 index 000000000..eca491600 --- /dev/null +++ b/Tests/bench_get.py @@ -0,0 +1,20 @@ +import sys +sys.path.insert(0, ".") + +import tester +import timeit + +def bench(mode): + im = tester.lena(mode) + get = im.im.getpixel + xy = 50, 50 # position shouldn't really matter + t0 = timeit.default_timer() + for i in range(1000000): + get(xy) + print(mode, timeit.default_timer() - t0, "us") + +bench("L") +bench("I") +bench("I;16") +bench("F") +bench("RGB") diff --git a/Tests/cms_test.py b/Tests/cms_test.py new file mode 100644 index 000000000..634e3f717 --- /dev/null +++ b/Tests/cms_test.py @@ -0,0 +1,222 @@ +# PyCMSTests.py +# Examples of how to use pyCMS, as well as tests to verify it works properly +# By Kevin Cazabon (kevin@cazabon.com) + +# Imports +import os +from PIL import Image +from PIL import ImageCms + +# import PyCMSError separately so we can catch it +PyCMSError = ImageCms.PyCMSError + +####################################################################### +# Configuration: +####################################################################### +# set this to the image you want to test with +IMAGE = "c:\\temp\\test.tif" + +# set this to where you want to save the output images +OUTPUTDIR = "c:\\temp\\" + +# set these to two different ICC profiles, one for input, one for output +# set the corresponding mode to the proper PIL mode for that profile +INPUT_PROFILE = "c:\\temp\\profiles\\sRGB.icm" +INMODE = "RGB" + +OUTPUT_PROFILE = "c:\\temp\\profiles\\genericRGB.icm" +OUTMODE = "RGB" + +PROOF_PROFILE = "c:\\temp\\profiles\\monitor.icm" + +# set to True to show() images, False to save them into OUTPUT_DIRECTORY +SHOW = False + +# Tests you can enable/disable +TEST_error_catching = True +TEST_profileToProfile = True +TEST_profileToProfile_inPlace = True +TEST_buildTransform = True +TEST_buildTransformFromOpenProfiles = True +TEST_buildProofTransform = True +TEST_getProfileInfo = True +TEST_misc = False + +####################################################################### +# helper functions +####################################################################### +def outputImage(im, funcName = None): + # save or display the image, depending on value of SHOW_IMAGES + if SHOW == True: + im.show() + else: + im.save(os.path.join(OUTPUTDIR, "%s.tif" %funcName)) + + +####################################################################### +# The tests themselves +####################################################################### + +if TEST_error_catching == True: + im = Image.open(IMAGE) + try: + #neither of these proifles exists (unless you make them), so we should + # get an error + imOut = ImageCms.profileToProfile(im, "missingProfile.icm", "cmyk.icm") + + except PyCMSError as reason: + print("We caught a PyCMSError: %s\n\n" %reason) + + print("error catching test completed successfully (if you see the message \ + above that we caught the error).") + +if TEST_profileToProfile == True: + # open the image file using the standard PIL function Image.open() + im = Image.open(IMAGE) + + # send the image, input/output profiles, and rendering intent to + # ImageCms.profileToProfile() + imOut = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE) + + # now that the image is converted, save or display it + outputImage(imOut, "profileToProfile") + + print("profileToProfile test completed successfully.") + +if TEST_profileToProfile_inPlace == True: + # we'll do the same test as profileToProfile, but modify im in place + # instead of getting a new image returned to us + im = Image.open(IMAGE) + + # send the image to ImageCms.profileToProfile(), specifying inPlace = True + result = ImageCms.profileToProfile(im, INPUT_PROFILE, OUTPUT_PROFILE, \ + outputMode = OUTMODE, inPlace = True) + + # now that the image is converted, save or display it + if result == None: + # this is the normal result when modifying in-place + outputImage(im, "profileToProfile_inPlace") + else: + # something failed... + print("profileToProfile in-place failed: %s" %result) + + print("profileToProfile in-place test completed successfully.") + +if TEST_buildTransform == True: + # make a transform using the input and output profile path strings + transform = ImageCms.buildTransform(INPUT_PROFILE, OUTPUT_PROFILE, INMODE, \ + OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransform_inPlace") + + print("buildTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_buildTransformFromOpenProfiles == True: + # we'll actually test a couple profile open/creation functions here too + + # first, get a handle to an input profile, in this case we'll create + # an sRGB profile on the fly: + inputProfile = ImageCms.createProfile("sRGB") + + # then, get a handle to the output profile + outputProfile = ImageCms.getOpenProfile(OUTPUT_PROFILE) + + # make a transform from these + transform = ImageCms.buildTransformFromOpenProfiles(inputProfile, \ + outputProfile, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildTransformFromOpenProfiles") + + # then do it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildTransformFromOpenProfiles_inPlace") + + print("buildTransformFromOpenProfiles test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the each item. + # Python should also do this automatically when it goes out of scope. + del(inputProfile) + del(outputProfile) + del(transform) + +if TEST_buildProofTransform == True: + # make a transform using the input and output and proof profile path + # strings + # images converted with this transform will simulate the appearance + # of the output device while actually being displayed/proofed on the + # proof device. This usually means a monitor, but can also mean + # other proof-printers like dye-sub, etc. + transform = ImageCms.buildProofTransform(INPUT_PROFILE, OUTPUT_PROFILE, \ + PROOF_PROFILE, INMODE, OUTMODE) + + # now, use the trnsform to convert a couple images + im = Image.open(IMAGE) + + # transform im normally + im2 = ImageCms.applyTransform(im, transform) + outputImage(im2, "buildProofTransform") + + # then transform it again using the same transform, this time in-place. + result = ImageCms.applyTransform(im, transform, inPlace = True) + outputImage(im, "buildProofTransform_inPlace") + + print("buildProofTransform test completed successfully.") + + # and, to clean up a bit, delete the transform + # this should call the C destructor for the transform structure. + # Python should also do this automatically when it goes out of scope. + del(transform) + +if TEST_getProfileInfo == True: + # get a profile handle + profile = ImageCms.getOpenProfile(INPUT_PROFILE) + + # lets print some info about our input profile: + print("Profile name (retrieved from profile string path name): %s" %ImageCms.getProfileName(INPUT_PROFILE)) + + # or, you could do the same thing using a profile handle as the arg + print("Profile name (retrieved from profile handle): %s" %ImageCms.getProfileName(profile)) + + # now lets get the embedded "info" tag contents + # once again, you can use a path to a profile, or a profile handle + print("Profile info (retrieved from profile handle): %s" %ImageCms.getProfileInfo(profile)) + + # and what's the default intent of this profile? + print("The default intent is (this will be an integer): %d" %(ImageCms.getDefaultIntent(profile))) + + # Hmmmm... but does this profile support INTENT_ABSOLUTE_COLORIMETRIC? + print("Does it support INTENT_ABSOLUTE_COLORIMETRIC?: (1 is yes, -1 is no): %s" \ + %ImageCms.isIntentSupported(profile, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, \ + ImageCms.DIRECTION_INPUT)) + + print("getProfileInfo test completed successfully.") + +if TEST_misc == True: + # test the versions, about, and copyright functions + print("Versions: %s" %str(ImageCms.versions())) + print("About:\n\n%s" %ImageCms.about()) + print("Copyright:\n\n%s" %ImageCms.copyright()) + + print("misc test completed successfully.") + diff --git a/Tests/crash_ttf_memory_error.py b/Tests/crash_ttf_memory_error.py new file mode 100644 index 000000000..881fc03a6 --- /dev/null +++ b/Tests/crash_ttf_memory_error.py @@ -0,0 +1,14 @@ +from PIL import Image, ImageFont, ImageDraw + +font = "../pil-archive/memory-error-2.ttf" + +s = "Test Text" +f = ImageFont.truetype(font, 64, index=0, encoding="unicode") +w, h = f.getsize(s) +i = Image.new("RGB", (500, h), "white") +d = ImageDraw.Draw(i) + +# this line causes a MemoryError +d.text((0,0), s, font=f, fill=0) + +i.show() diff --git a/Tests/fonts/FreeMono.ttf b/Tests/fonts/FreeMono.ttf new file mode 100644 index 000000000..f88bcef9c Binary files /dev/null and b/Tests/fonts/FreeMono.ttf differ diff --git a/Tests/fonts/helvO18.pcf b/Tests/fonts/helvO18.pcf new file mode 100644 index 000000000..f5e68ae9c Binary files /dev/null and b/Tests/fonts/helvO18.pcf differ diff --git a/Tests/icc/CMY.icm b/Tests/icc/CMY.icm new file mode 100644 index 000000000..acc71ddd9 Binary files /dev/null and b/Tests/icc/CMY.icm differ diff --git a/Tests/icc/YCC709.icm b/Tests/icc/YCC709.icm new file mode 100644 index 000000000..d9f5a5a5d Binary files /dev/null and b/Tests/icc/YCC709.icm differ diff --git a/Tests/icc/sRGB.icm b/Tests/icc/sRGB.icm new file mode 100644 index 000000000..7f9d18d09 Binary files /dev/null and b/Tests/icc/sRGB.icm differ diff --git a/Tests/images/12bit.cropped.tif b/Tests/images/12bit.cropped.tif new file mode 100644 index 000000000..85af58328 Binary files /dev/null and b/Tests/images/12bit.cropped.tif differ diff --git a/Tests/images/12in16bit.tif b/Tests/images/12in16bit.tif new file mode 100644 index 000000000..02e1bfe6a Binary files /dev/null and b/Tests/images/12in16bit.tif differ diff --git a/Tests/images/16bit.MM.cropped.tif b/Tests/images/16bit.MM.cropped.tif new file mode 100644 index 000000000..c4c24e2eb Binary files /dev/null and b/Tests/images/16bit.MM.cropped.tif differ diff --git a/Tests/images/16bit.MM.deflate.tif b/Tests/images/16bit.MM.deflate.tif new file mode 100644 index 000000000..90a62e94f Binary files /dev/null and b/Tests/images/16bit.MM.deflate.tif differ diff --git a/Tests/images/16bit.cropped.tif b/Tests/images/16bit.cropped.tif new file mode 100644 index 000000000..eb8b1dc21 Binary files /dev/null and b/Tests/images/16bit.cropped.tif differ diff --git a/Tests/images/16bit.deflate.tif b/Tests/images/16bit.deflate.tif new file mode 100644 index 000000000..4f9f4ec06 Binary files /dev/null and b/Tests/images/16bit.deflate.tif differ diff --git a/Tests/images/broken.png b/Tests/images/broken.png new file mode 100644 index 000000000..7def38564 --- /dev/null +++ b/Tests/images/broken.png @@ -0,0 +1,5 @@ +PNG + +/}P̒>0jȂ&✉\@TLU +x/ +Z`w;9c \ No newline at end of file diff --git a/Tests/images/caption_6_33_22.png b/Tests/images/caption_6_33_22.png new file mode 100644 index 000000000..2aa0a50bf Binary files /dev/null and b/Tests/images/caption_6_33_22.png differ diff --git a/Tests/images/create_eps.gnuplot b/Tests/images/create_eps.gnuplot new file mode 100644 index 000000000..4d7e29877 --- /dev/null +++ b/Tests/images/create_eps.gnuplot @@ -0,0 +1,30 @@ +#!/usr/bin/gnuplot + +#This is the script that was used to create our sample EPS files +#We used the following version of the gnuplot program +#G N U P L O T +#Version 4.6 patchlevel 3 last modified 2013-04-12 +#Build System: Darwin x86_64 + +#This file will generate the non_zero_bb.eps variant, in order to get the +#zero_bb.eps variant you will need to edit line6 in the result file to +#be "%%BoundingBox: 0 0 460 352" instead of "%%BoundingBox: 50 50 410 302" + +set t postscript eps color +set o "sample.eps" +set dummy u,v +set key bmargin center horizontal Right noreverse enhanced autotitles nobox +set parametric +set view 50, 30, 1, 1 +set isosamples 10, 10 +set hidden3d back offset 1 trianglepattern 3 undefined 1 altdiagonal bentover +set ticslevel 0 +set title "Interlocking Tori" + +set style line 1 lt 1 lw 1 pt 3 lc rgb "red" +set style line 2 lt 1 lw 1 pt 3 lc rgb "blue" + +set urange [ -3.14159 : 3.14159 ] noreverse nowriteback +set vrange [ -3.14159 : 3.14159 ] noreverse nowriteback +splot cos(u)+.5*cos(u)*cos(v),sin(u)+.5*sin(u)*cos(v),.5*sin(v) ls 1,\ + 1+cos(u)+.5*cos(u)*cos(v),.5*sin(v),sin(u)+.5*sin(u)*cos(v) ls 2 diff --git a/Tests/images/flower.jpg b/Tests/images/flower.jpg new file mode 100644 index 000000000..933719d1c Binary files /dev/null and b/Tests/images/flower.jpg differ diff --git a/Tests/images/flower2.jpg b/Tests/images/flower2.jpg new file mode 100644 index 000000000..e94b2f065 Binary files /dev/null and b/Tests/images/flower2.jpg differ diff --git a/Tests/images/g4-fillorder-test.png b/Tests/images/g4-fillorder-test.png new file mode 100644 index 000000000..86b009cfd Binary files /dev/null and b/Tests/images/g4-fillorder-test.png differ diff --git a/Tests/images/g4-fillorder-test.tif b/Tests/images/g4-fillorder-test.tif new file mode 100644 index 000000000..22537b308 Binary files /dev/null and b/Tests/images/g4-fillorder-test.tif differ diff --git a/Tests/images/l_trns.png b/Tests/images/l_trns.png new file mode 100644 index 000000000..a26a82076 Binary files /dev/null and b/Tests/images/l_trns.png differ diff --git a/Tests/images/lab-green.tif b/Tests/images/lab-green.tif new file mode 100644 index 000000000..76c129ee9 Binary files /dev/null and b/Tests/images/lab-green.tif differ diff --git a/Tests/images/lab-red.tif b/Tests/images/lab-red.tif new file mode 100644 index 000000000..fc1953006 Binary files /dev/null and b/Tests/images/lab-red.tif differ diff --git a/Tests/images/lab.tif b/Tests/images/lab.tif new file mode 100644 index 000000000..7dab9b2ba Binary files /dev/null and b/Tests/images/lab.tif differ diff --git a/Tests/images/lena.Lab.tif b/Tests/images/lena.Lab.tif new file mode 100644 index 000000000..335598210 Binary files /dev/null and b/Tests/images/lena.Lab.tif differ diff --git a/Tests/images/lena.tif b/Tests/images/lena.tif new file mode 100644 index 000000000..fead980d4 Binary files /dev/null and b/Tests/images/lena.tif differ diff --git a/Tests/images/lena_bw.png b/Tests/images/lena_bw.png new file mode 100644 index 000000000..f9b64c185 Binary files /dev/null and b/Tests/images/lena_bw.png differ diff --git a/Tests/images/lena_bw_500.png b/Tests/images/lena_bw_500.png new file mode 100644 index 000000000..1e1d0bd7d Binary files /dev/null and b/Tests/images/lena_bw_500.png differ diff --git a/Tests/images/lena_g4.tif b/Tests/images/lena_g4.tif new file mode 100644 index 000000000..7ebe72fab Binary files /dev/null and b/Tests/images/lena_g4.tif differ diff --git a/Tests/images/lena_g4_500.tif b/Tests/images/lena_g4_500.tif new file mode 100644 index 000000000..80f5e70f1 Binary files /dev/null and b/Tests/images/lena_g4_500.tif differ diff --git a/Tests/images/lena_webp_bits.ppm b/Tests/images/lena_webp_bits.ppm new file mode 100644 index 000000000..62fd9803d Binary files /dev/null and b/Tests/images/lena_webp_bits.ppm differ diff --git a/Tests/images/lena_webp_write.ppm b/Tests/images/lena_webp_write.ppm new file mode 100644 index 000000000..4fa197999 Binary files /dev/null and b/Tests/images/lena_webp_write.ppm differ diff --git a/Tests/images/multiline_text.png b/Tests/images/multiline_text.png new file mode 100644 index 000000000..ff1308c5e Binary files /dev/null and b/Tests/images/multiline_text.png differ diff --git a/Tests/images/non_zero_bb.eps b/Tests/images/non_zero_bb.eps new file mode 100644 index 000000000..750a44b38 Binary files /dev/null and b/Tests/images/non_zero_bb.eps differ diff --git a/Tests/images/non_zero_bb.png b/Tests/images/non_zero_bb.png new file mode 100644 index 000000000..156c9a091 Binary files /dev/null and b/Tests/images/non_zero_bb.png differ diff --git a/Tests/images/non_zero_bb_scale2.png b/Tests/images/non_zero_bb_scale2.png new file mode 100644 index 000000000..2600580b3 Binary files /dev/null and b/Tests/images/non_zero_bb_scale2.png differ diff --git a/Tests/images/p_trns_single.png b/Tests/images/p_trns_single.png new file mode 100644 index 000000000..8f37bd452 Binary files /dev/null and b/Tests/images/p_trns_single.png differ diff --git a/Tests/images/pil123p.png b/Tests/images/pil123p.png new file mode 100644 index 000000000..03960d493 Binary files /dev/null and b/Tests/images/pil123p.png differ diff --git a/Tests/images/pil123rgba.png b/Tests/images/pil123rgba.png new file mode 100644 index 000000000..c3d7b7d1b Binary files /dev/null and b/Tests/images/pil123rgba.png differ diff --git a/Tests/images/pil136.tiff b/Tests/images/pil136.tiff new file mode 100644 index 000000000..07d339e7c Binary files /dev/null and b/Tests/images/pil136.tiff differ diff --git a/Tests/images/pil168.tif b/Tests/images/pil168.tif new file mode 100644 index 000000000..621f03a2f Binary files /dev/null and b/Tests/images/pil168.tif differ diff --git a/Tests/images/pil184.pcx b/Tests/images/pil184.pcx new file mode 100644 index 000000000..bbd68be5c Binary files /dev/null and b/Tests/images/pil184.pcx differ diff --git a/Tests/images/pil_sample_cmyk.jpg b/Tests/images/pil_sample_cmyk.jpg new file mode 100644 index 000000000..0c11e70f2 Binary files /dev/null and b/Tests/images/pil_sample_cmyk.jpg differ diff --git a/Tests/images/pil_sample_rgb.jpg b/Tests/images/pil_sample_rgb.jpg new file mode 100644 index 000000000..956a86bae Binary files /dev/null and b/Tests/images/pil_sample_rgb.jpg differ diff --git a/Tests/images/pngtest_bad.png.bin b/Tests/images/pngtest_bad.png.bin new file mode 100644 index 000000000..07a74a104 Binary files /dev/null and b/Tests/images/pngtest_bad.png.bin differ diff --git a/Tests/images/pport_g4.tif b/Tests/images/pport_g4.tif new file mode 100644 index 000000000..8268d398d Binary files /dev/null and b/Tests/images/pport_g4.tif differ diff --git a/Tests/images/rgb.jpg b/Tests/images/rgb.jpg new file mode 100644 index 000000000..fa9953844 Binary files /dev/null and b/Tests/images/rgb.jpg differ diff --git a/Tests/images/rgb_trns.png b/Tests/images/rgb_trns.png new file mode 100644 index 000000000..dee80acdc Binary files /dev/null and b/Tests/images/rgb_trns.png differ diff --git a/Tests/images/tiff_adobe_deflate.tif b/Tests/images/tiff_adobe_deflate.tif new file mode 100644 index 000000000..7c0d9c53d Binary files /dev/null and b/Tests/images/tiff_adobe_deflate.tif differ diff --git a/Tests/images/zero_bb.eps b/Tests/images/zero_bb.eps new file mode 100644 index 000000000..e931bf833 Binary files /dev/null and b/Tests/images/zero_bb.eps differ diff --git a/Tests/images/zero_bb.png b/Tests/images/zero_bb.png new file mode 100644 index 000000000..7d02a5814 Binary files /dev/null and b/Tests/images/zero_bb.png differ diff --git a/Tests/images/zero_bb_scale2.png b/Tests/images/zero_bb_scale2.png new file mode 100644 index 000000000..81c9d056d Binary files /dev/null and b/Tests/images/zero_bb_scale2.png differ diff --git a/Tests/import_all.py b/Tests/import_all.py new file mode 100644 index 000000000..118bf69a7 --- /dev/null +++ b/Tests/import_all.py @@ -0,0 +1,13 @@ +import sys +sys.path.insert(0, ".") + +import glob, os +import traceback + +for file in glob.glob("PIL/*.py"): + module = os.path.basename(file)[:-3] + try: + exec("from PIL import " + module) + except (ImportError, SyntaxError): + print("===", "failed to import", module) + traceback.print_exc() diff --git a/Tests/large_memory_numpy_test.py b/Tests/large_memory_numpy_test.py new file mode 100644 index 000000000..eb9b8aa01 --- /dev/null +++ b/Tests/large_memory_numpy_test.py @@ -0,0 +1,37 @@ +from tester import * + +# This test is not run automatically. +# +# It requires > 2gb memory for the >2 gigapixel image generated in the +# second test. Running this automatically would amount to a denial of +# service on our testing infrastructure. I expect this test to fail +# on any 32 bit machine, as well as any smallish things (like +# raspberrypis). + +from PIL import Image +try: + import numpy as np +except: + skip() + +ydim = 32769 +xdim = 48000 +f = tempfile('temp.png') + +def _write_png(xdim,ydim): + dtype = np.uint8 + a = np.zeros((xdim, ydim), dtype=dtype) + im = Image.fromarray(a, 'L') + im.save(f) + success() + +def test_large(): + """ succeeded prepatch""" + _write_png(xdim,ydim) +def test_2gpx(): + """failed prepatch""" + _write_png(xdim,xdim) + + + + diff --git a/Tests/large_memory_test.py b/Tests/large_memory_test.py new file mode 100644 index 000000000..148841ec2 --- /dev/null +++ b/Tests/large_memory_test.py @@ -0,0 +1,27 @@ +from tester import * + +# This test is not run automatically. +# +# It requires > 2gb memory for the >2 gigapixel image generated in the +# second test. Running this automatically would amount to a denial of +# service on our testing infrastructure. I expect this test to fail +# on any 32 bit machine, as well as any smallish things (like +# raspberrypis). It does succeed on a 3gb Ubuntu 12.04x64 VM on python +# 2.7 an 3.2 + +from PIL import Image +ydim = 32769 +xdim = 48000 +f = tempfile('temp.png') + +def _write_png(xdim,ydim): + im = Image.new('L',(xdim,ydim),(0)) + im.save(f) + success() + +def test_large(): + """ succeeded prepatch""" + _write_png(xdim,ydim) +def test_2gpx(): + """failed prepatch""" + _write_png(xdim,xdim) diff --git a/Tests/make_hash.py b/Tests/make_hash.py new file mode 100644 index 000000000..71e208cff --- /dev/null +++ b/Tests/make_hash.py @@ -0,0 +1,57 @@ +# brute-force search for access descriptor hash table + +import random + +modes = [ + "1", + "L", "LA", + "I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B", + "F", + "P", "PA", + "RGB", "RGBA", "RGBa", "RGBX", + "CMYK", + "YCbCr", + ] + +def hash(s, i): + # djb2 hash: multiply by 33 and xor character + for c in s: + i = (((i<<5) + i) ^ ord(c)) & 0xffffffff + return i + +def check(size, i0): + h = [None] * size + for m in modes: + i = hash(m, i0) + i = i % size + if h[i]: + return 0 + h[i] = m + return h + +min_start = 0 + +# 1) find the smallest table size with no collisions +for min_size in range(len(modes), 16384): + if check(min_size, 0): + print(len(modes), "modes fit in", min_size, "slots") + break + +# 2) see if we can do better with a different initial value +for i0 in range(65556): + for size in range(1, min_size): + if check(size, i0): + if size < min_size: + print(len(modes), "modes fit in", size, "slots with start", i0) + min_size = size + min_start = i0 + +print() + +# print check(min_size, min_start) + +print("#define ACCESS_TABLE_SIZE", min_size) +print("#define ACCESS_TABLE_HASH", min_start) + +# for m in modes: +# print m, "=>", hash(m, min_start) % min_size diff --git a/Tests/run.py b/Tests/run.py new file mode 100644 index 000000000..02b633c90 --- /dev/null +++ b/Tests/run.py @@ -0,0 +1,96 @@ +from __future__ import print_function + +# minimal test runner + +import glob, os, os.path, sys, tempfile + +try: + root = os.path.dirname(__file__) +except NameError: + root = os.path.dirname(sys.argv[0]) + +if not os.path.isfile("PIL/Image.py"): + print("***", "please run this script from the PIL development directory as") + print("***", "$ python Tests/run.py") + sys.exit(1) + +print("-"*68) + +python_options = [] +tester_options = [] + +if "--installed" not in sys.argv: + os.environ["PYTHONPATH"] = "." + +if "--coverage" in sys.argv: + tester_options.append("--coverage") + +if "--log" in sys.argv: + tester_options.append("--log") + +files = glob.glob(os.path.join(root, "test_*.py")) +files.sort() + +success = failure = 0 +include = [x for x in sys.argv[1:] if x[:2] != "--"] +skipped = [] + +python_options = " ".join(python_options) +tester_options = " ".join(tester_options) + +for file in files: + test, ext = os.path.splitext(os.path.basename(file)) + if include and test not in include: + continue + print("running", test, "...") + # 2>&1 works on unix and on modern windowses. we might care about + # very old Python versions, but not ancient microsoft products :-) + out = os.popen("%s %s -u %s %s 2>&1" % ( + sys.executable, python_options, file, tester_options + )) + result = out.read().strip() + if result == "ok": + result = None + elif result == "skip": + print("---", "skipped") # FIXME: driver should include a reason + skipped.append(test) + continue + elif not result: + result = "(no output)" + status = out.close() + if status or result: + if status: + print("=== error", status) + if result: + if result[-3:] == "\nok": + # if there's an ok at the end, it's not really ok + result = result[:-3] + print(result) + failure = failure + 1 + else: + success = success + 1 + +print("-"*68) + +temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') +tempfiles = glob.glob(os.path.join(temp_root, "temp_*")) +if tempfiles: + print("===", "remaining temporary files") + for file in tempfiles: + print(file) + print("-"*68) + +def tests(n): + if n == 1: + return "1 test" + else: + return "%d tests" % n + +if skipped: + print("---", tests(len(skipped)), "skipped.") + print(skipped) +if failure: + print("***", tests(failure), "of", (success + failure), "failed.") + sys.exit(1) +else: + print(tests(success), "passed.") diff --git a/Tests/show_icc.py b/Tests/show_icc.py new file mode 100644 index 000000000..e062747e7 --- /dev/null +++ b/Tests/show_icc.py @@ -0,0 +1,28 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageCms + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/cmyk.jpg" + +i = Image.open(filename) + +print(i.format) +print(i.mode) +print(i.size) +print(i.tile) + +p = ImageCms.getMemoryProfile(i.info["icc_profile"]) + +print(repr(p.product_name)) +print(repr(p.product_info)) + +o = ImageCms.createProfile("sRGB") +t = ImageCms.buildTransformFromOpenProfiles(p, o, i.mode, "RGB") +i = ImageCms.applyTransform(i, t) + +i.show() diff --git a/Tests/show_mcidas.py b/Tests/show_mcidas.py new file mode 100644 index 000000000..db193b82a --- /dev/null +++ b/Tests/show_mcidas.py @@ -0,0 +1,26 @@ +import sys +sys.path.insert(0, ".") + +from PIL import Image +from PIL import ImageMath + +try: + filename = sys.argv[1] +except IndexError: + filename = "../pil-archive/goes12.2005.140.190925.BAND_01.mcidas" + # filename = "../pil-archive/goes12.2005.140.190925.BAND_01.im" + +im = Image.open(filename) + +print(im.format) +print(im.mode) +print(im.size) +print(im.tile) + +lo, hi = im.getextrema() + +print("map", lo, hi, "->", end=' ') +im = ImageMath.eval("convert(im*255/hi, 'L')", im=im, hi=hi) +print(im.getextrema()) + +im.show() diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py new file mode 100644 index 000000000..a30786458 --- /dev/null +++ b/Tests/test_000_sanity.py @@ -0,0 +1,24 @@ +from __future__ import print_function +from tester import * + +import PIL +import PIL.Image + +# Make sure we have the binary extension +im = PIL.Image.core.new("L", (100, 100)) + +assert PIL.Image.VERSION[:3] == '1.1' + +# Create an image and do stuff with it. +im = PIL.Image.new("1", (100, 100)) +assert (im.mode, im.size) == ('1', (100, 100)) +assert len(im.tobytes()) == 1300 + +# Create images in all remaining major modes. +im = PIL.Image.new("L", (100, 100)) +im = PIL.Image.new("P", (100, 100)) +im = PIL.Image.new("RGB", (100, 100)) +im = PIL.Image.new("I", (100, 100)) +im = PIL.Image.new("F", (100, 100)) + +print("ok") diff --git a/Tests/test_001_archive.py b/Tests/test_001_archive.py new file mode 100644 index 000000000..a914a6c4c --- /dev/null +++ b/Tests/test_001_archive.py @@ -0,0 +1,23 @@ +import PIL +import PIL.Image + +import glob, os + +for file in glob.glob("../pil-archive/*"): + f, e = os.path.splitext(file) + if e in [".txt", ".ttf", ".otf", ".zip"]: + continue + try: + im = PIL.Image.open(file) + im.load() + except IOError as v: + print("-", "failed to open", file, "-", v) + else: + print("+", file, im.mode, im.size, im.format) + if e == ".exif": + try: + info = im._getexif() + except KeyError as v: + print("-", "failed to get exif info from", file, "-", v) + +print("ok") diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py new file mode 100644 index 000000000..e0584641c --- /dev/null +++ b/Tests/test_file_bmp.py @@ -0,0 +1,27 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.bmp") + + lena().save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "BMP") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py new file mode 100644 index 000000000..e9d051b11 --- /dev/null +++ b/Tests/test_file_eps.py @@ -0,0 +1,108 @@ +from tester import * + +from PIL import Image, EpsImagePlugin +import sys +import io + +if EpsImagePlugin.gs_windows_binary == False: + # already checked. Not there. + skip() + +if not sys.platform.startswith('win'): + import subprocess + try: + gs = subprocess.Popen(['gs','--version'], stdout=subprocess.PIPE) + gs.stdout.read() + except OSError: + # no ghostscript + skip() + +#Our two EPS test files (they are identical except for their bounding boxes) +file1 = "Tests/images/zero_bb.eps" +file2 = "Tests/images/non_zero_bb.eps" + +#Due to palletization, we'll need to convert these to RGB after load +file1_compare = "Tests/images/zero_bb.png" +file1_compare_scale2 = "Tests/images/zero_bb_scale2.png" + +file2_compare = "Tests/images/non_zero_bb.png" +file2_compare_scale2 = "Tests/images/non_zero_bb_scale2.png" + +def test_sanity(): + #Regular scale + image1 = Image.open(file1) + image1.load() + assert_equal(image1.mode, "RGB") + assert_equal(image1.size, (460, 352)) + assert_equal(image1.format, "EPS") + + image2 = Image.open(file2) + image2.load() + assert_equal(image2.mode, "RGB") + assert_equal(image2.size, (360, 252)) + assert_equal(image2.format, "EPS") + + #Double scale + image1_scale2 = Image.open(file1) + image1_scale2.load(scale=2) + assert_equal(image1_scale2.mode, "RGB") + assert_equal(image1_scale2.size, (920, 704)) + assert_equal(image1_scale2.format, "EPS") + + image2_scale2 = Image.open(file2) + image2_scale2.load(scale=2) + assert_equal(image2_scale2.mode, "RGB") + assert_equal(image2_scale2.size, (720, 504)) + assert_equal(image2_scale2.format, "EPS") + +def test_file_object(): + #issue 479 + image1 = Image.open(file1) + with open(tempfile('temp_file.eps'), 'wb') as fh: + image1.save(fh, 'EPS') + +def test_iobase_object(): + #issue 479 + image1 = Image.open(file1) + with io.open(tempfile('temp_iobase.eps'), 'wb') as fh: + image1.save(fh, 'EPS') + +def test_render_scale1(): + #We need png support for these render test + codecs = dir(Image.core) + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zip/deflate support not available") + + #Zero bounding box + image1_scale1 = Image.open(file1) + image1_scale1.load() + image1_scale1_compare = Image.open(file1_compare).convert("RGB") + image1_scale1_compare.load() + assert_image_similar(image1_scale1, image1_scale1_compare, 5) + + #Non-Zero bounding box + image2_scale1 = Image.open(file2) + image2_scale1.load() + image2_scale1_compare = Image.open(file2_compare).convert("RGB") + image2_scale1_compare.load() + assert_image_similar(image2_scale1, image2_scale1_compare, 10) + +def test_render_scale2(): + #We need png support for these render test + codecs = dir(Image.core) + if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zip/deflate support not available") + + #Zero bounding box + image1_scale2 = Image.open(file1) + image1_scale2.load(scale=2) + image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB") + image1_scale2_compare.load() + assert_image_similar(image1_scale2, image1_scale2_compare, 5) + + #Non-Zero bounding box + image2_scale2 = Image.open(file2) + image2_scale2.load(scale=2) + image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB") + image2_scale2_compare.load() + assert_image_similar(image2_scale2, image2_scale2_compare, 10) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py new file mode 100644 index 000000000..4e06a732e --- /dev/null +++ b/Tests/test_file_fli.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.fli" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "FLI") diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py new file mode 100644 index 000000000..3a6478e2a --- /dev/null +++ b/Tests/test_file_gif.py @@ -0,0 +1,48 @@ +from tester import * + +from PIL import Image + +codecs = dir(Image.core) + +if "gif_encoder" not in codecs or "gif_decoder" not in codecs: + skip("gif support not available") # can this happen? + +# sample gif stream +file = "Images/lena.gif" +with open(file, "rb") as f: + data = f.read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "GIF") + +def test_optimize(): + def test(optimize): + im = Image.new("L", (1, 1), 0) + file = BytesIO() + im.save(file, "GIF", optimize=optimize) + return len(file.getvalue()) + assert_equal(test(0), 800) + assert_equal(test(1), 38) + +def test_roundtrip(): + out = tempfile('temp.gif') + im = lena() + im.save(out) + reread = Image.open(out) + + assert_image_similar(reread.convert('RGB'), im, 50) + +def test_roundtrip2(): + #see https://github.com/python-imaging/Pillow/issues/403 + out = 'temp.gif'#tempfile('temp.gif') + im = Image.open('Images/lena.gif') + im2 = im.copy() + im2.save(out) + reread = Image.open(out) + + assert_image_similar(reread.convert('RGB'), lena(), 50) + diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py new file mode 100644 index 000000000..e0db34acc --- /dev/null +++ b/Tests/test_file_ico.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ico" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (16, 16)) + assert_equal(im.format, "ICO") diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py new file mode 100644 index 000000000..de1c3f0e1 --- /dev/null +++ b/Tests/test_file_jpeg.py @@ -0,0 +1,193 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +codecs = dir(Image.core) + +if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: + skip("jpeg support not available") + +# sample jpeg stream +file = "Images/lena.jpg" +data = open(file, "rb").read() + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "JPEG", **options) + bytes = out.tell() + out.seek(0) + im = Image.open(out) + im.bytes = bytes # for testing only + return im + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.jpeglib_version, "\d+\.\d+$") + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + +# -------------------------------------------------------------------- + +def test_app(): + # Test APP/COM reader (@PIL135) + im = Image.open(file) + assert_equal(im.applist[0], + ("APP0", b"JFIF\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00")) + assert_equal(im.applist[1], ("COM", b"Python Imaging Library")) + assert_equal(len(im.applist), 2) + +def test_cmyk(): + # Test CMYK handling. Thanks to Tim and Charlie for test data, + # Michael for getting me to look one more time. + file = "Tests/images/pil_sample_cmyk.jpg" + im = Image.open(file) + # the source image has red pixels in the upper left corner. + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + # the opposite corner is black + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + # roundtrip, and check again + im = roundtrip(im) + c, m, y, k = [x / 255.0 for x in im.getpixel((0, 0))] + assert_true(c == 0.0 and m > 0.8 and y > 0.8 and k == 0.0) + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0]-1, im.size[1]-1))] + assert_true(k > 0.9) + +def test_dpi(): + def test(xdpi, ydpi=None): + im = Image.open(file) + im = roundtrip(im, dpi=(xdpi, ydpi or xdpi)) + return im.info.get("dpi") + assert_equal(test(72), (72, 72)) + assert_equal(test(300), (300, 300)) + assert_equal(test(100, 200), (100, 200)) + assert_equal(test(0), None) # square pixels + +def test_icc(): + # Test ICC support + im1 = Image.open("Tests/images/rgb.jpg") + icc_profile = im1.info["icc_profile"] + assert_equal(len(icc_profile), 3144) + # Roundtrip via physical file. + file = tempfile("temp.jpg") + im1.save(file, icc_profile=icc_profile) + im2 = Image.open(file) + assert_equal(im2.info.get("icc_profile"), icc_profile) + # Roundtrip via memory buffer. + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), icc_profile=icc_profile) + assert_image_equal(im1, im2) + assert_false(im1.info.get("icc_profile")) + assert_true(im2.info.get("icc_profile")) + +def test_icc_big(): + # Make sure that the "extra" support handles large blocks + def test(n): + # The ICC APP marker can store 65519 bytes per marker, so + # using a 4-byte test code should allow us to detect out of + # order issues. + icc_profile = (b"Test"*int(n/4+1))[:n] + assert len(icc_profile) == n # sanity + im1 = roundtrip(lena(), icc_profile=icc_profile) + assert_equal(im1.info.get("icc_profile"), icc_profile or None) + test(0); test(1) + test(3); test(4); test(5) + test(65533-14) # full JPEG marker block + test(65533-14+1) # full block plus one byte + test(ImageFile.MAXBLOCK) # full buffer block + test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK*4+3) # large block + +def test_optimize(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), optimize=1) + assert_image_equal(im1, im2) + assert_true(im1.bytes >= im2.bytes) + +def test_optimize_large_buffer(): + #https://github.com/python-imaging/Pillow/issues/148 + f = tempfile('temp.jpg') + # this requires ~ 1.5x Image.MAXBLOCK + im = Image.new("RGB", (4096,4096), 0xff3333) + im.save(f, format="JPEG", optimize=True) + +def test_progressive(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), progressive=True) + assert_image_equal(im1, im2) + assert_true(im1.bytes >= im2.bytes) + +def test_progressive_large_buffer(): + f = tempfile('temp.jpg') + # this requires ~ 1.5x Image.MAXBLOCK + im = Image.new("RGB", (4096,4096), 0xff3333) + im.save(f, format="JPEG", progressive=True) + +def test_large_exif(): + #https://github.com/python-imaging/Pillow/issues/148 + f = tempfile('temp.jpg') + im = lena() + im.save(f,'JPEG', quality=90, exif=b"1"*65532) + +def test_progressive(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), progressive=1) + im3 = roundtrip(lena(), progression=1) # compatibility + assert_image_equal(im1, im2) + assert_image_equal(im1, im3) + assert_false(im1.info.get("progressive")) + assert_false(im1.info.get("progression")) + assert_true(im2.info.get("progressive")) + assert_true(im2.info.get("progression")) + assert_true(im3.info.get("progressive")) + assert_true(im3.info.get("progression")) + +def test_quality(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), quality=50) + assert_image(im1, im2.mode, im2.size) + assert_true(im1.bytes >= im2.bytes) + +def test_smooth(): + im1 = roundtrip(lena()) + im2 = roundtrip(lena(), smooth=100) + assert_image(im1, im2.mode, im2.size) + +def test_subsampling(): + def getsampling(im): + layer = im.layer + return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] + # experimental API + im = roundtrip(lena(), subsampling=-1) # default + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=0) # 4:4:4 + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=1) # 4:2:2 + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=2) # 4:1:1 + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling=3) # default (undefined) + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + im = roundtrip(lena(), subsampling="4:4:4") + assert_equal(getsampling(im), (1, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:2:2") + assert_equal(getsampling(im), (2, 1, 1, 1, 1, 1)) + im = roundtrip(lena(), subsampling="4:1:1") + assert_equal(getsampling(im), (2, 2, 1, 1, 1, 1)) + + assert_exception(TypeError, lambda: roundtrip(lena(), subsampling="1:1:1")) + +def test_exif(): + im = Image.open("Tests/images/pil_sample_rgb.jpg") + info = im._getexif() + assert_equal(info[305], 'Adobe Photoshop CS Macintosh') diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py new file mode 100644 index 000000000..a94257bc0 --- /dev/null +++ b/Tests/test_file_libtiff.py @@ -0,0 +1,297 @@ +from tester import * + +from PIL import Image, TiffImagePlugin + +codecs = dir(Image.core) + +if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: + skip("tiff support not available") + +def _assert_noerr(im): + """Helper tests that assert basic sanity about the g4 tiff reading""" + #1 bit + assert_equal(im.mode, "1") + + # Does the data actually load + assert_no_exception(lambda: im.load()) + assert_no_exception(lambda: im.getdata()) + + try: + assert_equal(im._compression, 'group4') + except: + print("No _compression") + print (dir(im)) + + # can we write it back out, in a different form. + out = tempfile("temp.png") + assert_no_exception(lambda: im.save(out)) + +def test_g4_tiff(): + """Test the ordinary file path load path""" + + file = "Tests/images/lena_g4_500.tif" + im = Image.open(file) + + assert_equal(im.size, (500,500)) + _assert_noerr(im) + +def test_g4_large(): + file = "Tests/images/pport_g4.tif" + im = Image.open(file) + _assert_noerr(im) + +def test_g4_tiff_file(): + """Testing the string load path""" + + file = "Tests/images/lena_g4_500.tif" + with open(file,'rb') as f: + im = Image.open(f) + + assert_equal(im.size, (500,500)) + _assert_noerr(im) + +def test_g4_tiff_bytesio(): + """Testing the stringio loading code path""" + from io import BytesIO + file = "Tests/images/lena_g4_500.tif" + s = BytesIO() + with open(file,'rb') as f: + s.write(f.read()) + s.seek(0) + im = Image.open(s) + + assert_equal(im.size, (500,500)) + _assert_noerr(im) + +def test_g4_eq_png(): + """ Checking that we're actually getting the data that we expect""" + png = Image.open('Tests/images/lena_bw_500.png') + g4 = Image.open('Tests/images/lena_g4_500.tif') + + assert_image_equal(g4, png) + +# see https://github.com/python-imaging/Pillow/issues/279 +def test_g4_fillorder_eq_png(): + """ Checking that we're actually getting the data that we expect""" + png = Image.open('Tests/images/g4-fillorder-test.png') + g4 = Image.open('Tests/images/g4-fillorder-test.tif') + + assert_image_equal(g4, png) + +def test_g4_write(): + """Checking to see that the saved image is the same as what we wrote""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) + + out = tempfile("temp.tif") + rot = orig.transpose(Image.ROTATE_90) + assert_equal(rot.size,(500,500)) + rot.save(out) + + reread = Image.open(out) + assert_equal(reread.size,(500,500)) + _assert_noerr(reread) + assert_image_equal(reread, rot) + assert_equal(reread.info['compression'], 'group4') + + assert_equal(reread.info['compression'], orig.info['compression']) + + assert_false(orig.tobytes() == reread.tobytes()) + +def test_adobe_deflate_tiff(): + file = "Tests/images/tiff_adobe_deflate.tif" + im = Image.open(file) + + assert_equal(im.mode, "RGB") + assert_equal(im.size, (278, 374)) + assert_equal(im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) + assert_no_exception(lambda: im.load()) + +def test_write_metadata(): + """ Test metadata writing through libtiff """ + img = Image.open('Tests/images/lena_g4.tif') + f = tempfile('temp.tiff') + + img.save(f, tiffinfo = img.tag) + + loaded = Image.open(f) + + original = img.tag.named() + reloaded = loaded.tag.named() + + # PhotometricInterpretation is set from SAVE_INFO, not the original image. + ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'PhotometricInterpretation'] + + for tag, value in reloaded.items(): + if tag not in ignored: + if tag.endswith('Resolution'): + val = original[tag] + assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1], + msg="%s didn't roundtrip" % tag) + else: + assert_equal(original[tag], value, "%s didn't roundtrip" % tag) + + for tag, value in original.items(): + if tag not in ignored: + if tag.endswith('Resolution'): + val = reloaded[tag] + assert_almost_equal(val[0][0]/val[0][1], value[0][0]/value[0][1], + msg="%s didn't roundtrip" % tag) + else: + assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) + + +def test_g3_compression(): + i = Image.open('Tests/images/lena_g4_500.tif') + out = tempfile("temp.tif") + i.save(out, compression='group3') + + reread = Image.open(out) + assert_equal(reread.info['compression'], 'group3') + assert_image_equal(reread, i) + +def test_little_endian(): + im = Image.open('Tests/images/16bit.deflate.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + + + out = tempfile("temp.tif") + #out = "temp.le.tif" + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + # UNDONE - libtiff defaults to writing in native endian, so + # on big endian, we'll get back mode = 'I;16B' here. + +def test_big_endian(): + im = Image.open('Tests/images/16bit.MM.deflate.tif') + + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + + out = tempfile("temp.tif") + im.save(out) + reread = Image.open(out) + + assert_equal(reread.info['compression'], im.info['compression']) + assert_equal(reread.getpixel((0,0)), 480) + +def test_g4_string_info(): + """Tests String data in info directory""" + file = "Tests/images/lena_g4_500.tif" + orig = Image.open(file) + + out = tempfile("temp.tif") + + orig.tag[269] = 'temp.tif' + orig.save(out) + + reread = Image.open(out) + assert_equal('temp.tif', reread.tag[269]) + +def test_12bit_rawmode(): + """ Are we generating the same interpretation of the image as Imagemagick is? """ + TiffImagePlugin.READ_LIBTIFF = True + #Image.DEBUG = True + im = Image.open('Tests/images/12bit.cropped.tif') + im.load() + TiffImagePlugin.READ_LIBTIFF = False + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. + + im2 = Image.open('Tests/images/12in16bit.tif') + + if Image.DEBUG: + print (im.getpixel((0,0))) + print (im.getpixel((0,1))) + print (im.getpixel((0,2))) + + print (im2.getpixel((0,0))) + print (im2.getpixel((0,1))) + print (im2.getpixel((0,2))) + + assert_image_equal(im, im2) + +def test_blur(): + # test case from irc, how to do blur on b/w image and save to compressed tif. + from PIL import ImageFilter + out = tempfile('temp.tif') + im = Image.open('Tests/images/pport_g4.tif') + im = im.convert('L') + + im=im.filter(ImageFilter.GaussianBlur(4)) + im.save(out, compression='tiff_adobe_deflate') + + im2 = Image.open(out) + im2.load() + + assert_image_equal(im, im2) + + +def test_compressions(): + im = lena('RGB') + out = tempfile('temp.tif') + + TiffImagePlugin.READ_LIBTIFF = True + TiffImagePlugin.WRITE_LIBTIFF = True + + for compression in ('packbits', 'tiff_lzw'): + im.save(out, compression=compression) + im2 = Image.open(out) + assert_image_equal(im, im2) + + im.save(out, compression='jpeg') + im2 = Image.open(out) + assert_image_similar(im, im2, 30) + + TiffImagePlugin.READ_LIBTIFF = False + TiffImagePlugin.WRITE_LIBTIFF = False + + + + +def test_cmyk_save(): + im = lena('CMYK') + out = tempfile('temp.tif') + + im.save(out, compression='tiff_adobe_deflate') + im2 = Image.open(out) + assert_image_equal(im, im2) + +def xtest_bw_compression_wRGB(): + """ This test passes, but when running all tests causes a failure due to + output on stderr from the error thrown by libtiff. We need to capture that + but not now""" + + im = lena('RGB') + out = tempfile('temp.tif') + + assert_exception(IOError, lambda: im.save(out, compression='tiff_ccitt')) + assert_exception(IOError, lambda: im.save(out, compression='group3')) + assert_exception(IOError, lambda: im.save(out, compression='group4')) + diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py new file mode 100644 index 000000000..2ad71d6e6 --- /dev/null +++ b/Tests/test_file_libtiff_small.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image + +from test_file_libtiff import _assert_noerr + +codecs = dir(Image.core) + +if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: + skip("tiff support not available") + +""" The small lena image was failing on open in the libtiff + decoder because the file pointer was set to the wrong place + by a spurious seek. It wasn't failing with the byteio method. + + It was fixed by forcing an lseek to the beginning of the + file just before reading in libtiff. These tests remain + to ensure that it stays fixed. """ + + +def test_g4_lena_file(): + """Testing the open file load path""" + + file = "Tests/images/lena_g4.tif" + with open(file,'rb') as f: + im = Image.open(f) + + assert_equal(im.size, (128,128)) + _assert_noerr(im) + +def test_g4_lena_bytesio(): + """Testing the bytesio loading code path""" + from io import BytesIO + file = "Tests/images/lena_g4.tif" + s = BytesIO() + with open(file,'rb') as f: + s.write(f.read()) + s.seek(0) + im = Image.open(s) + + assert_equal(im.size, (128,128)) + _assert_noerr(im) + +def test_g4_lena(): + """The 128x128 lena image fails for some reason. Investigating""" + + file = "Tests/images/lena_g4.tif" + im = Image.open(file) + + assert_equal(im.size, (128,128)) + _assert_noerr(im) + diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py new file mode 100644 index 000000000..7ed200e43 --- /dev/null +++ b/Tests/test_file_msp.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.msp") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "MSP") diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py new file mode 100644 index 000000000..73d358229 --- /dev/null +++ b/Tests/test_file_pcx.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.pcx") + + lena("1").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "1") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PCX") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + +def test_pil184(): + # Check reading of files where xmin/xmax is not zero. + + file = "Tests/images/pil184.pcx" + im = Image.open(file) + + assert_equal(im.size, (447, 144)) + assert_equal(im.tile[0][1], (0, 0, 447, 144)) + + # Make sure all pixels are either 0 or 255. + assert_equal(im.histogram()[0] + im.histogram()[255], 447*144) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py new file mode 100644 index 000000000..6a5954b79 --- /dev/null +++ b/Tests/test_file_png.py @@ -0,0 +1,252 @@ +from tester import * + +from PIL import Image +from PIL import PngImagePlugin +import zlib + +codecs = dir(Image.core) + +if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zip/deflate support not available") + +# sample png stream + +file = "Images/lena.png" +data = open(file, "rb").read() + +# stuff to create inline PNG images + +MAGIC = PngImagePlugin._MAGIC + +def chunk(cid, *data): + file = BytesIO() + PngImagePlugin.putchunk(*(file, cid) + data) + return file.getvalue() + +o32 = PngImagePlugin.o32 + +IHDR = chunk(b"IHDR", o32(1), o32(1), b'\x08\x02', b'\0\0\0') +IDAT = chunk(b"IDAT") +IEND = chunk(b"IEND") + +HEAD = MAGIC + IHDR +TAIL = IDAT + IEND + +def load(data): + return Image.open(BytesIO(data)) + +def roundtrip(im, **options): + out = BytesIO() + im.save(out, "PNG", **options) + out.seek(0) + return Image.open(out) + +# -------------------------------------------------------------------- + +def test_sanity(): + + # internal version number + assert_match(Image.core.zlib_version, "\d+\.\d+\.\d+(\.\d+)?$") + + file = tempfile("temp.png") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +# -------------------------------------------------------------------- + +def test_broken(): + # Check reading of totally broken files. In this case, the test + # file was checked into Subversion as a text file. + + file = "Tests/images/broken.png" + assert_exception(IOError, lambda: Image.open(file)) + +def test_bad_text(): + # Make sure PIL can read malformed tEXt chunks (@PIL152) + + im = load(HEAD + chunk(b'tEXt') + TAIL) + assert_equal(im.info, {}) + + im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) + assert_equal(im.info, {'spam': 'egg'}) + + im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) + assert_equal(im.info, {'spam': 'egg\x00'}) + +def test_bad_ztxt(): + # Test reading malformed zTXt chunks (python-imaging/Pillow#318) + + im = load(HEAD + chunk(b'zTXt') + TAIL) + assert_equal(im.info, {}) + + im = load(HEAD + chunk(b'zTXt', b'spam') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'zTXt', b'spam\0') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'zTXt', b'spam\0\0') + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')[:1]) + TAIL) + assert_equal(im.info, {'spam': ''}) + + im = load(HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')) + TAIL) + assert_equal(im.info, {'spam': 'egg'}) + +def test_interlace(): + + file = "Tests/images/pil123p.png" + im = Image.open(file) + + assert_image(im, "P", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + + file = "Tests/images/pil123rgba.png" + im = Image.open(file) + + assert_image(im, "RGBA", (162, 150)) + assert_true(im.info.get("interlace")) + + assert_no_exception(lambda: im.load()) + +def test_load_transparent_p(): + file = "Tests/images/pil123p.png" + im = Image.open(file) + + assert_image(im, "P", (162, 150)) + im = im.convert("RGBA") + assert_image(im, "RGBA", (162, 150)) + + # image has 124 uniqe qlpha values + assert_equal(len(im.split()[3].getcolors()), 124) + +def test_load_transparent_rgb(): + file = "Tests/images/rgb_trns.png" + im = Image.open(file) + + assert_image(im, "RGB", (64, 64)) + im = im.convert("RGBA") + assert_image(im, "RGBA", (64, 64)) + + # image has 876 transparent pixels + assert_equal(im.split()[3].getcolors()[0][0], 876) + +def test_save_p_transparent_palette(): + in_file = "Tests/images/pil123p.png" + im = Image.open(in_file) + + file = tempfile("temp.png") + assert_no_exception(lambda: im.save(file)) + +def test_save_p_single_transparency(): + in_file = "Tests/images/p_trns_single.png" + im = Image.open(in_file) + + file = tempfile("temp.png") + assert_no_exception(lambda: im.save(file)) + +def test_save_l_transparency(): + in_file = "Tests/images/l_trns.png" + im = Image.open(in_file) + + file = tempfile("temp.png") + assert_no_exception(lambda: im.save(file)) + + # There are 559 transparent pixels. + im = im.convert('RGBA') + assert_equal(im.split()[3].getcolors()[0][0], 559) + +def test_save_rgb_single_transparency(): + in_file = "Tests/images/caption_6_33_22.png" + im = Image.open(in_file) + + file = tempfile("temp.png") + assert_no_exception(lambda: im.save(file)) + +def test_load_verify(): + # Check open/load/verify exception (@PIL150) + + im = Image.open("Images/lena.png") + assert_no_exception(lambda: im.verify()) + + im = Image.open("Images/lena.png") + im.load() + assert_exception(RuntimeError, lambda: im.verify()) + +def test_roundtrip_dpi(): + # Check dpi roundtripping + + im = Image.open(file) + + im = roundtrip(im, dpi=(100, 100)) + assert_equal(im.info["dpi"], (100, 100)) + +def test_roundtrip_text(): + # Check text roundtripping + + im = Image.open(file) + + info = PngImagePlugin.PngInfo() + info.add_text("TXT", "VALUE") + info.add_text("ZIP", "VALUE", 1) + + im = roundtrip(im, pnginfo=info) + assert_equal(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + assert_equal(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + +def test_scary(): + # Check reading of evil PNG file. For information, see: + # http://scary.beasts.org/security/CESA-2004-001.txt + # The first byte is removed from pngtest_bad.png + # to avoid classification as malware. + + with open("Tests/images/pngtest_bad.png.bin", 'rb') as fd: + data = b'\x89' + fd.read() + + pngfile = BytesIO(data) + assert_exception(IOError, lambda: Image.open(pngfile)) + +def test_trns_rgb(): + # Check writing and reading of tRNS chunks for RGB images. + # Independent file sample provided by Sebastian Spaeth. + + file = "Tests/images/caption_6_33_22.png" + im = Image.open(file) + assert_equal(im.info["transparency"], (248, 248, 248)) + + # check saving transparency by default + im = roundtrip(im) + assert_equal(im.info["transparency"], (248, 248, 248)) + + im = roundtrip(im, transparency=(0, 1, 2)) + assert_equal(im.info["transparency"], (0, 1, 2)) diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py new file mode 100644 index 000000000..fccb94905 --- /dev/null +++ b/Tests/test_file_ppm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.ppm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PPM") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py new file mode 100644 index 000000000..ef2d40594 --- /dev/null +++ b/Tests/test_file_psd.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.psd" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PSD") diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py new file mode 100644 index 000000000..fa33d3802 --- /dev/null +++ b/Tests/test_file_tar.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image, TarIO + +codecs = dir(Image.core) +if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: + skip("neither jpeg nor zip support not available") + +# sample ppm stream +tarfile = "Images/lena.tar" + +def test_sanity(): + if "zip_decoder" in codecs: + tar = TarIO.TarIO(tarfile, 'lena.png') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "PNG") + + if "jpeg_decoder" in codecs: + tar = TarIO.TarIO(tarfile, 'lena.jpg') + im = Image.open(tar) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "JPEG") + diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py new file mode 100644 index 000000000..9041b2046 --- /dev/null +++ b/Tests/test_file_tiff.py @@ -0,0 +1,130 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + file = tempfile("temp.tif") + + lena("RGB").save(file) + + im = Image.open(file) + im.load() + assert_equal(im.mode, "RGB") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "TIFF") + + lena("1").save(file) + im = Image.open(file) + + lena("L").save(file) + im = Image.open(file) + + lena("P").save(file) + im = Image.open(file) + + lena("RGB").save(file) + im = Image.open(file) + + lena("I").save(file) + im = Image.open(file) + +def test_mac_tiff(): + # Read RGBa images from Mac OS X [@PIL136] + + file = "Tests/images/pil136.tiff" + im = Image.open(file) + + assert_equal(im.mode, "RGBA") + assert_equal(im.size, (55, 43)) + assert_equal(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) + assert_no_exception(lambda: im.load()) + +def test_gimp_tiff(): + # Read TIFF JPEG images from GIMP [@PIL168] + + codecs = dir(Image.core) + if "jpeg_decoder" not in codecs: + skip("jpeg support not available") + + file = "Tests/images/pil168.tif" + im = Image.open(file) + + assert_equal(im.mode, "RGB") + assert_equal(im.size, (256, 256)) + assert_equal(im.tile, [ + ('jpeg', (0, 0, 256, 64), 8, ('RGB', '')), + ('jpeg', (0, 64, 256, 128), 1215, ('RGB', '')), + ('jpeg', (0, 128, 256, 192), 2550, ('RGB', '')), + ('jpeg', (0, 192, 256, 256), 3890, ('RGB', '')), + ]) + assert_no_exception(lambda: im.load()) + +def test_xyres_tiff(): + from PIL.TiffImagePlugin import X_RESOLUTION, Y_RESOLUTION + file = "Tests/images/pil168.tif" + im = Image.open(file) + assert isinstance(im.tag.tags[X_RESOLUTION][0], tuple) + assert isinstance(im.tag.tags[Y_RESOLUTION][0], tuple) + #Try to read a file where X,Y_RESOLUTION are ints + im.tag.tags[X_RESOLUTION] = (72,) + im.tag.tags[Y_RESOLUTION] = (72,) + im._setup() + assert_equal(im.info['dpi'], (72., 72.)) + + +def test_little_endian(): + im = Image.open('Tests/images/16bit.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16') + + b = im.tobytes() + # Bytes are in image native order (little endian) + if py3: + assert_equal(b[0], ord(b'\xe0')) + assert_equal(b[1], ord(b'\x01')) + else: + assert_equal(b[0], b'\xe0') + assert_equal(b[1], b'\x01') + + +def test_big_endian(): + im = Image.open('Tests/images/16bit.MM.cropped.tif') + assert_equal(im.getpixel((0,0)), 480) + assert_equal(im.mode, 'I;16B') + + b = im.tobytes() + + # Bytes are in image native order (big endian) + if py3: + assert_equal(b[0], ord(b'\x01')) + assert_equal(b[1], ord(b'\xe0')) + else: + assert_equal(b[0], b'\x01') + assert_equal(b[1], b'\xe0') + + +def test_12bit_rawmode(): + """ Are we generating the same interpretation of the image as Imagemagick is? """ + + #Image.DEBUG = True + im = Image.open('Tests/images/12bit.cropped.tif') + + # to make the target -- + # convert 12bit.cropped.tif -depth 16 tmp.tif + # convert tmp.tif -evaluate RightShift 4 12in16bit2.tif + # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, + # so we need to unshift so that the integer values are the same. + + im2 = Image.open('Tests/images/12in16bit.tif') + + if Image.DEBUG: + print (im.getpixel((0,0))) + print (im.getpixel((0,1))) + print (im.getpixel((0,2))) + + print (im2.getpixel((0,0))) + print (im2.getpixel((0,1))) + print (im2.getpixel((0,2))) + + assert_image_equal(im, im2) diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py new file mode 100644 index 000000000..354eb972f --- /dev/null +++ b/Tests/test_file_tiff_metadata.py @@ -0,0 +1,80 @@ +from tester import * +from PIL import Image, TiffImagePlugin, TiffTags + +tag_ids = dict(zip(TiffTags.TAGS.values(), TiffTags.TAGS.keys())) + +def test_rt_metadata(): + """ Test writing arbitray metadata into the tiff image directory + Use case is ImageJ private tags, one numeric, one arbitrary + data. https://github.com/python-imaging/Pillow/issues/291 + """ + + img = lena() + + textdata = "This is some arbitrary metadata for a text field" + info = TiffImagePlugin.ImageFileDirectory() + + info[tag_ids['ImageJMetaDataByteCounts']] = len(textdata) + info[tag_ids['ImageJMetaData']] = textdata + + f = tempfile("temp.tif") + + img.save(f, tiffinfo=info) + + loaded = Image.open(f) + + assert_equal(loaded.tag[50838], (len(textdata),)) + assert_equal(loaded.tag[50839], textdata) + +def test_read_metadata(): + img = Image.open('Tests/images/lena_g4.tif') + + known = {'YResolution': ((1207959552, 16777216),), + 'PlanarConfiguration': (1,), + 'BitsPerSample': (1,), + 'ImageLength': (128,), + 'Compression': (4,), + 'FillOrder': (1,), + 'DocumentName': 'lena.g4.tif', + 'RowsPerStrip': (128,), + 'ResolutionUnit': (1,), + 'PhotometricInterpretation': (0,), + 'PageNumber': (0, 1), + 'XResolution': ((1207959552, 16777216),), + 'ImageWidth': (128,), + 'Orientation': (1,), + 'StripByteCounts': (1796,), + 'SamplesPerPixel': (1,), + 'StripOffsets': (8,), + 'Software': 'ImageMagick 6.5.7-8 2012-08-17 Q16 http://www.imagemagick.org'} + + # assert_equal is equivalent, but less helpful in telling what's wrong. + named = img.tag.named() + for tag, value in named.items(): + assert_equal(known[tag], value) + + for tag, value in known.items(): + assert_equal(value, named[tag]) + + +def test_write_metadata(): + """ Test metadata writing through the python code """ + img = Image.open('Tests/images/lena.tif') + + f = tempfile('temp.tiff') + img.save(f, tiffinfo = img.tag) + + loaded = Image.open(f) + + original = img.tag.named() + reloaded = loaded.tag.named() + + ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', 'StripOffsets'] + + for tag, value in reloaded.items(): + if tag not in ignored: + assert_equal(original[tag], value, "%s didn't roundtrip" % tag) + + for tag, value in original.items(): + if tag not in ignored: + assert_equal(value, reloaded[tag], "%s didn't roundtrip" % tag) diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py new file mode 100644 index 000000000..a89dea3c4 --- /dev/null +++ b/Tests/test_file_webp.py @@ -0,0 +1,68 @@ +from tester import * + +from PIL import Image + + +try: + from PIL import _webp +except: + skip('webp support not installed') + + +def test_version(): + assert_no_exception(lambda: _webp.WebPDecoderVersion()) + assert_no_exception(lambda: _webp.WebPDecoderBuggyAlpha()) + +def test_read_rgb(): + + file_path = "Images/lena.webp" + image = Image.open(file_path) + + assert_equal(image.mode, "RGB") + assert_equal(image.size, (128, 128)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + # generated with: dwebp -ppm ../../Images/lena.webp -o lena_webp_bits.ppm + target = Image.open('Tests/images/lena_webp_bits.ppm') + assert_image_similar(image, target, 20.0) + + +def test_write_rgb(): + """ + Can we write a RGB mode file to webp without error. Does it have the bits we + expect? + + """ + + temp_file = tempfile("temp.webp") + + lena("RGB").save(temp_file) + + image = Image.open(temp_file) + image.load() + + assert_equal(image.mode, "RGB") + assert_equal(image.size, (128, 128)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + # If we're using the exact same version of webp, this test should pass. + # but it doesn't if the webp is generated on Ubuntu and tested on Fedora. + + # generated with: dwebp -ppm temp.webp -o lena_webp_write.ppm + #target = Image.open('Tests/images/lena_webp_write.ppm') + #assert_image_equal(image, target) + + # This test asserts that the images are similar. If the average pixel difference + # between the two images is less than the epsilon value, then we're going to + # accept that it's a reasonable lossy version of the image. The included lena images + # for webp are showing ~16 on Ubuntu, the jpegs are showing ~18. + target = lena("RGB") + assert_image_similar(image, target, 20.0) + + + + diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py new file mode 100644 index 000000000..5ac03b9d4 --- /dev/null +++ b/Tests/test_file_webp_alpha.py @@ -0,0 +1,82 @@ +from tester import * + +from PIL import Image + +try: + from PIL import _webp +except: + skip('webp support not installed') + + +if _webp.WebPDecoderBuggyAlpha(): + skip("Buggy early version of webp installed, not testing transparency") + +def test_read_rgba(): + # Generated with `cwebp transparent.png -o transparent.webp` + file_path = "Images/transparent.webp" + image = Image.open(file_path) + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, (200, 150)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + orig_bytes = image.tobytes() + + target = Image.open('Images/transparent.png') + assert_image_similar(image, target, 20.0) + + +def test_write_lossless_rgb(): + temp_file = tempfile("temp.webp") + #temp_file = "temp.webp" + + pil_image = lena('RGBA') + + mask = Image.new("RGBA", (64, 64), (128,128,128,128)) + pil_image.paste(mask, (0,0), mask) # add some partially transparent bits. + + pil_image.save(temp_file, lossless=True) + + image = Image.open(temp_file) + image.load() + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, pil_image.size) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + + assert_image_equal(image, pil_image) + +def test_write_rgba(): + """ + Can we write a RGBA mode file to webp without error. Does it have the bits we + expect? + + """ + + temp_file = tempfile("temp.webp") + + pil_image = Image.new("RGBA", (10, 10), (255, 0, 0, 20)) + pil_image.save(temp_file) + + if _webp.WebPDecoderBuggyAlpha(): + return + + image = Image.open(temp_file) + image.load() + + assert_equal(image.mode, "RGBA") + assert_equal(image.size, (10, 10)) + assert_equal(image.format, "WEBP") + assert_no_exception(image.load) + assert_no_exception(image.getdata) + + assert_image_similar(image, pil_image, 1.0) + + + + diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py new file mode 100644 index 000000000..ca2b5af19 --- /dev/null +++ b/Tests/test_file_webp_lossless.py @@ -0,0 +1,33 @@ +from tester import * + +from PIL import Image + + +try: + from PIL import _webp +except: + skip('webp support not installed') + + +if (_webp.WebPDecoderVersion() < 0x0200): + skip('lossless not included') + +def test_write_lossless_rgb(): + temp_file = tempfile("temp.webp") + + lena("RGB").save(temp_file, lossless=True) + + image = Image.open(temp_file) + image.load() + + assert_equal(image.mode, "RGB") + assert_equal(image.size, (128, 128)) + assert_equal(image.format, "WEBP") + assert_no_exception(lambda: image.load()) + assert_no_exception(lambda: image.getdata()) + + + assert_image_equal(image, lena("RGB")) + + + diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py new file mode 100644 index 000000000..b4146c3ee --- /dev/null +++ b/Tests/test_file_webp_metadata.py @@ -0,0 +1,101 @@ +from tester import * + +from PIL import Image + +try: + from PIL import _webp + if not _webp.HAVE_WEBPMUX: + skip('webpmux support not installed') +except: + skip('webp support not installed') + + + +def test_read_exif_metadata(): + + file_path = "Images/flower.webp" + image = Image.open(file_path) + + assert_equal(image.format, "WEBP") + exif_data = image.info.get("exif", None) + assert_true(exif_data) + + exif = image._getexif() + + #camera make + assert_equal(exif[271], "Canon") + + jpeg_image = Image.open('Tests/images/flower.jpg') + expected_exif = jpeg_image.info['exif'] + + assert_equal(exif_data, expected_exif) + + +def test_write_exif_metadata(): + file_path = "Tests/images/flower.jpg" + image = Image.open(file_path) + expected_exif = image.info['exif'] + + buffer = BytesIO() + + image.save(buffer, "webp", exif=expected_exif) + + buffer.seek(0) + webp_image = Image.open(buffer) + + webp_exif = webp_image.info.get('exif', None) + assert_true(webp_exif) + if webp_exif: + assert_equal(webp_exif, expected_exif, "Webp Exif didn't match") + + +def test_read_icc_profile(): + + file_path = "Images/flower2.webp" + image = Image.open(file_path) + + assert_equal(image.format, "WEBP") + assert_true(image.info.get("icc_profile", None)) + + icc = image.info['icc_profile'] + + jpeg_image = Image.open('Tests/images/flower2.jpg') + expected_icc = jpeg_image.info['icc_profile'] + + assert_equal(icc, expected_icc) + + +def test_write_icc_metadata(): + file_path = "Tests/images/flower2.jpg" + image = Image.open(file_path) + expected_icc_profile = image.info['icc_profile'] + + buffer = BytesIO() + + image.save(buffer, "webp", icc_profile=expected_icc_profile) + + buffer.seek(0) + webp_image = Image.open(buffer) + + webp_icc_profile = webp_image.info.get('icc_profile', None) + + assert_true(webp_icc_profile) + if webp_icc_profile: + assert_equal(webp_icc_profile, expected_icc_profile, "Webp ICC didn't match") + + +def test_read_no_exif(): + file_path = "Tests/images/flower.jpg" + image = Image.open(file_path) + expected_exif = image.info['exif'] + + buffer = BytesIO() + + image.save(buffer, "webp") + + buffer.seek(0) + webp_image = Image.open(buffer) + + assert_false(webp_image._getexif()) + + diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py new file mode 100644 index 000000000..f27a3a349 --- /dev/null +++ b/Tests/test_file_xbm.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +PIL151 = b""" +#define basic_width 32 +#define basic_height 32 +static char basic_bits[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x80, 0xff, 0xff, 0x01, 0x40, 0x00, 0x00, 0x02, +0x20, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x08, +0x20, 0x00, 0x00, 0x04, +0x20, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x02, +0x80, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, +}; +""" + +def test_pil151(): + + im = Image.open(BytesIO(PIL151)) + + assert_no_exception(lambda: im.load()) + assert_equal(im.mode, '1') + assert_equal(im.size, (32, 32)) diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py new file mode 100644 index 000000000..44135d028 --- /dev/null +++ b/Tests/test_file_xpm.py @@ -0,0 +1,14 @@ +from tester import * + +from PIL import Image + +# sample ppm stream +file = "Images/lena.xpm" +data = open(file, "rb").read() + +def test_sanity(): + im = Image.open(file) + im.load() + assert_equal(im.mode, "P") + assert_equal(im.size, (128, 128)) + assert_equal(im.format, "XPM") diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py new file mode 100644 index 000000000..366bb4468 --- /dev/null +++ b/Tests/test_font_bdf.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image, FontFile, BdfFontFile + +filename = "Images/courB08.bdf" + +def test_sanity(): + + file = open(filename, "rb") + font = BdfFontFile.BdfFontFile(file) + + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 190) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py new file mode 100644 index 000000000..60e6e0e26 --- /dev/null +++ b/Tests/test_font_pcf.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image, FontFile, PcfFontFile +from PIL import ImageFont, ImageDraw + +codecs = dir(Image.core) + +if "zip_encoder" not in codecs or "zip_decoder" not in codecs: + skip("zlib support not available") + +fontname = "Tests/fonts/helvO18.pcf" +tempname = tempfile("temp.pil", "temp.pbm") + +message = "hello, world" + +def test_sanity(): + + file = open(fontname, "rb") + font = PcfFontFile.PcfFontFile(file) + assert_true(isinstance(font, FontFile.FontFile)) + assert_equal(len([_f for _f in font.glyph if _f]), 192) + + font.save(tempname) + +def test_draw(): + + font = ImageFont.load(tempname) + image = Image.new("L", font.getsize(message), "white") + draw = ImageDraw.Draw(image) + draw.text((0, 0), message, font=font) + # assert_signature(image, "7216c60f988dea43a46bb68321e3c1b03ec62aee") diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py new file mode 100644 index 000000000..371b06a0b --- /dev/null +++ b/Tests/test_format_lab.py @@ -0,0 +1,41 @@ +from tester import * + +from PIL import Image + +def test_white(): + i = Image.open('Tests/images/lab.tif') + + bits = i.load() + + assert_equal(i.mode, 'LAB') + + assert_equal(i.getbands(), ('L','A', 'B')) + + k = i.getpixel((0,0)) + assert_equal(k, (255,128,128)) + + L = i.getdata(0) + a = i.getdata(1) + b = i.getdata(2) + + assert_equal(list(L), [255]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + + +def test_green(): + # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS + # == RGB: 0, 152, 117 + i = Image.open('Tests/images/lab-green.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,28,128)) + + +def test_red(): + # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS + # == RGB: 255, 0, 124 + i = Image.open('Tests/images/lab-red.tif') + + k = i.getpixel((0,0)) + assert_equal(k, (128,228,128)) diff --git a/Tests/test_image.py b/Tests/test_image.py new file mode 100644 index 000000000..26c699e66 --- /dev/null +++ b/Tests/test_image.py @@ -0,0 +1,39 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = Image.new("L", (100, 100)) + assert_equal(repr(im)[:45], " 8 bit lut for converting I->L images + see https://github.com/python-imaging/Pillow/issues/440 + """ + + im = lena("I") + assert_no_exception(lambda: im.point(list(range(256))*256, 'L')) diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py new file mode 100644 index 000000000..b23f69834 --- /dev/null +++ b/Tests/test_image_putalpha.py @@ -0,0 +1,43 @@ +from tester import * + +from PIL import Image + +def test_interface(): + + im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 0)) + + im = Image.new("RGBA", (1, 1), (1, 2, 3)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 255)) + + im.putalpha(Image.new("L", im.size, 4)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + + im.putalpha(5) + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 5)) + +def test_promote(): + + im = Image.new("L", (1, 1), 1) + assert_equal(im.getpixel((0, 0)), 1) + + im.putalpha(2) + assert_equal(im.mode, 'LA') + assert_equal(im.getpixel((0, 0)), (1, 2)) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + assert_equal(im.getpixel((0, 0)), (1, 2, 3)) + + im.putalpha(4) + assert_equal(im.mode, 'RGBA') + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) + +def test_readonly(): + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.readonly = 1 + + im.putalpha(4) + assert_false(im.readonly) + assert_equal(im.mode, 'RGBA') + assert_equal(im.getpixel((0, 0)), (1, 2, 3, 4)) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py new file mode 100644 index 000000000..e25359fdf --- /dev/null +++ b/Tests/test_image_putdata.py @@ -0,0 +1,40 @@ +from tester import * + +import sys + +from PIL import Image + +def test_sanity(): + + im1 = lena() + + data = list(im1.getdata()) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.putdata(data) + + assert_image_equal(im1, im2) + + # readonly + im2 = Image.new(im1.mode, im2.size, 0) + im2.readonly = 1 + im2.putdata(data) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + +def test_long_integers(): + # see bug-200802-systemerror + def put(value): + im = Image.new("RGBA", (1, 1)) + im.putdata([value]) + return im.getpixel((0, 0)) + assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) + assert_equal(put(0xFFFFFFFF), (255, 255, 255, 255)) + assert_equal(put(-1), (255, 255, 255, 255)) + assert_equal(put(-1), (255, 255, 255, 255)) + if sys.maxsize > 2**32: + assert_equal(put(sys.maxsize), (255, 255, 255, 255)) + else: + assert_equal(put(sys.maxsize), (255, 255, 255, 127)) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py new file mode 100644 index 000000000..b7ebb8853 --- /dev/null +++ b/Tests/test_image_putpalette.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +def test_putpalette(): + def palette(mode): + im = lena(mode).copy() + im.putpalette(list(range(256))*3) + p = im.getpalette() + if p: + return im.mode, p[:10] + return im.mode + assert_exception(ValueError, lambda: palette("1")) + assert_equal(palette("L"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_equal(palette("P"), ("P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + assert_exception(ValueError, lambda: palette("I")) + assert_exception(ValueError, lambda: palette("F")) + assert_exception(ValueError, lambda: palette("RGB")) + assert_exception(ValueError, lambda: palette("RGBA")) + assert_exception(ValueError, lambda: palette("YCbCr")) + +def test_imagepalette(): + im = lena("P") + assert_no_exception(lambda: im.putpalette(ImagePalette.negative())) + assert_no_exception(lambda: im.putpalette(ImagePalette.random())) + assert_no_exception(lambda: im.putpalette(ImagePalette.sepia())) + assert_no_exception(lambda: im.putpalette(ImagePalette.wedge())) diff --git a/Tests/test_image_putpixel.py b/Tests/test_image_putpixel.py new file mode 100644 index 000000000..2b60bbd97 --- /dev/null +++ b/Tests/test_image_putpixel.py @@ -0,0 +1,43 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im1 = lena() + im2 = Image.new(im1.mode, im1.size, 0) + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + im2.readonly = 1 + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pos = x, y + im2.putpixel(pos, im1.getpixel(pos)) + + assert_false(im2.readonly) + assert_image_equal(im1, im2) + + im2 = Image.new(im1.mode, im1.size, 0) + + pix1 = im1.load() + pix2 = im2.load() + + for y in range(im1.size[1]): + for x in range(im1.size[0]): + pix2[x, y] = pix1[x, y] + + assert_image_equal(im1, im2) + + + + +# see test_image_getpixel for more tests + diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py new file mode 100644 index 000000000..70b5eb503 --- /dev/null +++ b/Tests/test_image_quantize.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + + im = im.quantize() + assert_image(im, "P", im.size) + + im = lena() + im = im.quantize(palette=lena("P")) + assert_image(im, "P", im.size) + +def test_octree_quantize(): + im = lena() + + im = im.quantize(100, Image.FASTOCTREE) + assert_image(im, "P", im.size) + + assert len(im.getcolors()) == 100 \ No newline at end of file diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py new file mode 100644 index 000000000..4e228a396 --- /dev/null +++ b/Tests/test_image_resize.py @@ -0,0 +1,12 @@ +from tester import * + +from PIL import Image + +def test_resize(): + def resize(mode, size): + out = lena(mode).resize(size) + assert_equal(out.mode, mode) + assert_equal(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(resize, mode, (100, 100)) + yield_test(resize, mode, (200, 200)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py new file mode 100644 index 000000000..5e4782c87 --- /dev/null +++ b/Tests/test_image_rotate.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_rotate(): + def rotate(mode): + im = lena(mode) + out = im.rotate(45) + assert_equal(out.mode, mode) + assert_equal(out.size, im.size) # default rotate clips output + out = im.rotate(45, expand=1) + assert_equal(out.mode, mode) + assert_true(out.size != im.size) + for mode in "1", "P", "L", "RGB", "I", "F": + yield_test(rotate, mode) diff --git a/Tests/test_image_save.py b/Tests/test_image_save.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_save.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_seek.py b/Tests/test_image_seek.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_seek.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_show.py b/Tests/test_image_show.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_show.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py new file mode 100644 index 000000000..07a779664 --- /dev/null +++ b/Tests/test_image_split.py @@ -0,0 +1,49 @@ +from tester import * + +from PIL import Image + +def test_split(): + def split(mode): + layers = lena(mode).split() + return [(i.mode, i.size[0], i.size[1]) for i in layers] + assert_equal(split("1"), [('1', 128, 128)]) + assert_equal(split("L"), [('L', 128, 128)]) + assert_equal(split("I"), [('I', 128, 128)]) + assert_equal(split("F"), [('F', 128, 128)]) + assert_equal(split("P"), [('P', 128, 128)]) + assert_equal(split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("RGBA"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("CMYK"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + assert_equal(split("YCbCr"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + +def test_split_merge(): + def split_merge(mode): + return Image.merge(mode, lena(mode).split()) + assert_image_equal(lena("1"), split_merge("1")) + assert_image_equal(lena("L"), split_merge("L")) + assert_image_equal(lena("I"), split_merge("I")) + assert_image_equal(lena("F"), split_merge("F")) + assert_image_equal(lena("P"), split_merge("P")) + assert_image_equal(lena("RGB"), split_merge("RGB")) + assert_image_equal(lena("RGBA"), split_merge("RGBA")) + assert_image_equal(lena("CMYK"), split_merge("CMYK")) + assert_image_equal(lena("YCbCr"), split_merge("YCbCr")) + +def test_split_open(): + codecs = dir(Image.core) + + if 'zip_encoder' in codecs: + file = tempfile("temp.png") + else: + file = tempfile("temp.pcx") + + def split_open(mode): + lena(mode).save(file) + im = Image.open(file) + return len(im.split()) + assert_equal(split_open("1"), 1) + assert_equal(split_open("L"), 1) + assert_equal(split_open("P"), 1) + assert_equal(split_open("RGB"), 3) + if 'zip_encoder' in codecs: + assert_equal(split_open("RGBA"), 4) diff --git a/Tests/test_image_tell.py b/Tests/test_image_tell.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_tell.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py new file mode 100644 index 000000000..871dd1f54 --- /dev/null +++ b/Tests/test_image_thumbnail.py @@ -0,0 +1,36 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + im = lena() + im.thumbnail((100, 100)) + + assert_image(im, im.mode, (100, 100)) + +def test_aspect(): + + im = lena() + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((128, 256)) + im.thumbnail((50, 100)) + assert_image(im, im.mode, (50, 100)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((256, 128)) + im.thumbnail((100, 50)) + assert_image(im, im.mode, (100, 50)) + + im = lena().resize((128, 128)) + im.thumbnail((100, 100)) + assert_image(im, im.mode, (100, 100)) diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py new file mode 100644 index 000000000..6fb10dd53 --- /dev/null +++ b/Tests/test_image_tobitmap.py @@ -0,0 +1,15 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + + assert_exception(ValueError, lambda: lena().tobitmap()) + assert_no_exception(lambda: lena().convert("1").tobitmap()) + + im1 = lena().convert("1") + + bitmap = im1.tobitmap() + + assert_true(isinstance(bitmap, bytes)) + assert_image_equal(im1, fromstring(bitmap)) diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py new file mode 100644 index 000000000..d42399993 --- /dev/null +++ b/Tests/test_image_tobytes.py @@ -0,0 +1,7 @@ +from tester import * + +from PIL import Image + +def test_sanity(): + data = lena().tobytes() + assert_true(isinstance(data, bytes)) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py new file mode 100644 index 000000000..fdee6072f --- /dev/null +++ b/Tests/test_image_transform.py @@ -0,0 +1,116 @@ +from tester import * + +from PIL import Image + +def test_extent(): + im = lena('RGB') + (w,h) = im.size + transformed = im.transform(im.size, Image.EXTENT, + (0,0, + w//2,h//2), # ul -> lr + Image.BILINEAR) + + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) + + assert_image_similar(transformed, scaled, 10) # undone -- precision? + +def test_quad(): + # one simple quad transform, equivalent to scale & crop upper left quad + im = lena('RGB') + (w,h) = im.size + transformed = im.transform(im.size, Image.QUAD, + (0,0,0,h//2, + w//2,h//2,w//2,0), # ul -> ccw around quad + Image.BILINEAR) + + scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0,0,w,h)) + + assert_image_equal(transformed, scaled) + +def test_mesh(): + # this should be a checkerboard of halfsized lenas in ul, lr + im = lena('RGBA') + (w,h) = im.size + transformed = im.transform(im.size, Image.MESH, + [((0,0,w//2,h//2), # box + (0,0,0,h, + w,h,w,0)), # ul -> ccw around quad + ((w//2,h//2,w,h), # box + (0,0,0,h, + w,h,w,0))], # ul -> ccw around quad + Image.BILINEAR) + + #transformed.save('transformed.png') + + scaled = im.resize((w//2, h//2), Image.BILINEAR) + + checker = Image.new('RGBA', im.size) + checker.paste(scaled, (0,0)) + checker.paste(scaled, (w//2,h//2)) + + assert_image_equal(transformed, checker) + + # now, check to see that the extra area is (0,0,0,0) + blank = Image.new('RGBA', (w//2,h//2), (0,0,0,0)) + + assert_image_equal(blank, transformed.crop((w//2,0,w,h//2))) + assert_image_equal(blank, transformed.crop((0,h//2,w//2,h))) + +def _test_alpha_premult(op): + # create image with half white, half black, with the black half transparent. + # do op, + # there should be no darkness in the white section. + im = Image.new('RGBA', (10,10), (0,0,0,0)); + im2 = Image.new('RGBA', (5,10), (255,255,255,255)); + im.paste(im2, (0,0)) + + im = op(im, (40,10)) + im_background = Image.new('RGB', (40,10), (255,255,255)) + im_background.paste(im, (0,0), im) + + hist = im_background.histogram() + assert_equal(40*10, hist[-1]) + + +def test_alpha_premult_resize(): + + def op (im, sz): + return im.resize(sz, Image.LINEAR) + + _test_alpha_premult(op) + +def test_alpha_premult_transform(): + + def op(im, sz): + (w,h) = im.size + return im.transform(sz, Image.EXTENT, + (0,0, + w,h), + Image.BILINEAR) + + _test_alpha_premult(op) + + +def test_blank_fill(): + # attempting to hit + # https://github.com/python-imaging/Pillow/issues/254 reported + # + # issue is that transforms with transparent overflow area + # contained junk from previous images, especially on systems with + # constrained memory. So, attempt to fill up memory with a + # pattern, free it, and then run the mesh test again. Using a 1Mp + # image with 4 bands, for 4 megs of data allocated, x 64. OMM (64 + # bit 12.04 VM with 512 megs available, this fails with Pillow < + # a0eaf06cc5f62a6fb6de556989ac1014ff3348ea + # + # Running by default, but I'd totally understand not doing it in + # the future + + foo = [Image.new('RGBA',(1024,1024), (a,a,a,a)) + for a in range(1,65)] + + # Yeah. Watch some JIT optimize this out. + foo = None + + test_mesh() diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py new file mode 100644 index 000000000..43b3ef9d3 --- /dev/null +++ b/Tests/test_image_transpose.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image + +FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT +FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM +ROTATE_90 = Image.ROTATE_90 +ROTATE_180 = Image.ROTATE_180 +ROTATE_270 = Image.ROTATE_270 + +def test_sanity(): + + im = lena() + + assert_no_exception(lambda: im.transpose(FLIP_LEFT_RIGHT)) + assert_no_exception(lambda: im.transpose(FLIP_TOP_BOTTOM)) + + assert_no_exception(lambda: im.transpose(ROTATE_90)) + assert_no_exception(lambda: im.transpose(ROTATE_180)) + assert_no_exception(lambda: im.transpose(ROTATE_270)) + +def test_roundtrip(): + + im = lena() + + def transpose(first, second): + return im.transpose(first).transpose(second) + + assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + + assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) + assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) + diff --git a/Tests/test_image_verify.py b/Tests/test_image_verify.py new file mode 100644 index 000000000..7d4b6d9b3 --- /dev/null +++ b/Tests/test_image_verify.py @@ -0,0 +1,5 @@ +from tester import * + +from PIL import Image + +success() diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py new file mode 100644 index 000000000..16eaaf55e --- /dev/null +++ b/Tests/test_imagechops.py @@ -0,0 +1,56 @@ +from tester import * + +from PIL import Image +from PIL import ImageChops + +def test_sanity(): + + im = lena("L") + + ImageChops.constant(im, 128) + ImageChops.duplicate(im) + ImageChops.invert(im) + ImageChops.lighter(im, im) + ImageChops.darker(im, im) + ImageChops.difference(im, im) + ImageChops.multiply(im, im) + ImageChops.screen(im, im) + + ImageChops.add(im, im) + ImageChops.add(im, im, 2.0) + ImageChops.add(im, im, 2.0, 128) + ImageChops.subtract(im, im) + ImageChops.subtract(im, im, 2.0) + ImageChops.subtract(im, im, 2.0, 128) + + ImageChops.add_modulo(im, im) + ImageChops.subtract_modulo(im, im) + + ImageChops.blend(im, im, 0.5) + ImageChops.composite(im, im, im) + + ImageChops.offset(im, 10) + ImageChops.offset(im, 10, 20) + +def test_logical(): + + def table(op, a, b): + out = [] + for x in (a, b): + imx = Image.new("1", (1, 1), x) + for y in (a, b): + imy = Image.new("1", (1, 1), y) + out.append(op(imx, imy).getpixel((0, 0))) + return tuple(out) + + assert_equal(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + + assert_equal(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + assert_equal(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + assert_equal(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py new file mode 100644 index 000000000..d18132598 --- /dev/null +++ b/Tests/test_imagecms.py @@ -0,0 +1,159 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageCms +except ImportError: + skip() + +SRGB = "Tests/icc/sRGB.icm" + +def test_sanity(): + + # basic smoke test. + # this mostly follows the cms_test outline. + + v = ImageCms.versions() # should return four strings + assert_equal(v[0], '1.0.0 pil') + assert_equal(list(map(type, v)), [str, str, str, str]) + + # internal version number + assert_match(ImageCms.core.littlecms_version, "\d+\.\d+$") + + i = ImageCms.profileToProfile(lena(), SRGB, SRGB) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + p = ImageCms.createProfile("sRGB") + o = ImageCms.getOpenProfile(SRGB) + t = ImageCms.buildTransformFromOpenProfiles(p, o, "RGB", "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + t = ImageCms.buildProofTransform(SRGB, SRGB, SRGB, "RGB", "RGB") + assert_equal(t.inputMode, "RGB") + assert_equal(t.outputMode, "RGB") + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "RGB", (128, 128)) + + # test PointTransform convenience API + im = lena().point(t) + +def test_name(): + # get profile information for file + assert_equal(ImageCms.getProfileName(SRGB).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') +def x_test_info(): + assert_equal(ImageCms.getProfileInfo(SRGB).splitlines(), + ['sRGB IEC61966-2.1', '', + 'Copyright (c) 1998 Hewlett-Packard Company', '', + 'WhitePoint : D65 (daylight)', '', + 'Tests/icc/sRGB.icm']) + +def test_intent(): + assert_equal(ImageCms.getDefaultIntent(SRGB), 0) + assert_equal(ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + +def test_profile_object(): + # same, using profile object + p = ImageCms.createProfile("sRGB") +# assert_equal(ImageCms.getProfileName(p).strip(), +# 'sRGB built-in - (lcms internal)') +# assert_equal(ImageCms.getProfileInfo(p).splitlines(), +# ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + assert_equal(ImageCms.getDefaultIntent(p), 0) + assert_equal(ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, + ImageCms.DIRECTION_INPUT), 1) + +def test_extensions(): + # extensions + i = Image.open("Tests/images/rgb.jpg") + p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) + assert_equal(ImageCms.getProfileName(p).strip(), + 'IEC 61966-2.1 Default RGB colour space - sRGB') + +def test_exceptions(): + # the procedural pyCMS API uses PyCMSError for all sorts of errors + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.profileToProfile(lena(), "foo", "bar")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.buildTransform("foo", "bar", "RGB", "RGB")) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.getProfileName(None)) + assert_exception(ImageCms.PyCMSError, lambda: ImageCms.isIntentSupported(SRGB, None, None)) + + +def test_display_profile(): + # try fetching the profile for the current display device + assert_no_exception(lambda: ImageCms.get_display_profile()) + + +def test_lab_color_profile(): + pLab = ImageCms.createProfile("LAB", 5000) + pLab = ImageCms.createProfile("LAB", 6500) + +def test_simple_lab(): + i = Image.new('RGB', (10,10), (128,128,128)) + + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + i_lab = ImageCms.applyTransform(i, t) + + + assert_equal(i_lab.mode, 'LAB') + + k = i_lab.getpixel((0,0)) + assert_equal(k, (137,128,128)) # not a linear luminance map. so L != 128 + + L = i_lab.getdata(0) + a = i_lab.getdata(1) + b = i_lab.getdata(2) + + assert_equal(list(L), [137]*100) + assert_equal(list(a), [128]*100) + assert_equal(list(b), [128]*100) + + +def test_lab_color(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + # need to add a type mapping for some PIL type to TYPE_Lab_8 in findLCMSType, + # and have that mapping work back to a PIL mode. (likely RGB) + i = ImageCms.applyTransform(lena(), t) + assert_image(i, "LAB", (128, 128)) + + # i.save('temp.lab.tif') # visually verified vs PS. + + target = Image.open('Tests/images/lena.Lab.tif') + + assert_image_similar(i, target, 30) + +def test_lab_srgb(): + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + img = Image.open('Tests/images/lena.Lab.tif') + + img_srgb = ImageCms.applyTransform(img, t) + + # img_srgb.save('temp.srgb.tif') # visually verified vs ps. + + assert_image_similar(lena(), img_srgb, 30) + +def test_lab_roundtrip(): + # check to see if we're at least internally consistent. + pLab = ImageCms.createProfile("LAB") + t = ImageCms.buildTransform(SRGB, pLab, "RGB", "LAB") + + t2 = ImageCms.buildTransform(pLab, SRGB, "LAB", "RGB") + + i = ImageCms.applyTransform(lena(), t) + out = ImageCms.applyTransform(i, t2) + + assert_image_similar(lena(), out, 2) + + diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py new file mode 100644 index 000000000..23f21744a --- /dev/null +++ b/Tests/test_imagecolor.py @@ -0,0 +1,34 @@ +from tester import * + +from PIL import Image +from PIL import ImageColor + +# -------------------------------------------------------------------- +# sanity + +assert_equal((255, 0, 0), ImageColor.getrgb("#f00")) +assert_equal((255, 0, 0), ImageColor.getrgb("#ff0000")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255,0,0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(255, 0, 0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("rgb(100%,0%,0%)")) +assert_equal((255, 0, 0), ImageColor.getrgb("hsl(0, 100%, 50%)")) +assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255,0,0,0)")) +assert_equal((255, 0, 0, 0), ImageColor.getrgb("rgba(255, 0, 0, 0)")) +assert_equal((255, 0, 0), ImageColor.getrgb("red")) + +# -------------------------------------------------------------------- +# look for rounding errors (based on code by Tim Hatch) + +for color in list(ImageColor.colormap.keys()): + expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = Image.new("L", (1, 1), color).getpixel((0, 0)) + assert_equal(expected, actual) + +assert_equal((0, 0, 0), ImageColor.getcolor("black", "RGB")) +assert_equal((255, 255, 255), ImageColor.getcolor("white", "RGB")) + +assert_equal(0, ImageColor.getcolor("black", "L")) +assert_equal(255, ImageColor.getcolor("white", "L")) + +assert_equal(0, ImageColor.getcolor("black", "1")) +assert_equal(255, ImageColor.getcolor("white", "1")) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py new file mode 100644 index 000000000..f8b5c3c5c --- /dev/null +++ b/Tests/test_imagedraw.py @@ -0,0 +1,28 @@ +from tester import * + +from PIL import Image +from PIL import ImageDraw + +def test_sanity(): + + im = lena("RGB").copy() + + draw = ImageDraw.ImageDraw(im) + draw = ImageDraw.Draw(im) + + draw.ellipse(list(range(4))) + draw.line(list(range(10))) + draw.polygon(list(range(100))) + draw.rectangle(list(range(4))) + + success() + +def test_deprecated(): + + im = lena().copy() + + draw = ImageDraw.Draw(im) + + assert_warning(DeprecationWarning, lambda: draw.setink(0)) + assert_warning(DeprecationWarning, lambda: draw.setfill(0)) + diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py new file mode 100644 index 000000000..04f16bfa5 --- /dev/null +++ b/Tests/test_imageenhance.py @@ -0,0 +1,19 @@ +from tester import * + +from PIL import Image +from PIL import ImageEnhance + +def test_sanity(): + + # FIXME: assert_image + assert_no_exception(lambda: ImageEnhance.Color(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Contrast(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Brightness(lena()).enhance(0.5)) + assert_no_exception(lambda: ImageEnhance.Sharpness(lena()).enhance(0.5)) + +def test_crash(): + + # crashes on small images + im = Image.new("RGB", (1, 1)) + assert_no_exception(lambda: ImageEnhance.Sharpness(im).enhance(0.5)) + diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py new file mode 100644 index 000000000..2447dfb22 --- /dev/null +++ b/Tests/test_imagefile.py @@ -0,0 +1,71 @@ +from tester import * + +from PIL import Image +from PIL import ImageFile + +codecs = dir(Image.core) + +# save original block sizes +MAXBLOCK = ImageFile.MAXBLOCK +SAFEBLOCK = ImageFile.SAFEBLOCK + +def test_parser(): + + def roundtrip(format): + + im = lena("L").resize((1000, 1000)) + if format in ("MSP", "XBM"): + im = im.convert("1") + + file = BytesIO() + + im.save(file, format) + + data = file.getvalue() + + parser = ImageFile.Parser() + parser.feed(data) + imOut = parser.close() + + return im, imOut + + assert_image_equal(*roundtrip("BMP")) + assert_image_equal(*roundtrip("GIF")) + assert_image_equal(*roundtrip("IM")) + assert_image_equal(*roundtrip("MSP")) + if "zip_encoder" in codecs: + try: + # force multiple blocks in PNG driver + ImageFile.MAXBLOCK = 8192 + assert_image_equal(*roundtrip("PNG")) + finally: + ImageFile.MAXBLOCK = MAXBLOCK + assert_image_equal(*roundtrip("PPM")) + assert_image_equal(*roundtrip("TIFF")) + assert_image_equal(*roundtrip("XBM")) + #assert_image_equal(*roundtrip("EPS")) #no eps_decoder + assert_image_equal(*roundtrip("TGA")) + assert_image_equal(*roundtrip("PCX")) + + if "jpeg_encoder" in codecs: + im1, im2 = roundtrip("JPEG") # lossy compression + assert_image(im1, im2.mode, im2.size) + + # XXX Why assert exception and why does it fail? + # https://github.com/python-imaging/Pillow/issues/78 + #assert_exception(IOError, lambda: roundtrip("PDF")) + +def test_safeblock(): + + im1 = lena() + + if "zip_encoder" not in codecs: + skip("PNG (zlib) encoder not available") + + try: + ImageFile.SAFEBLOCK = 1 + im2 = fromstring(tostring(im1, "PNG")) + finally: + ImageFile.SAFEBLOCK = SAFEBLOCK + + assert_image_equal(im1, im2) diff --git a/Tests/test_imagefileio.py b/Tests/test_imagefileio.py new file mode 100644 index 000000000..c63f07bb0 --- /dev/null +++ b/Tests/test_imagefileio.py @@ -0,0 +1,24 @@ +from tester import * + +from PIL import Image +from PIL import ImageFileIO + +def test_fileio(): + + class DumbFile: + def __init__(self, data): + self.data = data + def read(self, bytes=None): + assert_equal(bytes, None) + return self.data + def close(self): + pass + + im1 = lena() + + io = ImageFileIO.ImageFileIO(DumbFile(tostring(im1, "PPM"))) + + im2 = Image.open(io) + assert_image_equal(im1, im2) + + diff --git a/Tests/test_imagefilter.py b/Tests/test_imagefilter.py new file mode 100644 index 000000000..214f88024 --- /dev/null +++ b/Tests/test_imagefilter.py @@ -0,0 +1,31 @@ +from tester import * + +from PIL import Image +from PIL import ImageFilter + +def test_sanity(): + # see test_image_filter for more tests + + assert_no_exception(lambda: ImageFilter.MaxFilter) + assert_no_exception(lambda: ImageFilter.MedianFilter) + assert_no_exception(lambda: ImageFilter.MinFilter) + assert_no_exception(lambda: ImageFilter.ModeFilter) + assert_no_exception(lambda: ImageFilter.Kernel((3, 3), list(range(9)))) + assert_no_exception(lambda: ImageFilter.GaussianBlur) + assert_no_exception(lambda: ImageFilter.GaussianBlur(5)) + assert_no_exception(lambda: ImageFilter.UnsharpMask) + assert_no_exception(lambda: ImageFilter.UnsharpMask(10)) + + assert_no_exception(lambda: ImageFilter.BLUR) + assert_no_exception(lambda: ImageFilter.CONTOUR) + assert_no_exception(lambda: ImageFilter.DETAIL) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE) + assert_no_exception(lambda: ImageFilter.EDGE_ENHANCE_MORE) + assert_no_exception(lambda: ImageFilter.EMBOSS) + assert_no_exception(lambda: ImageFilter.FIND_EDGES) + assert_no_exception(lambda: ImageFilter.SMOOTH) + assert_no_exception(lambda: ImageFilter.SMOOTH_MORE) + assert_no_exception(lambda: ImageFilter.SHARPEN) + + + diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py new file mode 100644 index 000000000..9ac2cdd89 --- /dev/null +++ b/Tests/test_imagefont.py @@ -0,0 +1,135 @@ +from tester import * + +from PIL import Image +from io import BytesIO +import os + +try: + from PIL import ImageFont + ImageFont.core.getfont # check if freetype is available +except ImportError: + skip() + +from PIL import ImageDraw + +font_path = "Tests/fonts/FreeMono.ttf" +font_size=20 + +def test_sanity(): + assert_match(ImageFont.core.freetype2_version, "\d+\.\d+\.\d+$") + +def test_font_with_name(): + assert_no_exception(lambda: ImageFont.truetype(font_path, font_size)) + assert_no_exception(lambda: _render(font_path)) + _clean() + +def _font_as_bytes(): + with open(font_path, 'rb') as f: + font_bytes = BytesIO(f.read()) + return font_bytes + +def test_font_with_filelike(): + assert_no_exception(lambda: ImageFont.truetype(_font_as_bytes(), font_size)) + assert_no_exception(lambda: _render(_font_as_bytes())) + # Usage note: making two fonts from the same buffer fails. + #shared_bytes = _font_as_bytes() + #assert_no_exception(lambda: _render(shared_bytes)) + #assert_exception(Exception, lambda: _render(shared_bytes)) + _clean() + +def test_font_with_open_file(): + with open(font_path, 'rb') as f: + assert_no_exception(lambda: _render(f)) + _clean() + +def test_font_old_parameters(): + assert_warning(DeprecationWarning, lambda: ImageFont.truetype(filename=font_path, size=font_size)) + +def _render(font): + txt = "Hello World!" + ttf = ImageFont.truetype(font, font_size) + w, h = ttf.getsize(txt) + img = Image.new("RGB", (256, 64), "white") + d = ImageDraw.Draw(img) + d.text((10, 10), txt, font=ttf, fill='black') + + img.save('font.png') + return img + +def _clean(): + os.unlink('font.png') + +def test_render_equal(): + img_path = _render(font_path) + with open(font_path, 'rb') as f: + font_filelike = BytesIO(f.read()) + img_filelike = _render(font_filelike) + + assert_image_equal(img_path, img_filelike) + _clean() + + +def test_render_multiline(): + im = Image.new(mode='RGB', size=(300,100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(font_path, font_size) + line_spacing = draw.textsize('A', font=ttf)[1] + 8 + lines = ['hey you', 'you are awesome', 'this looks awkward'] + y = 0 + for line in lines: + draw.text((0, y), line, font=ttf) + y += line_spacing + + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + # some versions of freetype have different horizontal spacing. + # setting a tight epsilon, I'm showing the original test failure + # at epsilon = ~38. + assert_image_similar(im, target_img,.5) + + +def test_rotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = Image.ROTATE_90 + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check (w,h) of box a is (h,w) of box b + assert_equal(box_size_a[0], box_size_b[1]) + assert_equal(box_size_a[1], box_size_b[0]) + + +def test_unrotated_transposed_font(): + img_grey = Image.new("L", (100, 100)) + draw = ImageDraw.Draw(img_grey) + word = "testing" + font = ImageFont.truetype(font_path, font_size) + + orientation = None + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) + + # Original font + draw.setfont(font) + box_size_a = draw.textsize(word) + + # Rotated font + draw.setfont(transposed_font) + box_size_b = draw.textsize(word) + + # Check boxes a and b are same size + assert_equal(box_size_a, box_size_b) + + diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py new file mode 100644 index 000000000..67ff71960 --- /dev/null +++ b/Tests/test_imagegrab.py @@ -0,0 +1,13 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageGrab +except ImportError as v: + skip(v) + +def test_grab(): + im = ImageGrab.grab() + assert_image(im, im.mode, im.size) + + diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py new file mode 100644 index 000000000..eaeb711ba --- /dev/null +++ b/Tests/test_imagemath.py @@ -0,0 +1,62 @@ +from tester import * + +from PIL import Image +from PIL import ImageMath + +def pixel(im): + if hasattr(im, "im"): + return "%s %r" % (im.mode, im.getpixel((0, 0))) + else: + if isinstance(im, type(0)): + return int(im) # hack to deal with booleans + print(im) + +A = Image.new("L", (1, 1), 1) +B = Image.new("L", (1, 1), 2) +F = Image.new("F", (1, 1), 3) +I = Image.new("I", (1, 1), 4) + +images = {"A": A, "B": B, "F": F, "I": I} + +def test_sanity(): + assert_equal(ImageMath.eval("1"), 1) + assert_equal(ImageMath.eval("1+A", A=2), 3) + assert_equal(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") + +def test_ops(): + + assert_equal(pixel(ImageMath.eval("-A", images)), "I -1") + assert_equal(pixel(ImageMath.eval("+B", images)), "L 2") + + assert_equal(pixel(ImageMath.eval("A+B", images)), "I 3") + assert_equal(pixel(ImageMath.eval("A-B", images)), "I -1") + assert_equal(pixel(ImageMath.eval("A*B", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A/B", images)), "I 0") + assert_equal(pixel(ImageMath.eval("B**2", images)), "I 4") + assert_equal(pixel(ImageMath.eval("B**33", images)), "I 2147483647") + + assert_equal(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") + assert_equal(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") + assert_equal(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") + assert_equal(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") + assert_equal(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") + assert_equal(pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + +def test_logical(): + assert_equal(pixel(ImageMath.eval("not A", images)), 0) + assert_equal(pixel(ImageMath.eval("A and B", images)), "L 2") + assert_equal(pixel(ImageMath.eval("A or B", images)), "L 1") + +def test_convert(): + assert_equal(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + assert_equal(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") + assert_equal(pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + +def test_compare(): + assert_equal(pixel(ImageMath.eval("min(A, B)", images)), "I 1") + assert_equal(pixel(ImageMath.eval("max(A, B)", images)), "I 2") + assert_equal(pixel(ImageMath.eval("A == 1", images)), "I 1") + assert_equal(pixel(ImageMath.eval("A == 2", images)), "I 0") diff --git a/Tests/test_imagemode.py b/Tests/test_imagemode.py new file mode 100644 index 000000000..54b04435f --- /dev/null +++ b/Tests/test_imagemode.py @@ -0,0 +1,23 @@ +from tester import * + +from PIL import Image +from PIL import ImageMode + +ImageMode.getmode("1") +ImageMode.getmode("L") +ImageMode.getmode("P") +ImageMode.getmode("RGB") +ImageMode.getmode("I") +ImageMode.getmode("F") + +m = ImageMode.getmode("1") +assert_equal(m.mode, "1") +assert_equal(m.bands, ("1",)) +assert_equal(m.basemode, "L") +assert_equal(m.basetype, "L") + +m = ImageMode.getmode("RGB") +assert_equal(m.mode, "RGB") +assert_equal(m.bands, ("R", "G", "B")) +assert_equal(m.basemode, "RGB") +assert_equal(m.basetype, "L") diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py new file mode 100644 index 000000000..8ed5ccefa --- /dev/null +++ b/Tests/test_imageops.py @@ -0,0 +1,81 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps + +class Deformer: + def getmesh(self, im): + x, y = im.size + return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] + +deformer = Deformer() + +def test_sanity(): + + ImageOps.autocontrast(lena("L")) + ImageOps.autocontrast(lena("RGB")) + + ImageOps.autocontrast(lena("L"), cutoff=10) + ImageOps.autocontrast(lena("L"), ignore=[0, 255]) + + ImageOps.colorize(lena("L"), (0, 0, 0), (255, 255, 255)) + ImageOps.colorize(lena("L"), "black", "white") + + ImageOps.crop(lena("L"), 1) + ImageOps.crop(lena("RGB"), 1) + + ImageOps.deform(lena("L"), deformer) + ImageOps.deform(lena("RGB"), deformer) + + ImageOps.equalize(lena("L")) + ImageOps.equalize(lena("RGB")) + + ImageOps.expand(lena("L"), 1) + ImageOps.expand(lena("RGB"), 1) + ImageOps.expand(lena("L"), 2, "blue") + ImageOps.expand(lena("RGB"), 2, "blue") + + ImageOps.fit(lena("L"), (128, 128)) + ImageOps.fit(lena("RGB"), (128, 128)) + + ImageOps.flip(lena("L")) + ImageOps.flip(lena("RGB")) + + ImageOps.grayscale(lena("L")) + ImageOps.grayscale(lena("RGB")) + + ImageOps.invert(lena("L")) + ImageOps.invert(lena("RGB")) + + ImageOps.mirror(lena("L")) + ImageOps.mirror(lena("RGB")) + + ImageOps.posterize(lena("L"), 4) + ImageOps.posterize(lena("RGB"), 4) + + ImageOps.solarize(lena("L")) + ImageOps.solarize(lena("RGB")) + + success() + +def test_1pxfit(): + # Division by zero in equalize if image is 1 pixel high + newimg = ImageOps.fit(lena("RGB").resize((1,1)), (35,35)) + assert_equal(newimg.size,(35,35)) + + newimg = ImageOps.fit(lena("RGB").resize((1,100)), (35,35)) + assert_equal(newimg.size,(35,35)) + + newimg = ImageOps.fit(lena("RGB").resize((100,1)), (35,35)) + assert_equal(newimg.size,(35,35)) + +def test_pil163(): + # Division by zero in equalize if < 255 pixels in image (@PIL163) + + i = lena("RGB").resize((15, 16)) + + ImageOps.equalize(i.convert("L")) + ImageOps.equalize(i.convert("P")) + ImageOps.equalize(i.convert("RGB")) + + success() diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py new file mode 100644 index 000000000..83a93b8e0 --- /dev/null +++ b/Tests/test_imageops_usm.py @@ -0,0 +1,55 @@ +from tester import * + +from PIL import Image +from PIL import ImageOps +from PIL import ImageFilter + +im = Image.open("Images/lena.ppm") + +def test_ops_api(): + + i = ImageOps.gaussian_blur(im, 2.0) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("blur.bmp") + + i = ImageOps.usm(im, 2.0, 125, 8) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + # i.save("usm.bmp") + +def test_filter_api(): + + filter = ImageFilter.GaussianBlur(2.0) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + + filter = ImageFilter.UnsharpMask(2.0, 125, 8) + i = im.filter(filter) + assert_equal(i.mode, "RGB") + assert_equal(i.size, (128, 128)) + +def test_usm(): + + usm = ImageOps.unsharp_mask + assert_exception(ValueError, lambda: usm(im.convert("1"))) + assert_no_exception(lambda: usm(im.convert("L"))) + assert_exception(ValueError, lambda: usm(im.convert("I"))) + assert_exception(ValueError, lambda: usm(im.convert("F"))) + assert_no_exception(lambda: usm(im.convert("RGB"))) + assert_no_exception(lambda: usm(im.convert("RGBA"))) + assert_no_exception(lambda: usm(im.convert("CMYK"))) + assert_exception(ValueError, lambda: usm(im.convert("YCbCr"))) + +def test_blur(): + + blur = ImageOps.gaussian_blur + assert_exception(ValueError, lambda: blur(im.convert("1"))) + assert_no_exception(lambda: blur(im.convert("L"))) + assert_exception(ValueError, lambda: blur(im.convert("I"))) + assert_exception(ValueError, lambda: blur(im.convert("F"))) + assert_no_exception(lambda: blur(im.convert("RGB"))) + assert_no_exception(lambda: blur(im.convert("RGBA"))) + assert_no_exception(lambda: blur(im.convert("CMYK"))) + assert_exception(ValueError, lambda: blur(im.convert("YCbCr"))) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py new file mode 100644 index 000000000..a22addda9 --- /dev/null +++ b/Tests/test_imagepalette.py @@ -0,0 +1,44 @@ +from tester import * + +from PIL import Image +from PIL import ImagePalette + +ImagePalette = ImagePalette.ImagePalette + +def test_sanity(): + + assert_no_exception(lambda: ImagePalette("RGB", list(range(256))*3)) + assert_exception(ValueError, lambda: ImagePalette("RGB", list(range(256))*2)) + +def test_getcolor(): + + palette = ImagePalette() + + map = {} + for i in range(256): + map[palette.getcolor((i, i, i))] = i + + assert_equal(len(map), 256) + assert_exception(ValueError, lambda: palette.getcolor((1, 2, 3))) + +def test_file(): + + palette = ImagePalette() + + file = tempfile("temp.lut") + + palette.save(file) + + from PIL.ImagePalette import load, raw + + p = load(file) + + # load returns raw palette information + assert_equal(len(p[0]), 768) + assert_equal(p[1], "RGB") + + p = raw(p[1], p[0]) + assert_true(isinstance(p, ImagePalette)) + + + diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py new file mode 100644 index 000000000..9e9e63c3a --- /dev/null +++ b/Tests/test_imagepath.py @@ -0,0 +1,54 @@ +from tester import * + +from PIL import Image +from PIL import ImagePath + +import array + +def test_path(): + + p = ImagePath.Path(list(range(10))) + + # sequence interface + assert_equal(len(p), 5) + assert_equal(p[0], (0.0, 1.0)) + assert_equal(p[-1], (8.0, 9.0)) + assert_equal(list(p[:1]), [(0.0, 1.0)]) + assert_equal(list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + + # method sanity check + assert_equal(p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + assert_equal(p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + + assert_equal(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) + + assert_equal(p.compact(5), 2) + assert_equal(list(p), [(0.0, 1.0), (4.0, 5.0), (8.0, 9.0)]) + + p.transform((1,0,1,0,1,1)) + assert_equal(list(p), [(1.0, 2.0), (5.0, 6.0), (9.0, 10.0)]) + + # alternative constructors + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0.0, 1.0]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([0, 1]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path([(0, 1)]) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(0)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(p.tolist(1)) + assert_equal(list(p), [(0.0, 1.0)]) + p = ImagePath.Path(array.array("f", [0, 1])) + assert_equal(list(p), [(0.0, 1.0)]) + + arr = array.array("f", [0, 1]) + if hasattr(arr, 'tobytes'): + p = ImagePath.Path(arr.tobytes()) + else: + p = ImagePath.Path(arr.tostring()) + assert_equal(list(p), [(0.0, 1.0)]) diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py new file mode 100644 index 000000000..73d1f4b1c --- /dev/null +++ b/Tests/test_imageqt.py @@ -0,0 +1,37 @@ +from tester import * + +from PIL import Image + +try: + from PyQt5.QtGui import QImage, qRgb, qRgba +except: + try: + from PyQt4.QtGui import QImage, qRgb, qRgba + except: + skip('PyQT4 or 5 not installed') + +from PIL import ImageQt + +def test_rgb(): + # from https://qt-project.org/doc/qt-4.8/qcolor.html + # typedef QRgb + # An ARGB quadruplet on the format #AARRGGBB, equivalent to an unsigned int. + + assert_equal(qRgb(0,0,0), qRgba(0,0,0,255)) + + def checkrgb(r,g,b): + val = ImageQt.rgb(r,g,b) + val = val % 2**24 # drop the alpha + assert_equal(val >> 16, r) + assert_equal(((val >> 8 ) % 2**8), g) + assert_equal(val % 2**8, b) + + checkrgb(0,0,0) + checkrgb(255,0,0) + checkrgb(0,255,0) + checkrgb(0,0,255) + + +def test_image(): + for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + assert_no_exception(lambda: ImageQt.ImageQt(lena(mode))) diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py new file mode 100644 index 000000000..3329b1a05 --- /dev/null +++ b/Tests/test_imagesequence.py @@ -0,0 +1,22 @@ +from tester import * + +from PIL import Image +from PIL import ImageSequence + +def test_sanity(): + + file = tempfile("temp.im") + + im = lena("RGB") + im.save(file) + + seq = ImageSequence.Iterator(im) + + index = 0 + for frame in seq: + assert_image_equal(im, frame) + assert_equal(im.tell(), index) + index = index + 1 + + assert_equal(index, 1) + diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py new file mode 100644 index 000000000..99ec005c8 --- /dev/null +++ b/Tests/test_imageshow.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageShow + +success() diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py new file mode 100644 index 000000000..02a461e22 --- /dev/null +++ b/Tests/test_imagestat.py @@ -0,0 +1,52 @@ +from tester import * + +from PIL import Image +from PIL import ImageStat + +def test_sanity(): + + im = lena() + + st = ImageStat.Stat(im) + st = ImageStat.Stat(im.histogram()) + st = ImageStat.Stat(im, Image.new("1", im.size, 1)) + + assert_no_exception(lambda: st.extrema) + assert_no_exception(lambda: st.sum) + assert_no_exception(lambda: st.mean) + assert_no_exception(lambda: st.median) + assert_no_exception(lambda: st.rms) + assert_no_exception(lambda: st.sum2) + assert_no_exception(lambda: st.var) + assert_no_exception(lambda: st.stddev) + assert_exception(AttributeError, lambda: st.spam) + + assert_exception(TypeError, lambda: ImageStat.Stat(1)) + +def test_lena(): + + im = lena() + + st = ImageStat.Stat(im) + + # verify a few values + assert_equal(st.extrema[0], (61, 255)) + assert_equal(st.median[0], 197) + assert_equal(st.sum[0], 2954416) + assert_equal(st.sum[1], 2027250) + assert_equal(st.sum[2], 1727331) + +def test_constant(): + + im = Image.new("L", (128, 128), 128) + + st = ImageStat.Stat(im) + + assert_equal(st.extrema[0], (128, 128)) + assert_equal(st.sum[0], 128**3) + assert_equal(st.sum2[0], 128**4) + assert_equal(st.mean[0], 128) + assert_equal(st.median[0], 128) + assert_equal(st.rms[0], 128) + assert_equal(st.var[0], 0) + assert_equal(st.stddev[0], 0) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py new file mode 100644 index 000000000..5c39c9283 --- /dev/null +++ b/Tests/test_imagetk.py @@ -0,0 +1,9 @@ +from tester import * + +from PIL import Image +try: + from PIL import ImageTk +except ImportError as v: + skip(v) + +success() diff --git a/Tests/test_imagetransform.py b/Tests/test_imagetransform.py new file mode 100644 index 000000000..884e6bb1c --- /dev/null +++ b/Tests/test_imagetransform.py @@ -0,0 +1,18 @@ +from tester import * + +from PIL import Image +from PIL import ImageTransform + +im = Image.new("L", (100, 100)) + +seq = tuple(range(10)) + +def test_sanity(): + transform = ImageTransform.AffineTransform(seq[:6]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.ExtentTransform(seq[:4]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.QuadTransform(seq[:8]) + assert_no_exception(lambda: im.transform((100, 100), transform)) + transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])]) + assert_no_exception(lambda: im.transform((100, 100), transform)) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py new file mode 100644 index 000000000..268a75b6f --- /dev/null +++ b/Tests/test_imagewin.py @@ -0,0 +1,6 @@ +from tester import * + +from PIL import Image +from PIL import ImageWin + +success() diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py new file mode 100644 index 000000000..93aa694d8 --- /dev/null +++ b/Tests/test_lib_image.py @@ -0,0 +1,30 @@ +from tester import * + +from PIL import Image + +def test_setmode(): + + im = Image.new("L", (1, 1), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("1", (1, 1), 1) + im.im.setmode("L") + assert_equal(im.im.getpixel((0, 0)), 255) + im.im.setmode("1") + assert_equal(im.im.getpixel((0, 0)), 255) + + im = Image.new("RGB", (1, 1), (1, 2, 3)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + im.im.setmode("RGBA") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGBX") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3, 255)) + im.im.setmode("RGB") + assert_equal(im.im.getpixel((0, 0)), (1, 2, 3)) + + assert_exception(ValueError, lambda: im.im.setmode("L")) + assert_exception(ValueError, lambda: im.im.setmode("RGBABCDE")) diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py new file mode 100644 index 000000000..7675348b3 --- /dev/null +++ b/Tests/test_lib_pack.py @@ -0,0 +1,138 @@ +from tester import * + +from PIL import Image + +def pack(): + pass # not yet + +def test_pack(): + + def pack(mode, rawmode): + if len(mode) == 1: + im = Image.new(mode, (1, 1), 1) + else: + im = Image.new(mode, (1, 1), (1, 2, 3, 4)[:len(mode)]) + + if py3: + return list(im.tobytes("raw", rawmode)) + else: + return [ord(c) for c in im.tobytes("raw", rawmode)] + + order = 1 if Image._ENDIAN == '<' else -1 + + assert_equal(pack("1", "1"), [128]) + assert_equal(pack("1", "1;I"), [0]) + assert_equal(pack("1", "1;R"), [1]) + assert_equal(pack("1", "1;IR"), [0]) + + assert_equal(pack("L", "L"), [1]) + + assert_equal(pack("I", "I"), [1, 0, 0, 0][::order]) + + assert_equal(pack("F", "F"), [0, 0, 128, 63][::order]) + + assert_equal(pack("LA", "LA"), [1, 2]) + + assert_equal(pack("RGB", "RGB"), [1, 2, 3]) + assert_equal(pack("RGB", "RGB;L"), [1, 2, 3]) + assert_equal(pack("RGB", "BGR"), [3, 2, 1]) + assert_equal(pack("RGB", "RGBX"), [1, 2, 3, 255]) # 255? + assert_equal(pack("RGB", "BGRX"), [3, 2, 1, 0]) + assert_equal(pack("RGB", "XRGB"), [0, 1, 2, 3]) + assert_equal(pack("RGB", "XBGR"), [0, 3, 2, 1]) + + assert_equal(pack("RGBX", "RGBX"), [1, 2, 3, 4]) # 4->255? + + assert_equal(pack("RGBA", "RGBA"), [1, 2, 3, 4]) + + assert_equal(pack("CMYK", "CMYK"), [1, 2, 3, 4]) + assert_equal(pack("YCbCr", "YCbCr"), [1, 2, 3]) + +def test_unpack(): + + def unpack(mode, rawmode, bytes_): + im = None + + if py3: + data = bytes(range(1,bytes_+1)) + else: + data = ''.join(chr(i) for i in range(1,bytes_+1)) + + im = Image.frombytes(mode, (1, 1), data, "raw", rawmode, 0, 1) + + return im.getpixel((0, 0)) + + def unpack_1(mode, rawmode, value): + assert mode == "1" + im = None + + if py3: + im = Image.frombytes(mode, (8, 1), bytes([value]), "raw", rawmode, 0, 1) + else: + im = Image.frombytes(mode, (8, 1), chr(value), "raw", rawmode, 0, 1) + + return tuple(im.getdata()) + + X = 255 + + assert_equal(unpack_1("1", "1", 1), (0,0,0,0,0,0,0,X)) + assert_equal(unpack_1("1", "1;I", 1), (X,X,X,X,X,X,X,0)) + assert_equal(unpack_1("1", "1;R", 1), (X,0,0,0,0,0,0,0)) + assert_equal(unpack_1("1", "1;IR", 1), (0,X,X,X,X,X,X,X)) + + assert_equal(unpack_1("1", "1", 170), (X,0,X,0,X,0,X,0)) + assert_equal(unpack_1("1", "1;I", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;R", 170), (0,X,0,X,0,X,0,X)) + assert_equal(unpack_1("1", "1;IR", 170), (X,0,X,0,X,0,X,0)) + + assert_equal(unpack("L", "L;2", 1), 0) + assert_equal(unpack("L", "L;4", 1), 0) + assert_equal(unpack("L", "L", 1), 1) + assert_equal(unpack("L", "L;I", 1), 254) + assert_equal(unpack("L", "L;R", 1), 128) + assert_equal(unpack("L", "L;16", 2), 2) # little endian + assert_equal(unpack("L", "L;16B", 2), 1) # big endian + + assert_equal(unpack("LA", "LA", 2), (1, 2)) + assert_equal(unpack("LA", "LA;L", 2), (1, 2)) + + assert_equal(unpack("RGB", "RGB", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;L", 3), (1, 2, 3)) + assert_equal(unpack("RGB", "RGB;R", 3), (128, 64, 192)) + assert_equal(unpack("RGB", "RGB;16B", 6), (1, 3, 5)) # ? + assert_equal(unpack("RGB", "BGR", 3), (3, 2, 1)) + assert_equal(unpack("RGB", "RGB;15", 2), (8, 131, 0)) + assert_equal(unpack("RGB", "BGR;15", 2), (0, 131, 8)) + assert_equal(unpack("RGB", "RGB;16", 2), (8, 64, 0)) + assert_equal(unpack("RGB", "BGR;16", 2), (0, 64, 8)) + assert_equal(unpack("RGB", "RGB;4B", 2), (17, 0, 34)) + + assert_equal(unpack("RGB", "RGBX", 4), (1, 2, 3)) + assert_equal(unpack("RGB", "BGRX", 4), (3, 2, 1)) + assert_equal(unpack("RGB", "XRGB", 4), (2, 3, 4)) + assert_equal(unpack("RGB", "XBGR", 4), (4, 3, 2)) + + assert_equal(unpack("RGBA", "RGBA", 4), (1, 2, 3, 4)) + assert_equal(unpack("RGBA", "BGRA", 4), (3, 2, 1, 4)) + assert_equal(unpack("RGBA", "ARGB", 4), (2, 3, 4, 1)) + assert_equal(unpack("RGBA", "ABGR", 4), (4, 3, 2, 1)) + assert_equal(unpack("RGBA", "RGBA;15", 2), (8, 131, 0, 0)) + assert_equal(unpack("RGBA", "BGRA;15", 2), (0, 131, 8, 0)) + assert_equal(unpack("RGBA", "RGBA;4B", 2), (17, 0, 34, 0)) + + assert_equal(unpack("RGBX", "RGBX", 4), (1, 2, 3, 4)) # 4->255? + assert_equal(unpack("RGBX", "BGRX", 4), (3, 2, 1, 255)) + assert_equal(unpack("RGBX", "XRGB", 4), (2, 3, 4, 255)) + assert_equal(unpack("RGBX", "XBGR", 4), (4, 3, 2, 255)) + assert_equal(unpack("RGBX", "RGB;15", 2), (8, 131, 0, 255)) + assert_equal(unpack("RGBX", "BGR;15", 2), (0, 131, 8, 255)) + assert_equal(unpack("RGBX", "RGB;4B", 2), (17, 0, 34, 255)) + + assert_equal(unpack("CMYK", "CMYK", 4), (1, 2, 3, 4)) + assert_equal(unpack("CMYK", "CMYK;I", 4), (254, 253, 252, 251)) + + assert_exception(ValueError, lambda: unpack("L", "L", 0)) + assert_exception(ValueError, lambda: unpack("RGB", "RGB", 2)) + assert_exception(ValueError, lambda: unpack("CMYK", "CMYK", 2)) + +run() diff --git a/Tests/test_locale.py b/Tests/test_locale.py new file mode 100644 index 000000000..2683c561b --- /dev/null +++ b/Tests/test_locale.py @@ -0,0 +1,31 @@ +from tester import * +from PIL import Image + +import locale + +# ref https://github.com/python-imaging/Pillow/issues/272 +## on windows, in polish locale: + +## import locale +## print locale.setlocale(locale.LC_ALL, 'polish') +## import string +## print len(string.whitespace) +## print ord(string.whitespace[6]) + +## Polish_Poland.1250 +## 7 +## 160 + +# one of string.whitespace is not freely convertable into ascii. + +path = "Images/lena.jpg" + +def test_sanity(): + assert_no_exception(lambda: Image.open(path)) + try: + locale.setlocale(locale.LC_ALL, "polish") + except: + skip('polish locale not available') + import string + assert_no_exception(lambda: Image.open(path)) + diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py new file mode 100644 index 000000000..4c1798509 --- /dev/null +++ b/Tests/test_mode_i16.py @@ -0,0 +1,105 @@ +from tester import * + +from PIL import Image + +def verify(im1): + im2 = lena("I") + assert_equal(im1.size, im2.size) + pix1 = im1.load() + pix2 = im2.load() + for y in range(im1.size[1]): + for x in range(im1.size[0]): + xy = x, y + if pix1[xy] != pix2[xy]: + failure( + "got %r from mode %s at %s, expected %r" % + (pix1[xy], im1.mode, xy, pix2[xy]) + ) + return + success() + +def test_basic(): + # PIL 1.1 has limited support for 16-bit image data. Check that + # create/copy/transform and save works as expected. + + def basic(mode): + + imIn = lena("I").convert(mode) + verify(imIn) + + w, h = imIn.size + + imOut = imIn.copy() + verify(imOut) # copy + + imOut = imIn.transform((w, h), Image.EXTENT, (0, 0, w, h)) + verify(imOut) # transform + + filename = tempfile("temp.im") + imIn.save(filename) + + imOut = Image.open(filename) + + verify(imIn) + verify(imOut) + + imOut = imIn.crop((0, 0, w, h)) + verify(imOut) + + imOut = Image.new(mode, (w, h), None) + imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) + imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) + + verify(imIn) + verify(imOut) + + imIn = Image.new(mode, (1, 1), 1) + assert_equal(imIn.getpixel((0, 0)), 1) + + imIn.putpixel((0, 0), 2) + assert_equal(imIn.getpixel((0, 0)), 2) + + if mode == "L": + max = 255 + else: + max = 32767 + + imIn = Image.new(mode, (1, 1), 256) + assert_equal(imIn.getpixel((0, 0)), min(256, max)) + + imIn.putpixel((0, 0), 512) + assert_equal(imIn.getpixel((0, 0)), min(512, max)) + + basic("L") + + basic("I;16") + basic("I;16B") + basic("I;16L") + + basic("I") + + +def test_tobytes(): + + def tobytes(mode): + return Image.new(mode, (1, 1), 1).tobytes() + + order = 1 if Image._ENDIAN == '<' else -1 + + assert_equal(tobytes("L"), b"\x01") + assert_equal(tobytes("I;16"), b"\x01\x00") + assert_equal(tobytes("I;16B"), b"\x00\x01") + assert_equal(tobytes("I"), b"\x01\x00\x00\x00"[::order]) + + +def test_convert(): + + im = lena("I") + + verify(im.convert("I;16")) + verify(im.convert("I;16").convert("L")) + verify(im.convert("I;16").convert("I")) + + verify(im.convert("I;16B")) + verify(im.convert("I;16B").convert("L")) + verify(im.convert("I;16B").convert("I")) diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py new file mode 100644 index 000000000..4833cabb2 --- /dev/null +++ b/Tests/test_numpy.py @@ -0,0 +1,120 @@ +from tester import * + +from PIL import Image +import struct + +try: + import site + import numpy +except ImportError: + skip() + +def test_numpy_to_image(): + + def to_image(dtype, bands=1, bool=0): + if bands == 1: + if bool: + data = [0, 1] * 50 + else: + data = list(range(100)) + a = numpy.array(data, dtype=dtype) + a.shape = 10, 10 + i = Image.fromarray(a) + if list(i.getdata()) != data: + print("data mismatch for", dtype) + else: + data = list(range(100)) + a = numpy.array([[x]*bands for x in data], dtype=dtype) + a.shape = 10, 10, bands + i = Image.fromarray(a) + if list(i.split()[0].getdata()) != list(range(100)): + print("data mismatch for", dtype) + # print dtype, list(i.getdata()) + return i + + # assert_image(to_image(numpy.bool, bool=1), "1", (10, 10)) + # assert_image(to_image(numpy.bool8, bool=1), "1", (10, 10)) + + assert_exception(TypeError, lambda: to_image(numpy.uint)) + assert_image(to_image(numpy.uint8), "L", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.uint16)) + assert_exception(TypeError, lambda: to_image(numpy.uint32)) + assert_exception(TypeError, lambda: to_image(numpy.uint64)) + + assert_image(to_image(numpy.int8), "I", (10, 10)) + if Image._ENDIAN == '<': # Little endian + assert_image(to_image(numpy.int16), "I;16", (10, 10)) + else: + assert_image(to_image(numpy.int16), "I;16B", (10, 10)) + assert_image(to_image(numpy.int32), "I", (10, 10)) + assert_exception(TypeError, lambda: to_image(numpy.int64)) + + assert_image(to_image(numpy.float), "F", (10, 10)) + assert_image(to_image(numpy.float32), "F", (10, 10)) + assert_image(to_image(numpy.float64), "F", (10, 10)) + + assert_image(to_image(numpy.uint8, 3), "RGB", (10, 10)) + assert_image(to_image(numpy.uint8, 4), "RGBA", (10, 10)) + + +# based on an erring example at http://is.gd/6F0esS (which resolves to) +# http://stackoverflow.com/questions/10854903/what-is-causing-dimension-dependent-attributeerror-in-pil-fromarray-function +def test_3d_array(): + a = numpy.ones((10, 10, 10), dtype=numpy.uint8) + assert_image(Image.fromarray(a[1, :, :]), "L", (10, 10)) + assert_image(Image.fromarray(a[:, 1, :]), "L", (10, 10)) + assert_image(Image.fromarray(a[:, :, 1]), "L", (10, 10)) + + +def _test_img_equals_nparray(img, np): + assert_equal(img.size, np.shape[0:2]) + px = img.load() + for x in range(0, img.size[0], int(img.size[0]/10)): + for y in range(0, img.size[1], int(img.size[1]/10)): + assert_deep_equal(px[x,y], np[y,x]) + + +def test_16bit(): + img = Image.open('Tests/images/16bit.cropped.tif') + np_img = numpy.array(img) + _test_img_equals_nparray(img, np_img) + assert_equal(np_img.dtype, numpy.dtype('u2'), + ("I;16L", '= (3,0)) + +# some test helpers + +_target = None +_tempfiles = [] +_logfile = None + +def success(): + import sys + success.count += 1 + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +def failure(msg=None, frame=None): + import sys, linecache + failure.count += 1 + if _target: + if frame is None: + frame = sys._getframe() + while frame.f_globals.get("__name__") != _target.__name__: + frame = frame.f_back + location = (frame.f_code.co_filename, frame.f_lineno) + prefix = "%s:%d: " % location + line = linecache.getline(*location) + print(prefix + line.strip() + " failed:") + if msg: + print("- " + msg) + if _logfile: + print(sys.argv[0], success.count, failure.count, file=_logfile) + +success.count = failure.count = 0 + +# predicates + +def assert_true(v, msg=None): + if v: + success() + else: + failure(msg or "got %r, expected true value" % v) + +def assert_false(v, msg=None): + if v: + failure(msg or "got %r, expected false value" % v) + else: + success() + +def assert_equal(a, b, msg=None): + if a == b: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + +def assert_almost_equal(a, b, msg=None, eps=1e-6): + if abs(a-b) < eps: + success() + else: + failure(msg or "got %r, expected %r" % (a, b)) + +def assert_deep_equal(a, b, msg=None): + try: + if len(a) == len(b): + if all([x==y for x,y in zip(a,b)]): + success() + else: + failure(msg or "got %s, expected %s" % (a,b)) + else: + failure(msg or "got length %s, expected %s" % (len(a), len(b))) + except: + assert_equal(a,b,msg) + + +def assert_match(v, pattern, msg=None): + import re + if re.match(pattern, v): + success() + else: + failure(msg or "got %r, doesn't match pattern %r" % (v, pattern)) + +def assert_exception(exc_class, func): + import sys, traceback + try: + func() + except exc_class: + success() + except: + failure("expected %r exception, got %r" % ( + exc_class.__name__, sys.exc_info()[0].__name__)) + traceback.print_exc() + else: + failure("expected %r exception, got no exception" % exc_class.__name__) + +def assert_no_exception(func): + import sys, traceback + try: + func() + except: + failure("expected no exception, got %r" % sys.exc_info()[0].__name__) + traceback.print_exc() + else: + success() + +def assert_warning(warn_class, func): + # note: this assert calls func three times! + import warnings + def warn_error(message, category, **options): + raise category(message) + def warn_ignore(message, category, **options): + pass + warn = warnings.warn + result = None + try: + warnings.warn = warn_ignore + assert_no_exception(func) + result = func() + warnings.warn = warn_error + assert_exception(warn_class, func) + finally: + warnings.warn = warn # restore + return result + +# helpers + +from io import BytesIO + +def fromstring(data): + from PIL import Image + return Image.open(BytesIO(data)) + +def tostring(im, format, **options): + out = BytesIO() + im.save(out, format, **options) + return out.getvalue() + +def lena(mode="RGB", cache={}): + from PIL import Image + im = cache.get(mode) + if im is None: + if mode == "RGB": + im = Image.open("Images/lena.ppm") + elif mode == "F": + im = lena("L").convert(mode) + elif mode[:4] == "I;16": + im = lena("I").convert(mode) + else: + im = lena("RGB").convert(mode) + cache[mode] = im + return im + +def assert_image(im, mode, size, msg=None): + if mode is not None and im.mode != mode: + failure(msg or "got mode %r, expected %r" % (im.mode, mode)) + elif size is not None and im.size != size: + failure(msg or "got size %r, expected %r" % (im.size, size)) + else: + success() + +def assert_image_equal(a, b, msg=None): + if a.mode != b.mode: + failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + failure(msg or "got size %r, expected %r" % (a.size, b.size)) + elif a.tobytes() != b.tobytes(): + failure(msg or "got different content") + # generate better diff? + else: + success() + +def assert_image_similar(a, b, epsilon, msg=None): + epsilon = float(epsilon) + if a.mode != b.mode: + return failure(msg or "got mode %r, expected %r" % (a.mode, b.mode)) + elif a.size != b.size: + return failure(msg or "got size %r, expected %r" % (a.size, b.size)) + diff = 0 + try: + ord(b'0') + for abyte,bbyte in zip(a.tobytes(),b.tobytes()): + diff += abs(ord(abyte)-ord(bbyte)) + except: + for abyte,bbyte in zip(a.tobytes(),b.tobytes()): + diff += abs(abyte-bbyte) + ave_diff = float(diff)/(a.size[0]*a.size[1]) + if epsilon < ave_diff: + failure(msg or "average pixel value difference %.4f > epsilon %.4f" %(ave_diff, epsilon)) + else: + success() + +def tempfile(template, *extra): + import os, os.path, sys, tempfile + files = [] + root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.mkdir(root) + except OSError: + pass + for temp in (template,) + extra: + assert temp[:5] in ("temp.", "temp_") + name = os.path.basename(sys.argv[0]) + name = temp[:4] + os.path.splitext(name)[0][4:] + name = name + "_%d" % len(_tempfiles) + temp[4:] + name = os.path.join(root, name) + files.append(name) + _tempfiles.extend(files) + return files[0] + +# test runner + +def run(): + global _target, _tests, run + import sys, traceback + _target = sys.modules["__main__"] + run = None # no need to run twice + tests = [] + for name, value in list(vars(_target).items()): + if name[:5] == "test_" and type(value) is type(success): + tests.append((value.__code__.co_firstlineno, name, value)) + tests.sort() # sort by line + for lineno, name, func in tests: + try: + _tests = [] + func() + for func, args in _tests: + func(*args) + except: + t, v, tb = sys.exc_info() + tb = tb.tb_next + if tb: + failure(frame=tb.tb_frame) + traceback.print_exception(t, v, tb) + else: + print("%s:%d: cannot call test function: %s" % ( + sys.argv[0], lineno, v)) + failure.count += 1 + +def yield_test(function, *args): + # collect delayed/generated tests + _tests.append((function, args)) + +def skip(msg=None): + import os + print("skip") + os._exit(0) # don't run exit handlers + +def _setup(): + global _logfile + def report(): + if run: + run() + if success.count and not failure.count: + print("ok") + # only clean out tempfiles if test passed + import os, os.path, tempfile + for file in _tempfiles: + try: + os.remove(file) + except OSError: + pass # report? + temp_root = os.path.join(tempfile.gettempdir(), 'pillow-tests') + try: + os.rmdir(temp_root) + except OSError: + pass + + if "--coverage" in sys.argv: + import coverage + coverage.stop() + # The coverage module messes up when used from inside an + # atexit handler. Do an explicit save to make sure that + # we actually flush the coverage cache. + coverage.the_coverage.save() + import atexit, sys + atexit.register(report) + if "--coverage" in sys.argv: + import coverage + coverage.start() + if "--log" in sys.argv: + _logfile = open("test.log", "a") + + +_setup() diff --git a/Tests/threaded_save.py b/Tests/threaded_save.py new file mode 100644 index 000000000..8162e713c --- /dev/null +++ b/Tests/threaded_save.py @@ -0,0 +1,56 @@ +from PIL import Image + +import sys, time +import io +import threading, queue + +try: + format = sys.argv[1] +except: + format = "PNG" + +im = Image.open("Images/lena.ppm") +im.load() + +queue = queue.Queue() + +result = [] + +class Worker(threading.Thread): + def run(self): + while 1: + im = queue.get() + if im is None: + queue.task_done() + sys.stdout.write("x") + break + f = io.BytesIO() + im.save(f, format, optimize=1) + data = f.getvalue() + result.append(len(data)) + im = Image.open(io.BytesIO(data)) + im.load() + sys.stdout.write(".") + queue.task_done() + +t0 = time.time() + +threads = 20 +jobs = 100 + +for i in range(threads): + w = Worker() + w.start() + +for i in range(jobs): + queue.put(im) + +for i in range(threads): + queue.put(None) + +queue.join() + +print() +print(time.time() - t0) +print(len(result), sum(result)) +print(result) diff --git a/Tests/versions.py b/Tests/versions.py new file mode 100644 index 000000000..a4e4a0bc2 --- /dev/null +++ b/Tests/versions.py @@ -0,0 +1,23 @@ +from PIL import Image + +def version(module, version): + v = getattr(module.core, version + "_version", None) + if v: + print(version, v) + +version(Image, "jpeglib") +version(Image, "zlib") + +try: + from PIL import ImageFont +except ImportError: + pass +else: + version(ImageFont, "freetype2") + +try: + from PIL import ImageCms +except ImportError: + pass +else: + version(ImageCms, "littlecms") diff --git a/Tk/install.txt b/Tk/install.txt index e24d36301..0e2ade06d 100644 --- a/Tk/install.txt +++ b/Tk/install.txt @@ -1,5 +1,5 @@ ==================================================================== -Using PIL With Tkinter +Using PIL With Tkinter ==================================================================== Starting with 1.0 final (release candidate 2 and later, to be diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c index 5963ee24c..3ebe49d48 100644 --- a/Tk/tkImaging.c +++ b/Tk/tkImaging.c @@ -11,11 +11,11 @@ * * To use this module, import the _imagingtk module (ImageTk does * this for you). - * + * * If you're using Python in an embedded context, you can add the * following lines to your Tcl_AppInit function (in tkappinit.c) * instead. Put them after the calls to Tcl_Init and Tk_Init: - * + * * { * extern void TkImaging_Init(Tcl_Interp* interp); * TkImaging_Init(interp); @@ -48,9 +48,8 @@ for the Tcl_CreateCommand command. */ #define USE_COMPAT_CONST -#include "tk.h" - #include "Imaging.h" +#include "tk.h" #include @@ -58,7 +57,7 @@ static Imaging ImagingFind(const char* name) { - long id; + Py_ssize_t id; /* FIXME: use CObject instead? */ id = atol(name); diff --git a/_imaging.c b/_imaging.c index c21897dec..c62f0257d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -65,17 +65,23 @@ * 2005-10-02 fl Added access proxy * 2006-06-18 fl Always draw last point in polyline * - * Copyright (c) 1997-2006 by Secret Labs AB + * Copyright (c) 1997-2006 by Secret Labs AB * Copyright (c) 1995-2006 by Fredrik Lundh * * See the README file for information on usage and redistribution. */ +#define PILLOW_VERSION "2.3.0" #include "Python.h" +#ifdef HAVE_LIBZ +#include "zlib.h" +#endif + #include "Imaging.h" +#include "py3.h" /* Configuration stuff. Feel free to undef things you don't need. */ #define WITH_IMAGECHOPS /* ImageChops support */ @@ -95,7 +101,7 @@ /* PIL Plus extensions */ #undef WITH_CRACKCODE /* pil plus */ -#undef VERBOSE +#undef VERBOSE #define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) @@ -103,21 +109,8 @@ #define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)]) #define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif - /* -------------------------------------------------------------------- */ -/* OBJECT ADMINISTRATION */ +/* OBJECT ADMINISTRATION */ /* -------------------------------------------------------------------- */ typedef struct { @@ -126,7 +119,7 @@ typedef struct { ImagingAccess access; } ImagingObject; -staticforward PyTypeObject Imaging_Type; +static PyTypeObject Imaging_Type; #ifdef WITH_IMAGEDRAW @@ -148,7 +141,7 @@ typedef struct { Glyph glyphs[256]; } ImagingFontObject; -staticforward PyTypeObject ImagingFont_Type; +static PyTypeObject ImagingFont_Type; typedef struct { PyObject_HEAD @@ -157,7 +150,7 @@ typedef struct { int blend; } ImagingDrawObject; -staticforward PyTypeObject ImagingDraw_Type; +static PyTypeObject ImagingDraw_Type; #endif @@ -167,20 +160,20 @@ typedef struct { int readonly; } PixelAccessObject; -staticforward PyTypeObject PixelAccess_Type; +static PyTypeObject PixelAccess_Type; -PyObject* +PyObject* PyImagingNew(Imaging imOut) { ImagingObject* imagep; if (!imOut) - return NULL; + return NULL; imagep = PyObject_New(ImagingObject, &Imaging_Type); if (imagep == NULL) { - ImagingDelete(imOut); - return NULL; + ImagingDelete(imOut); + return NULL; } #ifdef VERBOSE @@ -202,18 +195,18 @@ _dealloc(ImagingObject* imagep) #endif if (imagep->access) - ImagingAccessDelete(imagep->image, imagep->access); + ImagingAccessDelete(imagep->image, imagep->access); ImagingDelete(imagep->image); PyObject_Del(imagep); } -#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type) +#define PyImaging_Check(op) (Py_TYPE(op) == &Imaging_Type) Imaging PyImaging_AsImaging(PyObject *op) { if (!PyImaging_Check(op)) { - PyErr_BadInternalCall(); - return NULL; + PyErr_BadInternalCall(); + return NULL; } return ((ImagingObject *)op)->image; @@ -243,43 +236,46 @@ void ImagingSectionLeave(ImagingSectionCookie* cookie) /* -------------------------------------------------------------------- */ /* Python compatibility API */ -#if PY_VERSION_HEX < 0x02020000 - -int PyImaging_CheckBuffer(PyObject *buffer) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount && - procs->bf_getsegcount(buffer, NULL) == 1) - return 1; - return 0; -} - -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) -{ - PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; - return procs->bf_getreadbuffer(buffer, 0, ptr); -} - -#else - int PyImaging_CheckBuffer(PyObject* buffer) { - return PyObject_CheckReadBuffer(buffer); +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_CheckBuffer(buffer); +#else + return PyObject_CheckBuffer(buffer) || PyObject_CheckReadBuffer(buffer); +#endif } -int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view) { /* must call check_buffer first! */ -#if PY_VERSION_HEX < 0x02050000 - int n = 0; +#if PY_VERSION_HEX >= 0x03000000 + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); #else - Py_ssize_t n = 0; -#endif - PyObject_AsReadBuffer(buffer, ptr, &n); - return (int) n; -} + /* Use new buffer protocol if available + (mmap doesn't support this in 2.7, go figure) */ + if (PyObject_CheckBuffer(buffer)) { + return PyObject_GetBuffer(buffer, view, PyBUF_SIMPLE); + } + /* Pretend we support the new protocol; PyBuffer_Release happily ignores + calling bf_releasebuffer on objects that don't support it */ + view->buf = NULL; + view->len = 0; + view->readonly = 1; + view->format = NULL; + view->ndim = 0; + view->shape = NULL; + view->strides = NULL; + view->suboffsets = NULL; + view->itemsize = 0; + view->internal = NULL; + + Py_INCREF(buffer); + view->obj = buffer; + + return PyObject_AsReadBuffer(buffer, (void *) &view->buf, &view->len); #endif +} /* -------------------------------------------------------------------- */ /* EXCEPTION REROUTING */ @@ -291,6 +287,7 @@ static const char* wrong_mode = "unrecognized image mode"; static const char* wrong_raw_mode = "unrecognized raw mode"; static const char* outside_image = "image index out of range"; static const char* outside_palette = "palette index out of range"; +static const char* wrong_palette_size = "invalid palette size"; static const char* no_palette = "image has no palette"; static const char* readonly = "image is readonly"; /* static const char* no_content = "image has no content"; */ @@ -339,7 +336,7 @@ ImagingError_Clear(void) } /* -------------------------------------------------------------------- */ -/* HELPERS */ +/* HELPERS */ /* -------------------------------------------------------------------- */ static int @@ -372,14 +369,14 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) void* list; if (!PySequence_Check(arg)) { - PyErr_SetString(PyExc_TypeError, must_be_sequence); - return NULL; + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; } n = PyObject_Length(arg); if (length && wrong_length && n != *length) { - PyErr_SetString(PyExc_ValueError, wrong_length); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; } list = malloc(n * (type & 0xff)); @@ -472,33 +469,33 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) } pixel; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return NULL; + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; } access->get_pixel(im, x, y, &pixel); switch (im->type) { case IMAGING_TYPE_UINT8: - switch (im->bands) { - case 1: - return PyInt_FromLong(pixel.b[0]); - case 2: - return Py_BuildValue("ii", pixel.b[0], pixel.b[1]); - case 3: - return Py_BuildValue("iii", pixel.b[0], pixel.b[1], pixel.b[2]); - case 4: - return Py_BuildValue("iiii", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]); - } - break; + switch (im->bands) { + case 1: + return PyInt_FromLong(pixel.b[0]); + case 2: + return Py_BuildValue("BB", pixel.b[0], pixel.b[1]); + case 3: + return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]); + case 4: + return Py_BuildValue("BBBB", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]); + } + break; case IMAGING_TYPE_INT32: - return PyInt_FromLong(pixel.i); + return PyInt_FromLong(pixel.i); case IMAGING_TYPE_FLOAT32: - return PyFloat_FromDouble(pixel.f); + return PyFloat_FromDouble(pixel.f); case IMAGING_TYPE_SPECIAL: - if (strncmp(im->mode, "I;16", 4) == 0) - return PyInt_FromLong(pixel.h); - break; + if (strncmp(im->mode, "I;16", 4) == 0) + return PyInt_FromLong(pixel.h); + break; } /* unknown type */ @@ -527,8 +524,17 @@ getink(PyObject* color, Imaging im, char* ink) ink[1] = ink[2] = ink[3] = 0; } else { a = 255; - if (PyInt_Check(color)) { - r = PyInt_AS_LONG(color); +#if PY_VERSION_HEX >= 0x03000000 + if (PyLong_Check(color)) { + r = (int) PyLong_AsLong(color); +#else + if (PyInt_Check(color) || PyLong_Check(color)) { + if (PyInt_Check(color)) + r = PyInt_AS_LONG(color); + else + r = (int) PyLong_AsLong(color); +#endif + /* compatibility: ABGR */ a = (UINT8) (r >> 24); b = (UINT8) (r >> 16); @@ -581,10 +587,10 @@ getink(PyObject* color, Imaging im, char* ink) } /* -------------------------------------------------------------------- */ -/* FACTORIES */ +/* FACTORIES */ /* -------------------------------------------------------------------- */ -static PyObject* +static PyObject* _fill(PyObject* self, PyObject* args) { char* mode; @@ -592,12 +598,12 @@ _fill(PyObject* self, PyObject* args) PyObject* color; char buffer[4]; Imaging im; - + xsize = ysize = 256; color = NULL; if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) - return NULL; + return NULL; im = ImagingNew(mode, xsize, ysize); if (!im) @@ -616,107 +622,121 @@ _fill(PyObject* self, PyObject* args) return PyImagingNew(im); } -static PyObject* +static PyObject* _new(PyObject* self, PyObject* args) { char* mode; int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNew(mode, xsize, ysize)); } -static PyObject* +static PyObject* _new_array(PyObject* self, PyObject* args) { char* mode; int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNewArray(mode, xsize, ysize)); } -static PyObject* +static PyObject* _new_block(PyObject* self, PyObject* args) { char* mode; int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNewBlock(mode, xsize, ysize)); } -static PyObject* +static PyObject* _getcount(PyObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, ":getcount")) - return NULL; + return NULL; return PyInt_FromLong(ImagingNewCount); } -static PyObject* +static PyObject* _linear_gradient(PyObject* self, PyObject* args) { char* mode; if (!PyArg_ParseTuple(args, "s", &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingFillLinearGradient(mode)); } -static PyObject* +static PyObject* _radial_gradient(PyObject* self, PyObject* args) { char* mode; if (!PyArg_ParseTuple(args, "s", &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingFillRadialGradient(mode)); } -static PyObject* +static PyObject* _open_ppm(PyObject* self, PyObject* args) { char* filename; if (!PyArg_ParseTuple(args, "s", &filename)) - return NULL; + return NULL; return PyImagingNew(ImagingOpenPPM(filename)); } -static PyObject* +static PyObject* +_alpha_composite(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep1; + ImagingObject* imagep2; + + if (!PyArg_ParseTuple(args, "O!O!", + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; + + return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image)); +} + +static PyObject* _blend(ImagingObject* self, PyObject* args) { ImagingObject* imagep1; ImagingObject* imagep2; double alpha; - + alpha = 0.5; if (!PyArg_ParseTuple(args, "O!O!|d", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2, - &alpha)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2, + &alpha)) + return NULL; return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, - (float) alpha)); + (float) alpha)); } /* -------------------------------------------------------------------- */ -/* METHODS */ +/* METHODS */ /* -------------------------------------------------------------------- */ -static PyObject* +static PyObject* _convert(ImagingObject* self, PyObject* args) { char* mode; @@ -724,31 +744,31 @@ _convert(ImagingObject* self, PyObject* args) ImagingObject *paletteimage = NULL; if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) - return NULL; + return NULL; if (paletteimage != NULL) { - if (!PyImaging_Check(paletteimage)) { - PyObject_Print((PyObject *)paletteimage, stderr, 0); - PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'"); - return NULL; - } - if (paletteimage->image->palette == NULL) { - PyErr_SetString(PyExc_ValueError, "null palette"); - return NULL; - } + if (!PyImaging_Check(paletteimage)) { + PyObject_Print((PyObject *)paletteimage, stderr, 0); + PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'"); + return NULL; + } + if (paletteimage->image->palette == NULL) { + PyErr_SetString(PyExc_ValueError, "null palette"); + return NULL; + } } return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither)); } -static PyObject* +static PyObject* _convert2(ImagingObject* self, PyObject* args) { ImagingObject* imagep1; ImagingObject* imagep2; if (!PyArg_ParseTuple(args, "O!O!", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; if (!ImagingConvert2(imagep1->image, imagep2->image)) return NULL; @@ -757,41 +777,56 @@ _convert2(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _convert_matrix(ImagingObject* self, PyObject* args) { char* mode; float m[12]; if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m+0, m+1, m+2, m+3)) { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, - m+0, m+1, m+2, m+3, - m+4, m+5, m+6, m+7, - m+8, m+9, m+10, m+11)) - return NULL; + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, + m+0, m+1, m+2, m+3, + m+4, m+5, m+6, m+7, + m+8, m+9, m+10, m+11)) + return NULL; } return PyImagingNew(ImagingConvertMatrix(self->image, mode, m)); } -static PyObject* +static PyObject* +_convert_transparent(ImagingObject* self, PyObject* args) +{ + char* mode; + int r,g,b; + if (PyArg_ParseTuple(args, "s(iii)", &mode, &r, &g, &b)) { + return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b)); + } + PyErr_Clear(); + if (PyArg_ParseTuple(args, "si", &mode, &r)) { + return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0)); + } + return NULL; +} + +static PyObject* _copy(ImagingObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "")) - return NULL; + return NULL; return PyImagingNew(ImagingCopy(self->image)); } -static PyObject* +static PyObject* _copy2(ImagingObject* self, PyObject* args) { ImagingObject* imagep1; ImagingObject* imagep2; if (!PyArg_ParseTuple(args, "O!O!", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; if (!ImagingCopy2(imagep1->image, imagep2->image)) return NULL; @@ -800,28 +835,28 @@ _copy2(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _crop(ImagingObject* self, PyObject* args) { int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) - return NULL; + return NULL; return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1)); } -static PyObject* +static PyObject* _expand(ImagingObject* self, PyObject* args) { int x, y; int mode = 0; if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingExpand(self->image, x, y, mode)); } -static PyObject* +static PyObject* _filter(ImagingObject* self, PyObject* args) { PyObject* imOut; @@ -832,9 +867,9 @@ _filter(ImagingObject* self, PyObject* args) float divisor, offset; PyObject* kernel = NULL; if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize, - &divisor, &offset, &kernel)) + &divisor, &offset, &kernel)) return NULL; - + /* get user-defined kernel */ kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32); if (!kerneldata) @@ -854,7 +889,7 @@ _filter(ImagingObject* self, PyObject* args) } #ifdef WITH_UNSHARPMASK -static PyObject* +static PyObject* _gaussian_blur(ImagingObject* self, PyObject* args) { Imaging imIn; @@ -876,7 +911,7 @@ _gaussian_blur(ImagingObject* self, PyObject* args) } #endif -static PyObject* +static PyObject* _getpalette(ImagingObject* self, PyObject* args) { PyObject* palette; @@ -887,29 +922,40 @@ _getpalette(ImagingObject* self, PyObject* args) char* mode = "RGB"; char* rawmode = "RGB"; if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) - return NULL; + return NULL; if (!self->image->palette) { - PyErr_SetString(PyExc_ValueError, no_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; } pack = ImagingFindPacker(mode, rawmode, &bits); if (!pack) { - PyErr_SetString(PyExc_ValueError, wrong_raw_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; } - palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8); + palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) - return NULL; + return NULL; - pack((UINT8*) PyString_AsString(palette), - self->image->palette->palette, palettesize); + pack((UINT8*) PyBytes_AsString(palette), + self->image->palette->palette, palettesize); return palette; } +static PyObject* +_getpalettemode(ImagingObject* self, PyObject* args) +{ + if (!self->image->palette) { + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; + } + + return PyUnicode_FromString(self->image->palette->mode); +} + static inline int _getxy(PyObject* xy, int* x, int *y) { @@ -917,7 +963,7 @@ _getxy(PyObject* xy, int* x, int *y) if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2) goto badarg; - + value = PyTuple_GET_ITEM(xy, 0); if (PyInt_Check(value)) *x = PyInt_AS_LONG(value); @@ -951,7 +997,7 @@ _getxy(PyObject* xy, int* x, int *y) return -1; } -static PyObject* +static PyObject* _getpixel(ImagingObject* self, PyObject* args) { PyObject* xy; @@ -996,7 +1042,7 @@ _histogram(ImagingObject* self, PyObject* args) PyObject* extremap = NULL; ImagingObject* maskp = NULL; if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) - return NULL; + return NULL; if (extremap) { ep = &extrema; @@ -1030,19 +1076,19 @@ _histogram(ImagingObject* self, PyObject* args) h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep); if (!h) - return NULL; + return NULL; /* Build an integer list containing the histogram */ list = PyList_New(h->bands * 256); for (i = 0; i < h->bands * 256; i++) { - PyObject* item; - item = PyInt_FromLong(h->histogram[i]); - if (item == NULL) { - Py_DECREF(list); - list = NULL; - break; - } - PyList_SetItem(list, i, item); + PyObject* item; + item = PyInt_FromLong(h->histogram[i]); + if (item == NULL) { + Py_DECREF(list); + list = NULL; + break; + } + PyList_SetItem(list, i, item); } ImagingHistogramDelete(h); @@ -1051,28 +1097,28 @@ _histogram(ImagingObject* self, PyObject* args) } #ifdef WITH_MODEFILTER -static PyObject* +static PyObject* _modefilter(ImagingObject* self, PyObject* args) { int size; if (!PyArg_ParseTuple(args, "i", &size)) - return NULL; + return NULL; return PyImagingNew(ImagingModeFilter(self->image, size)); } #endif -static PyObject* +static PyObject* _offset(ImagingObject* self, PyObject* args) { int xoffset, yoffset; if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) - return NULL; + return NULL; return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset)); } -static PyObject* +static PyObject* _paste(ImagingObject* self, PyObject* args) { int status; @@ -1082,10 +1128,10 @@ _paste(ImagingObject* self, PyObject* args) int x0, y0, x1, y1; ImagingObject* maskp = NULL; if (!PyArg_ParseTuple(args, "O(iiii)|O!", - &source, - &x0, &y0, &x1, &y1, - &Imaging_Type, &maskp)) - return NULL; + &source, + &x0, &y0, &x1, &y1, + &Imaging_Type, &maskp)) + return NULL; if (PyImaging_Check(source)) status = ImagingPaste( @@ -1123,7 +1169,7 @@ _point(ImagingObject* self, PyObject* args) PyObject* list; char* mode; if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) - return NULL; + return NULL; if (mode && !strcmp(mode, "F")) { FLOAT32* data; @@ -1194,7 +1240,7 @@ _point_transform(ImagingObject* self, PyObject* args) double scale = 1.0; double offset = 0.0; if (!PyArg_ParseTuple(args, "|dd", &scale, &offset)) - return NULL; + return NULL; return PyImagingNew(ImagingPointTransform(self->image, scale, offset)); } @@ -1203,31 +1249,33 @@ static PyObject* _putdata(ImagingObject* self, PyObject* args) { Imaging image; - int n, i, x, y; + // i & n are # pixels, require py_ssize_t. x can be as large as n. y, just because. + Py_ssize_t n, i, x, y; PyObject* data; double scale = 1.0; double offset = 0.0; + if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) - return NULL; + return NULL; if (!PySequence_Check(data)) { - PyErr_SetString(PyExc_TypeError, must_be_sequence); - return NULL; + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; } image = self->image; n = PyObject_Length(data); - if (n > (int) (image->xsize * image->ysize)) { - PyErr_SetString(PyExc_TypeError, "too many data entries"); - return NULL; + if (n > (Py_ssize_t) (image->xsize * image->ysize)) { + PyErr_SetString(PyExc_TypeError, "too many data entries"); + return NULL; } if (image->image8) { - if (PyString_Check(data)) { + if (PyBytes_Check(data)) { unsigned char* p; - p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data); + p = (unsigned char*) PyBytes_AS_STRING(data); if (scale == 1.0 && offset == 0.0) /* Plain string data */ for (i = y = 0; i < n; i += image->xsize, y++) { @@ -1236,7 +1284,7 @@ _putdata(ImagingObject* self, PyObject* args) x = image->xsize; memcpy(image->image8[y], p+i, x); } - else + else /* Scaled and clipped string data */ for (i = x = y = 0; i < n; i++) { image->image8[y][x] = CLIP((int) (p[i] * scale + offset)); @@ -1312,14 +1360,18 @@ _putdata(ImagingObject* self, PyObject* args) break; default: for (i = x = y = 0; i < n; i++) { - char ink[4]; + union { + char ink[4]; + INT32 inkint; + } u; + PyObject *op = PySequence_GetItem(data, i); - if (!op || !getink(op, image, ink)) { + if (!op || !getink(op, image, u.ink)) { Py_DECREF(op); return NULL; } /* FIXME: what about scale and offset? */ - image->image32[y][x] = *((INT32*) ink); + image->image32[y][x] = u.inkint; Py_XDECREF(op); if (++x >= (int) image->xsize) x = 0, y++; @@ -1335,15 +1387,14 @@ _putdata(ImagingObject* self, PyObject* args) #ifdef WITH_QUANTIZE -#include "Quant.h" -static PyObject* +static PyObject* _quantize(ImagingObject* self, PyObject* args) { int colours = 256; int method = 0; int kmeans = 0; if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) - return NULL; + return NULL; if (!self->image->xsize || !self->image->ysize) { /* no content; return an empty image */ @@ -1356,7 +1407,7 @@ _quantize(ImagingObject* self, PyObject* args) } #endif -static PyObject* +static PyObject* _putpalette(ImagingObject* self, PyObject* args) { ImagingShuffler unpack; @@ -1365,18 +1416,23 @@ _putpalette(ImagingObject* self, PyObject* args) char* rawmode; UINT8* palette; int palettesize; - if (!PyArg_ParseTuple(args, "ss#", &rawmode, &palette, &palettesize)) - return NULL; + if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize)) + return NULL; if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) { - PyErr_SetString(PyExc_ValueError, wrong_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_mode); + return NULL; } unpack = ImagingFindUnpacker("RGB", rawmode, &bits); if (!unpack) { - PyErr_SetString(PyExc_ValueError, wrong_raw_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; + } + + if ( palettesize * 8 / bits > 256) { + PyErr_SetString(PyExc_ValueError, wrong_palette_size); + return NULL; } ImagingPaletteDelete(self->image->palette); @@ -1391,22 +1447,22 @@ _putpalette(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _putpalettealpha(ImagingObject* self, PyObject* args) { int index; int alpha = 0; if (!PyArg_ParseTuple(args, "i|i", &index, &alpha)) - return NULL; + return NULL; if (!self->image->palette) { - PyErr_SetString(PyExc_ValueError, no_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; } if (index < 0 || index >= 256) { - PyErr_SetString(PyExc_ValueError, outside_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, outside_palette); + return NULL; } strcpy(self->image->palette->mode, "RGBA"); @@ -1416,7 +1472,35 @@ _putpalettealpha(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* +_putpalettealphas(ImagingObject* self, PyObject* args) +{ + int i; + UINT8 *values; + int length; + if (!PyArg_ParseTuple(args, "s#", &values, &length)) + return NULL; + + if (!self->image->palette) { + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; + } + + if (length > 256) { + PyErr_SetString(PyExc_ValueError, outside_palette); + return NULL; + } + + strcpy(self->image->palette->mode, "RGBA"); + for (i=0; iimage->palette->palette[i*4+3] = (UINT8) values[i]; + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* _putpixel(ImagingObject* self, PyObject* args) { Imaging im; @@ -1425,13 +1509,13 @@ _putpixel(ImagingObject* self, PyObject* args) int x, y; PyObject* color; if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) - return NULL; + return NULL; im = self->image; - + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return NULL; + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; } if (!getink(color, im, ink)) @@ -1445,18 +1529,18 @@ _putpixel(ImagingObject* self, PyObject* args) } #ifdef WITH_RANKFILTER -static PyObject* +static PyObject* _rankfilter(ImagingObject* self, PyObject* args) { int size, rank; if (!PyArg_ParseTuple(args, "ii", &size, &rank)) - return NULL; + return NULL; return PyImagingNew(ImagingRankFilter(self->image, size, rank)); } #endif -static PyObject* +static PyObject* _resize(ImagingObject* self, PyObject* args) { Imaging imIn; @@ -1465,38 +1549,38 @@ _resize(ImagingObject* self, PyObject* args) int xsize, ysize; int filter = IMAGING_TRANSFORM_NEAREST; if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) - return NULL; + return NULL; imIn = self->image; imOut = ImagingNew(imIn->mode, xsize, ysize); if (imOut) - (void) ImagingResize(imOut, imIn, filter); - + (void) ImagingResize(imOut, imIn, filter); + return PyImagingNew(imOut); } -static PyObject* +static PyObject* _rotate(ImagingObject* self, PyObject* args) { Imaging imOut; Imaging imIn; - + double theta; int filter = IMAGING_TRANSFORM_NEAREST; if (!PyArg_ParseTuple(args, "d|i", &theta, &filter)) - return NULL; + return NULL; imIn = self->image; theta = fmod(theta, 360.0); if (theta < 0.0) - theta += 360; + theta += 360; if (filter && imIn->type != IMAGING_TYPE_SPECIAL) { /* Rotate with resampling filter */ imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); - (void) ImagingRotate(imOut, imIn, theta, filter); + (void) ImagingRotate(imOut, imIn, theta, filter); } else if (theta == 90.0 || theta == 270.0) { /* Use fast version */ imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); @@ -1527,7 +1611,7 @@ _rotate(ImagingObject* self, PyObject* args) #define IS_RGB(mode)\ (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX")) -static PyObject* +static PyObject* im_setmode(ImagingObject* self, PyObject* args) { /* attempt to modify the mode of an image in place */ @@ -1537,7 +1621,7 @@ im_setmode(ImagingObject* self, PyObject* args) char* mode; int modelen; if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) - return NULL; + return NULL; im = self->image; @@ -1565,7 +1649,7 @@ im_setmode(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _stretch(ImagingObject* self, PyObject* args) { Imaging imIn; @@ -1575,14 +1659,14 @@ _stretch(ImagingObject* self, PyObject* args) int xsize, ysize; int filter = IMAGING_TRANSFORM_NEAREST; if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) - return NULL; + return NULL; imIn = self->image; /* two-pass resize: minimize size of intermediate image */ - if (imIn->xsize * ysize < xsize * imIn->ysize) + if ((Py_ssize_t) imIn->xsize * ysize < (Py_ssize_t) xsize * imIn->ysize) imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize); - else + else imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); if (!imTemp) return NULL; @@ -1607,11 +1691,11 @@ _stretch(ImagingObject* self, PyObject* args) } ImagingDelete(imTemp); - + return PyImagingNew(imOut); } -static PyObject* +static PyObject* _transform2(ImagingObject* self, PyObject* args) { static const char* wrong_number = "wrong number of matrix entries"; @@ -1629,10 +1713,10 @@ _transform2(ImagingObject* self, PyObject* args) int fill = 1; if (!PyArg_ParseTuple(args, "(iiii)O!iO|ii", &x0, &y0, &x1, &y1, - &Imaging_Type, &imagep, + &Imaging_Type, &imagep, &method, &data, &filter, &fill)) - return NULL; + return NULL; switch (method) { case IMAGING_TRANSFORM_AFFINE: @@ -1686,7 +1770,7 @@ _transform2(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _transpose(ImagingObject* self, PyObject* args) { Imaging imIn; @@ -1694,10 +1778,10 @@ _transpose(ImagingObject* self, PyObject* args) int op; if (!PyArg_ParseTuple(args, "i", &op)) - return NULL; + return NULL; imIn = self->image; - + switch (op) { case 0: /* flip left right */ case 1: /* flip top bottom */ @@ -1736,7 +1820,7 @@ _transpose(ImagingObject* self, PyObject* args) } #ifdef WITH_UNSHARPMASK -static PyObject* +static PyObject* _unsharp_mask(ImagingObject* self, PyObject* args) { Imaging imIn; @@ -1762,25 +1846,25 @@ _unsharp_mask(ImagingObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -static PyObject* +static PyObject* _isblock(ImagingObject* self, PyObject* args) { return PyInt_FromLong((long) self->image->block); } -static PyObject* +static PyObject* _getbbox(ImagingObject* self, PyObject* args) { int bbox[4]; if (!ImagingGetBBox(self->image, bbox)) { - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(Py_None); + return Py_None; } return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]); } -static PyObject* +static PyObject* _getcolors(ImagingObject* self, PyObject* args) { ImagingColorItem* items; @@ -1789,7 +1873,7 @@ _getcolors(ImagingObject* self, PyObject* args) int maxcolors = 256; if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) - return NULL; + return NULL; items = ImagingGetColors(self->image, maxcolors, &colors); if (!items) @@ -1814,7 +1898,7 @@ _getcolors(ImagingObject* self, PyObject* args) return out; } -static PyObject* +static PyObject* _getextrema(ImagingObject* self, PyObject* args) { union { @@ -1823,7 +1907,7 @@ _getextrema(ImagingObject* self, PyObject* args) FLOAT32 f[2]; } extrema; int status; - + status = ImagingGetExtrema(self->image, &extrema); if (status < 0) return NULL; @@ -1831,7 +1915,7 @@ _getextrema(ImagingObject* self, PyObject* args) if (status) switch (self->image->type) { case IMAGING_TYPE_UINT8: - return Py_BuildValue("ii", extrema.u[0], extrema.u[1]); + return Py_BuildValue("BB", extrema.u[0], extrema.u[1]); case IMAGING_TYPE_INT32: return Py_BuildValue("ii", extrema.i[0], extrema.i[1]); case IMAGING_TYPE_FLOAT32: @@ -1842,7 +1926,7 @@ _getextrema(ImagingObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _getprojection(ImagingObject* self, PyObject* args) { unsigned char* xprofile; @@ -1853,15 +1937,16 @@ _getprojection(ImagingObject* self, PyObject* args) yprofile = malloc(self->image->ysize); if (xprofile == NULL || yprofile == NULL) { - free(xprofile); - free(yprofile); - return PyErr_NoMemory(); + free(xprofile); + free(yprofile); + return PyErr_NoMemory(); } ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); - result = Py_BuildValue("s#s#", xprofile, self->image->xsize, - yprofile, self->image->ysize); + result = Py_BuildValue(PY_ARG_BYTES_LENGTH PY_ARG_BYTES_LENGTH, + xprofile, self->image->xsize, + yprofile, self->image->ysize); free(xprofile); free(yprofile); @@ -1871,45 +1956,45 @@ _getprojection(ImagingObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -static PyObject* +static PyObject* _getband(ImagingObject* self, PyObject* args) { int band; if (!PyArg_ParseTuple(args, "i", &band)) - return NULL; + return NULL; return PyImagingNew(ImagingGetBand(self->image, band)); } -static PyObject* +static PyObject* _fillband(ImagingObject* self, PyObject* args) { int band; int color; if (!PyArg_ParseTuple(args, "ii", &band, &color)) - return NULL; + return NULL; if (!ImagingFillBand(self->image, band, color)) return NULL; - + Py_INCREF(Py_None); return Py_None; } -static PyObject* +static PyObject* _putband(ImagingObject* self, PyObject* args) { ImagingObject* imagep; int band; if (!PyArg_ParseTuple(args, "O!i", - &Imaging_Type, &imagep, - &band)) - return NULL; + &Imaging_Type, &imagep, + &band)) + return NULL; if (!ImagingPutBand(self->image, imagep->image, band)) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; @@ -1919,68 +2004,68 @@ _putband(ImagingObject* self, PyObject* args) #ifdef WITH_IMAGECHOPS -static PyObject* +static PyObject* _chop_invert(ImagingObject* self, PyObject* args) { return PyImagingNew(ImagingNegative(self->image)); } -static PyObject* +static PyObject* _chop_lighter(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopLighter(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_darker(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopDarker(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_difference(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopDifference(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_multiply(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopMultiply(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_screen(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopScreen(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_add(ImagingObject* self, PyObject* args) { ImagingObject* imagep; @@ -1991,14 +2076,14 @@ _chop_add(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) - return NULL; + &scale, &offset)) + return NULL; return PyImagingNew(ImagingChopAdd(self->image, imagep->image, - scale, offset)); + scale, offset)); } -static PyObject* +static PyObject* _chop_subtract(ImagingObject* self, PyObject* args) { ImagingObject* imagep; @@ -2009,64 +2094,64 @@ _chop_subtract(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) - return NULL; + &scale, &offset)) + return NULL; return PyImagingNew(ImagingChopSubtract(self->image, imagep->image, - scale, offset)); + scale, offset)); } -static PyObject* +static PyObject* _chop_and(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopAnd(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_or(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopOr(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_xor(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopXor(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_add_modulo(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image)); } -static PyObject* +static PyObject* _chop_subtract_modulo(ImagingObject* self, PyObject* args) { ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image)); } @@ -2088,19 +2173,19 @@ _font_new(PyObject* self_, PyObject* args) ImagingObject* imagep; unsigned char* glyphdata; int glyphdata_length; - if (!PyArg_ParseTuple(args, "O!s#", - &Imaging_Type, &imagep, - &glyphdata, &glyphdata_length)) + if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH, + &Imaging_Type, &imagep, + &glyphdata, &glyphdata_length)) return NULL; if (glyphdata_length != 256 * 20) { - PyErr_SetString(PyExc_ValueError, wrong_length); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; } self = PyObject_New(ImagingFontObject, &ImagingFont_Type); if (self == NULL) - return NULL; + return NULL; /* glyph bitmap */ self->bitmap = imagep->image; @@ -2218,12 +2303,6 @@ static struct PyMethodDef _font_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_font_getattr(ImagingFontObject* self, char* name) -{ - return Py_FindMethod(_font_methods, (PyObject*) self, name); -} - /* -------------------------------------------------------------------- */ static PyObject* @@ -2238,7 +2317,7 @@ _draw_new(PyObject* self_, PyObject* args) self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type); if (self == NULL) - return NULL; + return NULL; /* keep a reference to the image object */ Py_INCREF(imagep); @@ -2260,7 +2339,7 @@ _draw_dealloc(ImagingDrawObject* self) extern int PyPath_Flatten(PyObject* data, double **xy); -static PyObject* +static PyObject* _draw_ink(ImagingDrawObject* self, PyObject* args) { INT32 ink = 0; @@ -2275,7 +2354,7 @@ _draw_ink(ImagingDrawObject* self, PyObject* args) return PyInt_FromLong((int) ink); } -static PyObject* +static PyObject* _draw_arc(ImagingDrawObject* self, PyObject* args) { int x0, y0, x1, y1; @@ -2285,7 +2364,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "(iiii)iii|i", &x0, &y0, &x1, &y1, &start, &end, &ink)) - return NULL; + return NULL; if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end, &ink, op) < 0) @@ -2295,7 +2374,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_bitmap(ImagingDrawObject* self, PyObject* args) { double *xy; @@ -2305,17 +2384,16 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) ImagingObject* bitmap; int ink; if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 1) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 1 coordinate" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 1 coordinate" + ); + return NULL; } n = ImagingDrawBitmap( @@ -2332,7 +2410,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_chord(ImagingDrawObject* self, PyObject* args) { int x0, y0, x1, y1; @@ -2340,7 +2418,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) int start, end; if (!PyArg_ParseTuple(args, "(iiii)iiii", &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) - return NULL; + return NULL; if (ImagingDrawChord(self->image->image, x0, y0, x1, y1, start, end, &ink, fill, self->blend) < 0) @@ -2350,7 +2428,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_ellipse(ImagingDrawObject* self, PyObject* args) { double* xy; @@ -2360,24 +2438,24 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; } - n = ImagingDrawEllipse( - self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3], - &ink, fill, self->blend - ); - + n = ImagingDrawEllipse(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + &ink, fill, self->blend + ); + free(xy); if (n < 0) @@ -2387,23 +2465,23 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_line(ImagingDrawObject* self, PyObject* args) { int x0, y0, x1, y1; int ink; if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink)) - return NULL; + return NULL; if (ImagingDrawLine(self->image->image, x0, y0, x1, y1, &ink, self->blend) < 0) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; } -static PyObject* +static PyObject* _draw_lines(ImagingDrawObject* self, PyObject* args) { double *xy; @@ -2413,15 +2491,15 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) int ink; int width = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (width <= 1) { double *p = NULL; - for (i = 0; i < n-1; i++) { + for (i = 0; i < n-1; i++) { p = &xy[i+i]; if (ImagingDrawLine( self->image->image, @@ -2456,22 +2534,22 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_point(ImagingDrawObject* self, PyObject* args) { int x, y; int ink; if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink)) - return NULL; + return NULL; if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; } -static PyObject* +static PyObject* _draw_points(ImagingDrawObject* self, PyObject* args) { double *xy; @@ -2480,19 +2558,19 @@ _draw_points(ImagingDrawObject* self, PyObject* args) PyObject *data; int ink; if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; for (i = 0; i < n; i++) { - double *p = &xy[i+i]; - if (ImagingDrawPoint(self->image->image, (int) p[0], (int) p[1], + double *p = &xy[i+i]; + if (ImagingDrawPoint(self->image->image, (int) p[0], (int) p[1], &ink, self->blend) < 0) { - free(xy); - return NULL; - } + free(xy); + return NULL; + } } free(xy); @@ -2501,12 +2579,12 @@ _draw_points(ImagingDrawObject* self, PyObject* args) return Py_None; } -#ifdef WITH_ARROW +#ifdef WITH_ARROW /* from outline.c */ extern ImagingOutline PyOutline_AsOutline(PyObject* outline); -static PyObject* +static PyObject* _draw_outline(ImagingDrawObject* self, PyObject* args) { ImagingOutline outline; @@ -2515,7 +2593,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) - return NULL; + return NULL; outline = PyOutline_AsOutline(outline_); if (!outline) { @@ -2525,7 +2603,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) if (ImagingDrawOutline(self->image->image, outline, &ink, fill, self->blend) < 0) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; @@ -2533,7 +2611,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) #endif -static PyObject* +static PyObject* _draw_pieslice(ImagingDrawObject* self, PyObject* args) { int x0, y0, x1, y1; @@ -2541,7 +2619,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) int start, end; if (!PyArg_ParseTuple(args, "(iiii)iiii", &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) - return NULL; + return NULL; if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1, start, end, &ink, fill, self->blend) < 0) @@ -2551,7 +2629,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_polygon(ImagingDrawObject* self, PyObject* args) { double *xy; @@ -2562,33 +2640,32 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n < 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain at least 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain at least 2 coordinates" + ); + return NULL; } /* Copy list of vertices to array */ ixy = (int*) malloc(n * 2 * sizeof(int)); for (i = 0; i < n; i++) { - ixy[i+i] = (int) xy[i+i]; - ixy[i+i+1] = (int) xy[i+i+1]; + ixy[i+i] = (int) xy[i+i]; + ixy[i+i+1] = (int) xy[i+i+1]; } free(xy); if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, self->blend) < 0) { - free(ixy); - return NULL; + free(ixy); + return NULL; } free(ixy); @@ -2597,7 +2674,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw_rectangle(ImagingDrawObject* self, PyObject* args) { double* xy; @@ -2607,24 +2684,24 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; } - n = ImagingDrawRectangle( - self->image->image, (int) xy[0], (int) xy[1], - (int) xy[2], (int) xy[3], &ink, fill, self->blend - ); - + n = ImagingDrawRectangle(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + &ink, fill, self->blend + ); + free(xy); if (n < 0) @@ -2656,12 +2733,6 @@ static struct PyMethodDef _draw_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_draw_getattr(ImagingDrawObject* self, char* name) -{ - return Py_FindMethod(_draw_methods, (PyObject*) self, name); -} - #endif @@ -2676,7 +2747,7 @@ pixel_access_new(ImagingObject* imagep, PyObject* args) self = PyObject_New(PixelAccessObject, &PixelAccess_Type); if (self == NULL) - return NULL; + return NULL; /* keep a reference to the image object */ Py_INCREF(imagep); @@ -2720,8 +2791,8 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) return -1; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return -1; + PyErr_SetString(PyExc_IndexError, outside_image); + return -1; } if (!color) /* FIXME: raise exception? */ @@ -2736,12 +2807,12 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) } /* -------------------------------------------------------------------- */ -/* EFFECTS (experimental) */ +/* EFFECTS (experimental) */ /* -------------------------------------------------------------------- */ #ifdef WITH_EFFECTS -static PyObject* +static PyObject* _effect_mandelbrot(ImagingObject* self, PyObject* args) { int xsize = 512; @@ -2755,29 +2826,29 @@ _effect_mandelbrot(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "|(ii)(dddd)i", &xsize, &ysize, &extent[0], &extent[1], &extent[2], &extent[3], &quality)) - return NULL; + return NULL; return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality)); } -static PyObject* +static PyObject* _effect_noise(ImagingObject* self, PyObject* args) { int xsize, ysize; float sigma = 128; if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) - return NULL; + return NULL; return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma)); } -static PyObject* +static PyObject* _effect_spread(ImagingObject* self, PyObject* args) { int dist; if (!PyArg_ParseTuple(args, "i", &dist)) - return NULL; + return NULL; return PyImagingNew(ImagingEffectSpread(self->image, dist)); } @@ -2785,10 +2856,10 @@ _effect_spread(ImagingObject* self, PyObject* args) #endif /* -------------------------------------------------------------------- */ -/* UTILITIES */ +/* UTILITIES */ /* -------------------------------------------------------------------- */ -static PyObject* +static PyObject* _crc32(PyObject* self, PyObject* args) { unsigned char* buffer; @@ -2798,8 +2869,9 @@ _crc32(PyObject* self, PyObject* args) hi = lo = 0; - if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo)) - return NULL; + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"|(ii)", + &buffer, &bytes, &hi, &lo)) + return NULL; crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF); @@ -2808,48 +2880,47 @@ _crc32(PyObject* self, PyObject* args) return Py_BuildValue("ii", (crc >> 16) & 0xFFFF, crc & 0xFFFF); } -static PyObject* +static PyObject* _getcodecstatus(PyObject* self, PyObject* args) { int status; char* msg; if (!PyArg_ParseTuple(args, "i", &status)) - return NULL; + return NULL; switch (status) { case IMAGING_CODEC_OVERRUN: - msg = "buffer overrun"; break; + msg = "buffer overrun"; break; case IMAGING_CODEC_BROKEN: - msg = "broken data stream"; break; + msg = "broken data stream"; break; case IMAGING_CODEC_UNKNOWN: - msg = "unrecognized data stream contents"; break; + msg = "unrecognized data stream contents"; break; case IMAGING_CODEC_CONFIG: - msg = "codec configuration error"; break; + msg = "codec configuration error"; break; case IMAGING_CODEC_MEMORY: - msg = "out of memory"; break; + msg = "out of memory"; break; default: - Py_INCREF(Py_None); - return Py_None; + Py_RETURN_NONE; } - return PyString_FromString(msg); + return PyUnicode_FromString(msg); } /* -------------------------------------------------------------------- */ -/* DEBUGGING HELPERS */ +/* DEBUGGING HELPERS */ /* -------------------------------------------------------------------- */ #ifdef WITH_DEBUG -static PyObject* +static PyObject* _save_ppm(ImagingObject* self, PyObject* args) { char* filename; if (!PyArg_ParseTuple(args, "s", &filename)) - return NULL; + return NULL; if (!ImagingSavePPM(self->image, filename)) return NULL; @@ -2876,6 +2947,7 @@ static struct PyMethodDef methods[] = { {"convert", (PyCFunction)_convert, 1}, {"convert2", (PyCFunction)_convert2, 1}, {"convert_matrix", (PyCFunction)_convert_matrix, 1}, + {"convert_transparent", (PyCFunction)_convert_transparent, 1}, {"copy", (PyCFunction)_copy, 1}, {"copy2", (PyCFunction)_copy2, 1}, #ifdef WITH_CRACKCODE @@ -2917,10 +2989,12 @@ static struct PyMethodDef methods[] = { {"fillband", (PyCFunction)_fillband, 1}, {"setmode", (PyCFunction)im_setmode, 1}, - + {"getpalette", (PyCFunction)_getpalette, 1}, + {"getpalettemode", (PyCFunction)_getpalettemode, 1}, {"putpalette", (PyCFunction)_putpalette, 1}, {"putpalettealpha", (PyCFunction)_putpalettealpha, 1}, + {"putpalettealphas", (PyCFunction)_putpalettealphas, 1}, #ifdef WITH_IMAGECHOPS /* Channel operations (ImageChops) */ @@ -2964,29 +3038,48 @@ static struct PyMethodDef methods[] = { /* attributes */ -static PyObject* -_getattr(ImagingObject* self, char* name) +static PyObject* +_getattr_mode(ImagingObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (strcmp(name, "mode") == 0) - return PyString_FromString(self->image->mode); - if (strcmp(name, "size") == 0) - return Py_BuildValue("ii", self->image->xsize, self->image->ysize); - if (strcmp(name, "bands") == 0) - return PyInt_FromLong(self->image->bands); - if (strcmp(name, "id") == 0) - return PyInt_FromLong((long) self->image); - if (strcmp(name, "ptr") == 0) - return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + return PyUnicode_FromString(self->image->mode); } +static PyObject* +_getattr_size(ImagingObject* self, void* closure) +{ + return Py_BuildValue("ii", self->image->xsize, self->image->ysize); +} + +static PyObject* +_getattr_bands(ImagingObject* self, void* closure) +{ + return PyInt_FromLong(self->image->bands); +} + +static PyObject* +_getattr_id(ImagingObject* self, void* closure) +{ + return PyInt_FromSsize_t((Py_ssize_t) self->image); +} + +static PyObject* +_getattr_ptr(ImagingObject* self, void* closure) +{ +#if (PY_VERSION_HEX >= 0x02070000 && PY_VERSION_HEX < 0x03000000) || PY_VERSION_HEX >= 0x03010000 + return PyCapsule_New(self->image, IMAGING_MAGIC, NULL); +#else + return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); +#endif +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { "bands", (getter) _getattr_bands }, + { "id", (getter) _getattr_id }, + { "ptr", (getter) _getattr_ptr }, + { NULL } +}; /* basic sequence semantics */ @@ -2995,7 +3088,7 @@ image_length(ImagingObject *self) { Imaging im = self->image; - return im->xsize * im->ysize; + return (Py_ssize_t) im->xsize * im->ysize; } static PyObject * @@ -3014,7 +3107,7 @@ image_item(ImagingObject *self, Py_ssize_t i) } static PySequenceMethods image_as_sequence = { - (inquiry) image_length, /*sq_length*/ + (lenfunc) image_length, /*sq_length*/ (binaryfunc) NULL, /*sq_concat*/ (ssizeargfunc) NULL, /*sq_repeat*/ (ssizeargfunc) image_item, /*sq_item*/ @@ -3026,64 +3119,123 @@ static PySequenceMethods image_as_sequence = { /* type description */ -statichere PyTypeObject Imaging_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "ImagingCore", /*tp_name*/ - sizeof(ImagingObject), /*tp_size*/ - 0, /*tp_itemsize*/ +static PyTypeObject Imaging_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingCore", /*tp_name*/ + sizeof(ImagingObject), /*tp_size*/ + 0, /*tp_itemsize*/ /* methods */ - (destructor)_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ 0, /*tp_as_number */ &image_as_sequence, /*tp_as_sequence */ 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; #ifdef WITH_IMAGEDRAW -statichere PyTypeObject ImagingFont_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "ImagingFont", /*tp_name*/ - sizeof(ImagingFontObject), /*tp_size*/ - 0, /*tp_itemsize*/ +static PyTypeObject ImagingFont_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingFont", /*tp_name*/ + sizeof(ImagingFontObject), /*tp_size*/ + 0, /*tp_itemsize*/ /* methods */ - (destructor)_font_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)_font_getattr, /*tp_getattr*/ + (destructor)_font_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _font_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; -statichere PyTypeObject ImagingDraw_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "ImagingDraw", /*tp_name*/ - sizeof(ImagingDrawObject), /*tp_size*/ - 0, /*tp_itemsize*/ +static PyTypeObject ImagingDraw_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingDraw", /*tp_name*/ + sizeof(ImagingDrawObject), /*tp_size*/ + 0, /*tp_itemsize*/ /* methods */ - (destructor)_draw_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)_draw_getattr, /*tp_getattr*/ + (destructor)_draw_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _draw_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; #endif static PyMappingMethods pixel_access_as_mapping = { - (inquiry) NULL, /*mp_length*/ + (lenfunc) NULL, /*mp_length*/ (binaryfunc) pixel_access_getitem, /*mp_subscript*/ (objobjargproc) pixel_access_setitem, /*mp_ass_subscript*/ }; /* type description */ -statichere PyTypeObject PixelAccess_Type = { - PyObject_HEAD_INIT(NULL) - 0, "PixelAccess", sizeof(PixelAccessObject), 0, +static PyTypeObject PixelAccess_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "PixelAccess", sizeof(PixelAccessObject), 0, /* methods */ (destructor)pixel_access_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ @@ -3109,6 +3261,7 @@ extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args); @@ -3127,6 +3280,7 @@ extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args); /* Display support etc (in display.c) */ #ifdef WIN32 @@ -3152,6 +3306,7 @@ extern PyObject* PyImaging_MapBuffer(PyObject* self, PyObject* args); static PyMethodDef functions[] = { /* Object factories */ + {"alpha_composite", (PyCFunction)_alpha_composite, 1}, {"blend", (PyCFunction)_blend, 1}, {"fill", (PyCFunction)_fill, 1}, {"new", (PyCFunction)_new, 1}, @@ -3175,6 +3330,10 @@ static PyMethodDef functions[] = { {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, #endif {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, +#ifdef HAVE_LIBTIFF + {"libtiff_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1}, + {"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1}, +#endif {"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1}, {"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1}, {"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1}, @@ -3237,7 +3396,7 @@ static PyMethodDef functions[] = { #ifdef WITH_IMAGEPATH {"path", (PyCFunction)PyPath_Create, 1}, #endif - + /* Experimental arrow graphics stuff */ #ifdef WITH_ARROW {"outline", (PyCFunction)PyOutline_Create, 1}, @@ -3246,36 +3405,77 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) -init_imaging(void) -{ - PyObject* m; - PyObject* d; +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); + + /* Ready object types */ + if (PyType_Ready(&Imaging_Type) < 0) + return -1; - /* Patch object type */ - Imaging_Type.ob_type = &PyType_Type; #ifdef WITH_IMAGEDRAW - ImagingFont_Type.ob_type = &PyType_Type; - ImagingDraw_Type.ob_type = &PyType_Type; + if (PyType_Ready(&ImagingFont_Type) < 0) + return -1; + + if (PyType_Ready(&ImagingDraw_Type) < 0) + return -1; #endif - PixelAccess_Type.ob_type = &PyType_Type; + if (PyType_Ready(&PixelAccess_Type) < 0) + return -1; ImagingAccessInit(); - m = Py_InitModule("_imaging", functions); - d = PyModule_GetDict(m); - #ifdef HAVE_LIBJPEG { extern const char* ImagingJpegVersion(void); - PyDict_SetItemString(d, "jpeglib_version", PyString_FromString(ImagingJpegVersion())); + PyDict_SetItemString(d, "jpeglib_version", PyUnicode_FromString(ImagingJpegVersion())); } #endif #ifdef HAVE_LIBZ + /* zip encoding strategies */ + PyModule_AddIntConstant(m, "DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY); + PyModule_AddIntConstant(m, "FILTERED", Z_FILTERED); + PyModule_AddIntConstant(m, "HUFFMAN_ONLY", Z_HUFFMAN_ONLY); + PyModule_AddIntConstant(m, "RLE", Z_RLE); + PyModule_AddIntConstant(m, "FIXED", Z_FIXED); { extern const char* ImagingZipVersion(void); - PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion())); + PyDict_SetItemString(d, "zlib_version", PyUnicode_FromString(ImagingZipVersion())); } #endif + + PyDict_SetItemString(d, "PILLOW_VERSION", PyUnicode_FromString(PILLOW_VERSION)); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imaging(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imaging", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imaging(void) +{ + PyObject* m = Py_InitModule("_imaging", functions); + setup_module(m); +} +#endif + diff --git a/_imagingcms.c b/_imagingcms.c index e9a3b00c5..99da647ae 100644 --- a/_imagingcms.c +++ b/_imagingcms.c @@ -1,4 +1,4 @@ -/* +/* * pyCMS * a Python / PIL interface to the littleCMS ICC Color Management System * Copyright (C) 2002-2003 Kevin Cazabon @@ -6,11 +6,11 @@ * http://www.cazabon.com * Adapted/reworked for PIL by Fredrik Lundh * Copyright (c) 2009 Fredrik Lundh - * + * * pyCMS home page: http://www.cazabon.com/pyCMS * littleCMS home page: http://www.littlecms.com * (littleCMS is Copyright (C) 1998-2001 Marti Maria) - * + * * Originally released under LGPL. Graciously donated to PIL in * March 2009, for distribution under the standard PIL license */ @@ -24,27 +24,22 @@ http://www.cazabon.com\n\ " #include "Python.h" -#include "lcms.h" +#include "lcms2.h" #include "Imaging.h" - -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if LCMS_VERSION < 117 -#define LCMSBOOL BOOL -#endif +#include "py3.h" #ifdef WIN32 +#include +#include #include #endif -#define PYCMSVERSION "0.1.0 pil" +#define PYCMSVERSION "1.0.0 pil" /* version history */ /* + 1.0.0 pil Integrating littleCMS2 0.1.0 pil integration & refactoring 0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003 - fixed some memory holes in how transforms/profiles were created and passed back to Python @@ -59,7 +54,7 @@ http://www.cazabon.com\n\ /* known to-do list with current version: Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all PIL modes (it probably isn't for the more obscure ones) - + Add support for creating custom RGB profiles on the fly Add support for checking presence of a specific tag in a profile Add support for other littleCMS features as required @@ -83,9 +78,9 @@ typedef struct { cmsHPROFILE profile; } CmsProfileObject; -staticforward PyTypeObject CmsProfile_Type; +static PyTypeObject CmsProfile_Type; -#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type) +#define CmsProfile_Check(op) (Py_TYPE(op) == &CmsProfile_Type) static PyObject* cms_profile_new(cmsHPROFILE profile) @@ -110,8 +105,6 @@ cms_profile_open(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - hProfile = cmsOpenProfileFromFile(sProfile, "r"); if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile file"); @@ -128,14 +121,19 @@ cms_profile_fromstring(PyObject* self, PyObject* args) char* pProfile; int nProfile; +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:profile_frombytes", &pProfile, &nProfile)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile)) return NULL; - - cmsErrorAction(LCMS_ERROR_IGNORE); +#endif hProfile = cmsOpenProfileFromMem(pProfile, nProfile); - if (!hProfile) + if (!hProfile) { PyErr_SetString(PyExc_IOError, "cannot open profile from string"); + return NULL; + } return cms_profile_new(hProfile); } @@ -156,9 +154,9 @@ typedef struct { cmsHTRANSFORM transform; } CmsTransformObject; -staticforward PyTypeObject CmsTransform_Type; +static PyTypeObject CmsTransform_Type; -#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type) +#define CmsTransform_Check(op) (Py_TYPE(op) == &CmsTransform_Type) static PyObject* cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out) @@ -188,25 +186,25 @@ cms_transform_dealloc(CmsTransformObject* self) /* internal functions */ static const char* -findICmode(icColorSpaceSignature cs) +findICmode(cmsColorSpaceSignature cs) { switch (cs) { - case icSigXYZData: return "XYZ"; - case icSigLabData: return "LAB"; - case icSigLuvData: return "LUV"; - case icSigYCbCrData: return "YCbCr"; - case icSigYxyData: return "YXY"; - case icSigRgbData: return "RGB"; - case icSigGrayData: return "L"; - case icSigHsvData: return "HSV"; - case icSigHlsData: return "HLS"; - case icSigCmykData: return "CMYK"; - case icSigCmyData: return "CMY"; + case cmsSigXYZData: return "XYZ"; + case cmsSigLabData: return "LAB"; + case cmsSigLuvData: return "LUV"; + case cmsSigYCbCrData: return "YCbCr"; + case cmsSigYxyData: return "YXY"; + case cmsSigRgbData: return "RGB"; + case cmsSigGrayData: return "L"; + case cmsSigHsvData: return "HSV"; + case cmsSigHlsData: return "HLS"; + case cmsSigCmykData: return "CMYK"; + case cmsSigCmyData: return "CMY"; default: return ""; /* other TBA */ } } -static DWORD +static cmsUInt32Number findLCMStype(char* PILmode) { if (strcmp(PILmode, "RGB") == 0) { @@ -239,6 +237,10 @@ findLCMStype(char* PILmode) else if (strcmp(PILmode, "YCC") == 0) { return TYPE_YCbCr_8; } + else if (strcmp(PILmode, "LAB") == 0) { + // LabX equvalent like ALab, but not reversed -- no #define in lcms2 + return (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)); + } else { /* take a wild guess... but you probably should fail instead. */ @@ -265,12 +267,10 @@ pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) } static cmsHTRANSFORM -_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS) +_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -289,12 +289,10 @@ _buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sIn } static cmsHTRANSFORM -_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS) +_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, cmsUInt32Number cmsFLAGS) { cmsHTRANSFORM hTransform; - cmsErrorAction(LCMS_ERROR_IGNORE); - Py_BEGIN_ALLOW_THREADS /* create the transform */ @@ -332,8 +330,6 @@ buildTransform(PyObject *self, PyObject *args) { if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS); if (!transform) @@ -359,10 +355,8 @@ buildProofTransform(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS); - + if (!transform) return NULL; @@ -373,21 +367,19 @@ buildProofTransform(PyObject *self, PyObject *args) static PyObject * cms_transform_apply(CmsTransformObject *self, PyObject *args) { - long idIn; - long idOut; + Py_ssize_t idIn; + Py_ssize_t idOut; Imaging im; Imaging imOut; int result; - if (!PyArg_ParseTuple(args, "ll:apply", &idIn, &idOut)) + if (!PyArg_ParseTuple(args, "nn:apply", &idIn, &idOut)) return NULL; im = (Imaging) idIn; imOut = (Imaging) idOut; - cmsErrorAction(LCMS_ERROR_IGNORE); - result = pyCMSdoTransform(im, imOut, self->transform); return Py_BuildValue("i", result); @@ -401,32 +393,34 @@ createProfile(PyObject *self, PyObject *args) { char *sColorSpace; cmsHPROFILE hProfile; - int iColorTemp = 0; - LPcmsCIExyY whitePoint = NULL; - LCMSBOOL result; + cmsFloat64Number dColorTemp = 0.0; + cmsCIExyY whitePoint; + cmsBool result; - if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp)) + if (!PyArg_ParseTuple(args, "s|d:createProfile", &sColorSpace, &dColorTemp)) return NULL; - cmsErrorAction(LCMS_ERROR_IGNORE); - if (strcmp(sColorSpace, "LAB") == 0) { - if (iColorTemp > 0) { - result = cmsWhitePointFromTemp(iColorTemp, whitePoint); + if (dColorTemp > 0.0) { + result = cmsWhitePointFromTemp(&whitePoint, dColorTemp); if (!result) { - PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin"); + PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be float in degrees Kelvin"); return NULL; } - hProfile = cmsCreateLabProfile(whitePoint); - } else - hProfile = cmsCreateLabProfile(NULL); + hProfile = cmsCreateLab2Profile(&whitePoint); + } else { + hProfile = cmsCreateLab2Profile(NULL); + } } - else if (strcmp(sColorSpace, "XYZ") == 0) + else if (strcmp(sColorSpace, "XYZ") == 0) { hProfile = cmsCreateXYZProfile(); - else if (strcmp(sColorSpace, "sRGB") == 0) + } + else if (strcmp(sColorSpace, "sRGB") == 0) { hProfile = cmsCreate_sRGBProfile(); - else + } + else { hProfile = NULL; + } if (!hProfile) { PyErr_SetString(PyExc_ValueError, "failed to create requested color space"); @@ -442,7 +436,7 @@ createProfile(PyObject *self, PyObject *args) static PyObject * cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) { - LCMSBOOL result; + cmsBool result; int intent; int direction; @@ -461,7 +455,7 @@ static PyObject * cms_get_display_profile_win32(PyObject* self, PyObject* args) { char filename[MAX_PATH]; - DWORD filename_size; + cmsUInt32Number filename_size; BOOL ok; int handle = 0; @@ -480,7 +474,7 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) } if (ok) - return PyString_FromStringAndSize(filename, filename_size-1); + return PyUnicode_FromStringAndSize(filename, filename_size-1); Py_INCREF(Py_None); return Py_None; @@ -493,6 +487,7 @@ cms_get_display_profile_win32(PyObject* self, PyObject* args) static PyMethodDef pyCMSdll_methods[] = { {"profile_open", cms_profile_open, 1}, + {"profile_frombytes", cms_profile_fromstring, 1}, {"profile_fromstring", cms_profile_fromstring, 1}, /* profile and transform functions */ @@ -513,40 +508,121 @@ static struct PyMethodDef cms_profile_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_profile_getattr(CmsProfileObject* self, char* name) +static PyObject* +_profile_getattr(CmsProfileObject* self, cmsInfoType field) { - if (!strcmp(name, "product_name")) - return PyString_FromString(cmsTakeProductName(self->profile)); - if (!strcmp(name, "product_desc")) - return PyString_FromString(cmsTakeProductDesc(self->profile)); - if (!strcmp(name, "product_info")) - return PyString_FromString(cmsTakeProductInfo(self->profile)); - if (!strcmp(name, "rendering_intent")) - return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); - if (!strcmp(name, "pcs")) - return PyString_FromString(findICmode(cmsGetPCS(self->profile))); - if (!strcmp(name, "color_space")) - return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); - /* FIXME: add more properties (creation_datetime etc) */ - - return Py_FindMethod(cms_profile_methods, (PyObject*) self, name); + // UNDONE -- check that I'm getting the right fields on these. + // return PyUnicode_DecodeFSDefault(cmsTakeProductName(self->profile)); + //wchar_t buf[256]; -- UNDONE need wchar_t for unicode version. + char buf[256]; + cmsUInt32Number written; + written = cmsGetProfileInfoASCII(self->profile, + field, + "en", + "us", + buf, + 256); + if (written) { + return PyUnicode_FromString(buf); + } + // UNDONE suppressing error here by sending back blank string. + return PyUnicode_FromString(""); } -statichere PyTypeObject CmsProfile_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsProfile", sizeof(CmsProfileObject), 0, +static PyObject* +cms_profile_getattr_product_desc(CmsProfileObject* self, void* closure) +{ + // description was Description != 'Copyright' || or "%s - %s" (manufacturer, model) in 1.x + return _profile_getattr(self, cmsInfoDescription); +} + +/* use these four for the individual fields. + */ +static PyObject* +cms_profile_getattr_product_description(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoDescription); +} + +static PyObject* +cms_profile_getattr_product_model(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoModel); +} + +static PyObject* +cms_profile_getattr_product_manufacturer(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoManufacturer); +} + +static PyObject* +cms_profile_getattr_product_copyright(CmsProfileObject* self, void* closure) +{ + return _profile_getattr(self, cmsInfoCopyright); +} + +static PyObject* +cms_profile_getattr_rendering_intent(CmsProfileObject* self, void* closure) +{ + return PyInt_FromLong(cmsGetHeaderRenderingIntent(self->profile)); +} + +static PyObject* +cms_profile_getattr_pcs(CmsProfileObject* self, void* closure) +{ + return PyUnicode_DecodeFSDefault(findICmode(cmsGetPCS(self->profile))); +} + +static PyObject* +cms_profile_getattr_color_space(CmsProfileObject* self, void* closure) +{ + return PyUnicode_DecodeFSDefault(findICmode(cmsGetColorSpace(self->profile))); +} + +/* FIXME: add more properties (creation_datetime etc) */ +static struct PyGetSetDef cms_profile_getsetters[] = { + { "product_desc", (getter) cms_profile_getattr_product_desc }, + { "product_description", (getter) cms_profile_getattr_product_description }, + { "product_manufacturer", (getter) cms_profile_getattr_product_manufacturer }, + { "product_model", (getter) cms_profile_getattr_product_model }, + { "product_copyright", (getter) cms_profile_getattr_product_copyright }, + { "rendering_intent", (getter) cms_profile_getattr_rendering_intent }, + { "pcs", (getter) cms_profile_getattr_pcs }, + { "color_space", (getter) cms_profile_getattr_color_space }, + { NULL } +}; + +static PyTypeObject CmsProfile_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsProfile", sizeof(CmsProfileObject), 0, /* methods */ (destructor) cms_profile_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_profile_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_profile_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_profile_getsetters, /*tp_getset*/ }; static struct PyMethodDef cms_transform_methods[] = { @@ -554,55 +630,101 @@ static struct PyMethodDef cms_transform_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -cms_transform_getattr(CmsTransformObject* self, char* name) +static PyObject* +cms_transform_getattr_inputMode(CmsTransformObject* self, void* closure) { - if (!strcmp(name, "inputMode")) - return PyString_FromString(self->mode_in); - if (!strcmp(name, "outputMode")) - return PyString_FromString(self->mode_out); - - return Py_FindMethod(cms_transform_methods, (PyObject*) self, name); + return PyUnicode_FromString(self->mode_in); } -statichere PyTypeObject CmsTransform_Type = { - PyObject_HEAD_INIT(NULL) - 0, "CmsTransform", sizeof(CmsTransformObject), 0, +static PyObject* +cms_transform_getattr_outputMode(CmsTransformObject* self, void* closure) +{ + return PyUnicode_FromString(self->mode_out); +} + +static struct PyGetSetDef cms_transform_getsetters[] = { + { "inputMode", (getter) cms_transform_getattr_inputMode }, + { "outputMode", (getter) cms_transform_getattr_outputMode }, + { NULL } +}; + +static PyTypeObject CmsTransform_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "CmsTransform", sizeof(CmsTransformObject), 0, /* methods */ (destructor) cms_transform_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc) cms_transform_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number */ - 0, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0 /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + cms_transform_methods, /*tp_methods*/ + 0, /*tp_members*/ + cms_transform_getsetters, /*tp_getset*/ }; -DL_EXPORT(void) -init_imagingcms(void) -{ - PyObject *m; +static int +setup_module(PyObject* m) { PyObject *d; PyObject *v; - /* Patch up object types */ - CmsProfile_Type.ob_type = &PyType_Type; - CmsTransform_Type.ob_type = &PyType_Type; - - m = Py_InitModule("_imagingcms", pyCMSdll_methods); d = PyModule_GetDict(m); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); -#else - { - char buffer[100]; - sprintf(buffer, "%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); - v = PyString_FromString(buffer); - } -#endif + /* Ready object types */ + PyType_Ready(&CmsProfile_Type); + PyType_Ready(&CmsTransform_Type); + + d = PyModule_GetDict(m); + + v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); PyDict_SetItemString(d, "littlecms_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingcms(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingcms", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + pyCMSdll_methods, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingcms(void) +{ + PyObject *m = Py_InitModule("_imagingcms", pyCMSdll_methods); + setup_module(m); +} +#endif + diff --git a/_imagingft.c b/_imagingft.c index 935808718..d99bb91b2 100644 --- a/_imagingft.c +++ b/_imagingft.c @@ -35,21 +35,10 @@ #include #endif -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif +#include FT_GLYPH_H -#if PY_VERSION_HEX >= 0x01060000 -#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) -/* defining this enables unicode support (default under 1.6a1 and later) */ -#define HAVE_UNICODE -#endif -#endif - -#if !defined(Py_RETURN_NONE) -#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None -#endif +#define KEEP_PY_UNICODE +#include "py3.h" #if !defined(FT_LOAD_TARGET_MONO) #define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME @@ -70,7 +59,11 @@ struct { const char* message; } ft_errors[] = +#if defined(USE_FREETYPE_2_1) +#include FT_ERRORS_H +#else #include +#endif /* -------------------------------------------------------------------- */ /* font objects */ @@ -82,7 +75,7 @@ typedef struct { FT_Face face; } FontObject; -staticforward PyTypeObject Font_Type; +static PyTypeObject Font_Type; /* round a 26.6 pixel coordinate to the nearest larger integer */ #define PIXEL(x) ((((x)+63) & -64)>>6) @@ -110,25 +103,16 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) FontObject* self; int error; - char* filename; + char* filename = NULL; int size; int index = 0; - unsigned char* encoding = NULL; + unsigned char* encoding; + unsigned char* font_bytes; + int font_bytes_size = 0; static char* kwlist[] = { - "filename", "size", "index", "encoding", NULL + "filename", "size", "index", "encoding", "font_bytes", NULL }; -#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000 - if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist, - Py_FileSystemDefaultEncoding, &filename, - &size, &index, &encoding)) - return NULL; -#else - if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist, - &filename, &size, &index, &encoding)) - return NULL; -#endif - if (!library) { PyErr_SetString( PyExc_IOError, @@ -136,12 +120,24 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) ); return NULL; } - - self = PyObject_New(FontObject, &Font_Type); - if (!self) + if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|iss#", kwlist, + Py_FileSystemDefaultEncoding, &filename, + &size, &index, &encoding, &font_bytes, + &font_bytes_size)) return NULL; - error = FT_New_Face(library, filename, index, &self->face); + self = PyObject_New(FontObject, &Font_Type); + if (!self) { + if (filename) + PyMem_Free(filename); + return NULL; + } + + if (filename && font_bytes_size <= 0) { + error = FT_New_Face(library, filename, index, &self->face); + } else { + error = FT_New_Memory_Face(library, (FT_Byte*)font_bytes, font_bytes_size, index, &self->face); + } if (!error) error = FT_Set_Pixel_Sizes(self->face, 0, size); @@ -152,6 +148,8 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) ); error = FT_Select_Charmap(self->face, encoding_tag); } + if (filename) + PyMem_Free(filename); if (error) { PyObject_Del(self); @@ -160,11 +158,10 @@ getfont(PyObject* self_, PyObject* args, PyObject* kw) return (PyObject*) self; } - + static int font_getchar(PyObject* string, int index, FT_ULong* char_out) { -#if defined(HAVE_UNICODE) if (PyUnicode_Check(string)) { Py_UNICODE* p = PyUnicode_AS_UNICODE(string); int size = PyUnicode_GET_SIZE(string); @@ -173,7 +170,8 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = p[index]; return 1; } -#endif + +#if PY_VERSION_HEX < 0x03000000 if (PyString_Check(string)) { unsigned char* p = (unsigned char*) PyString_AS_STRING(string); int size = PyString_GET_SIZE(string); @@ -182,16 +180,18 @@ font_getchar(PyObject* string, int index, FT_ULong* char_out) *char_out = (unsigned char) p[index]; return 1; } +#endif + return 0; } static PyObject* font_getsize(FontObject* self, PyObject* args) { - int i, x; + int i, x, y_max, y_min; FT_ULong ch; FT_Face face; - int xoffset; + int xoffset, yoffset; FT_Bool kerning = FT_HAS_KERNING(self->face); FT_UInt last_index = 0; @@ -201,20 +201,23 @@ font_getsize(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getsize", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; } face = NULL; - xoffset = 0; + xoffset = yoffset = 0; + y_max = y_min = 0; for (x = i = 0; font_getchar(string, i, &ch); i++) { int index, error; + FT_BBox bbox; + FT_Glyph glyph; face = self->face; index = FT_Get_Char_Index(face, ch); if (kerning && last_index && index) { @@ -229,7 +232,20 @@ font_getsize(FontObject* self, PyObject* args) if (i == 0) xoffset = face->glyph->metrics.horiBearingX; x += face->glyph->metrics.horiAdvance; + + FT_Get_Glyph(face->glyph, &glyph); + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox); + if (bbox.yMax > y_max) + y_max = bbox.yMax; + if (bbox.yMin < y_min) + y_min = bbox.yMin; + + /* find max distance of baseline from top */ + if (face->glyph->metrics.horiBearingY > yoffset) + yoffset = face->glyph->metrics.horiBearingY; + last_index = index; + FT_Done_Glyph(glyph); } if (face) { @@ -245,12 +261,15 @@ font_getsize(FontObject* self, PyObject* args) face->glyph->metrics.horiBearingX; if (offset < 0) x -= offset; + /* difference between the font ascender and the distance of + * the baseline from the top */ + yoffset = PIXEL(self->face->size->metrics.ascender - yoffset); } return Py_BuildValue( "(ii)(ii)", - PIXEL(x), PIXEL(self->face->size->metrics.height), - PIXEL(xoffset), 0 + PIXEL(x), PIXEL(y_max - y_min), + PIXEL(xoffset), yoffset ); } @@ -267,10 +286,10 @@ font_getabc(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O:getabc", &string)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -285,7 +304,7 @@ font_getabc(FontObject* self, PyObject* args) return geterror(error); a = face->glyph->metrics.horiBearingX / 64.0; b = face->glyph->metrics.width / 64.0; - c = (face->glyph->metrics.horiAdvance - + c = (face->glyph->metrics.horiAdvance - face->glyph->metrics.horiBearingX - face->glyph->metrics.width) / 64.0; } else @@ -310,15 +329,17 @@ font_render(FontObject* self, PyObject* args) /* render string into given buffer (the buffer *must* have the right size, or this will crash) */ PyObject* string; - long id; + Py_ssize_t id; int mask = 0; - if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask)) + int temp; + int xx, x0, x1; + if (!PyArg_ParseTuple(args, "On|i:render", &string, &id, &mask)) return NULL; -#if defined(HAVE_UNICODE) - if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyUnicode_Check(string)) { #else - if (!PyString_Check(string)) { + if (!PyUnicode_Check(string) && !PyString_Check(string)) { #endif PyErr_SetString(PyExc_TypeError, "expected string"); return NULL; @@ -330,6 +351,18 @@ font_render(FontObject* self, PyObject* args) if (mask) load_flags |= FT_LOAD_TARGET_MONO; + ascender = 0; + for (i = 0; font_getchar(string, i, &ch); i++) { + index = FT_Get_Char_Index(self->face, ch); + error = FT_Load_Glyph(self->face, index, load_flags); + if (error) + return geterror(error); + glyph = self->face->glyph; + temp = (glyph->bitmap.rows - glyph->bitmap_top); + if (temp > ascender) + ascender = temp; + } + for (x = i = 0; font_getchar(string, i, &ch); i++) { if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) x = -PIXEL(self->face->glyph->metrics.horiBearingX); @@ -340,24 +373,26 @@ font_render(FontObject* self, PyObject* args) &delta); x += delta.x >> 6; } + error = FT_Load_Glyph(self->face, index, load_flags); if (error) return geterror(error); + glyph = self->face->glyph; + + source = (unsigned char*) glyph->bitmap.buffer; + xx = x + glyph->bitmap_left; + x0 = 0; + x1 = glyph->bitmap.width; + if (xx < 0) + x0 = -xx; + if (xx + x1 > im->xsize) + x1 = im->xsize - xx; + if (mask) { /* use monochrome mask (on palette images, etc) */ - int xx, x0, x1; - source = (unsigned char*) glyph->bitmap.buffer; - ascender = PIXEL(self->face->size->metrics.ascender); - xx = x + glyph->bitmap_left; - x0 = 0; - x1 = glyph->bitmap.width; - if (xx < 0) - x0 = -xx; - if (xx + x1 > im->xsize) - x1 = im->xsize - xx; for (y = 0; y < glyph->bitmap.rows; y++) { - int yy = y + ascender - glyph->bitmap_top; + int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender); if (yy >= 0 && yy < im->ysize) { /* blend this glyph into the buffer */ unsigned char *target = im->image8[yy] + xx; @@ -375,18 +410,8 @@ font_render(FontObject* self, PyObject* args) } } else { /* use antialiased rendering */ - int xx, x0, x1; - source = (unsigned char*) glyph->bitmap.buffer; - ascender = PIXEL(self->face->size->metrics.ascender); - xx = x + glyph->bitmap_left; - x0 = 0; - x1 = glyph->bitmap.width; - if (xx < 0) - x0 = -xx; - if (xx + x1 > im->xsize) - x1 = im->xsize - xx; for (y = 0; y < glyph->bitmap.rows; y++) { - int yy = y + ascender - glyph->bitmap_top; + int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender); if (yy >= 0 && yy < im->ysize) { /* blend this glyph into the buffer */ int i; @@ -420,49 +445,89 @@ static PyMethodDef font_methods[] = { {NULL, NULL} }; -static PyObject* -font_getattr(FontObject* self, char* name) +static PyObject* +font_getattr_family(FontObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(font_methods, (PyObject*) self, name); - - if (res) - return res; - - PyErr_Clear(); - - /* attributes */ - if (!strcmp(name, "family")) { - if (self->face->family_name) - return PyString_FromString(self->face->family_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "style")) { - if (self->face->style_name) - return PyString_FromString(self->face->style_name); - Py_RETURN_NONE; - } - if (!strcmp(name, "ascent")) - return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); - if (!strcmp(name, "descent")) - return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); - - if (!strcmp(name, "glyphs")) - /* number of glyphs provided by this font */ - return PyInt_FromLong(self->face->num_glyphs); - - PyErr_SetString(PyExc_AttributeError, name); - return NULL; +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->family_name) + return PyUnicode_FromString(self->face->family_name); +#else + if (self->face->family_name) + return PyString_FromString(self->face->family_name); +#endif + Py_RETURN_NONE; } -statichere PyTypeObject Font_Type = { - PyObject_HEAD_INIT(NULL) - 0, "Font", sizeof(FontObject), 0, +static PyObject* +font_getattr_style(FontObject* self, void* closure) +{ +#if PY_VERSION_HEX >= 0x03000000 + if (self->face->style_name) + return PyUnicode_FromString(self->face->style_name); +#else + if (self->face->style_name) + return PyString_FromString(self->face->style_name); +#endif + Py_RETURN_NONE; +} + +static PyObject* +font_getattr_ascent(FontObject* self, void* closure) +{ + return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); +} + +static PyObject* +font_getattr_descent(FontObject* self, void* closure) +{ + return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); +} + +static PyObject* +font_getattr_glyphs(FontObject* self, void* closure) +{ + return PyInt_FromLong(self->face->num_glyphs); +} + +static struct PyGetSetDef font_getsetters[] = { + { "family", (getter) font_getattr_family }, + { "style", (getter) font_getattr_style }, + { "ascent", (getter) font_getattr_ascent }, + { "descent", (getter) font_getattr_descent }, + { "glyphs", (getter) font_getattr_glyphs }, + { NULL } +}; + +static PyTypeObject Font_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "Font", sizeof(FontObject), 0, /* methods */ (destructor)font_dealloc, /* tp_dealloc */ 0, /* tp_print */ - (getattrfunc)font_getattr, /* tp_getattr */ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + font_methods, /*tp_methods*/ + 0, /*tp_members*/ + font_getsetters, /*tp_getset*/ }; static PyMethodDef _functions[] = { @@ -470,33 +535,58 @@ static PyMethodDef _functions[] = { {NULL, NULL} }; -DL_EXPORT(void) -init_imagingft(void) -{ - PyObject* m; +static int +setup_module(PyObject* m) { PyObject* d; PyObject* v; int major, minor, patch; - /* Patch object type */ - Font_Type.ob_type = &PyType_Type; - - m = Py_InitModule("_imagingft", _functions); d = PyModule_GetDict(m); + /* Ready object type */ + PyType_Ready(&Font_Type); + if (FT_Init_FreeType(&library)) - return; /* leave it uninitalized */ + return 0; /* leave it uninitalized */ FT_Library_Version(library, &major, &minor, &patch); -#if PY_VERSION_HEX >= 0x02020000 - v = PyString_FromFormat("%d.%d.%d", major, minor, patch); +#if PY_VERSION_HEX >= 0x03000000 + v = PyUnicode_FromFormat("%d.%d.%d", major, minor, patch); #else - { - char buffer[100]; - sprintf(buffer, "%d.%d.%d", major, minor, patch); - v = PyString_FromString(buffer); - } + v = PyString_FromFormat("%d.%d.%d", major, minor, patch); #endif PyDict_SetItemString(d, "freetype2_version", v); + + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingft(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingft", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingft(void) +{ + PyObject* m = Py_InitModule("_imagingft", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingmath.c b/_imagingmath.c index 928986bb3..c6334edeb 100644 --- a/_imagingmath.c +++ b/_imagingmath.c @@ -16,6 +16,7 @@ #include "Python.h" #include "Imaging.h" +#include "py3.h" #include "math.h" #include "float.h" @@ -81,7 +82,7 @@ void name(Imaging out, Imaging im1, Imaging im2)\ /* -------------------------------------------------------------------- * some day, we should add FPE protection mechanisms. see pyfpe.h for * details. - * + * * PyFPE_START_PROTECT("Error in foobar", return 0) * PyFPE_END_PROTECT(result) */ @@ -173,15 +174,15 @@ _unop(PyObject* self, PyObject* args) Imaging im1; void (*unop)(Imaging, Imaging); - long op, i0, i1; - if (!PyArg_ParseTuple(args, "lll", &op, &i0, &i1)) + Py_ssize_t op, i0, i1; + if (!PyArg_ParseTuple(args, "nnn", &op, &i0, &i1)) return NULL; out = (Imaging) i0; im1 = (Imaging) i1; unop = (void*) op; - + unop(out, im1); Py_INCREF(Py_None); @@ -196,8 +197,8 @@ _binop(PyObject* self, PyObject* args) Imaging im2; void (*binop)(Imaging, Imaging, Imaging); - long op, i0, i1, i2; - if (!PyArg_ParseTuple(args, "llll", &op, &i0, &i1, &i2)) + Py_ssize_t op, i0, i1, i2; + if (!PyArg_ParseTuple(args, "nnnn", &op, &i0, &i1, &i2)) return NULL; out = (Imaging) i0; @@ -205,7 +206,7 @@ _binop(PyObject* self, PyObject* args) im2 = (Imaging) i2; binop = (void*) op; - + binop(out, im1, im2); Py_INCREF(Py_None); @@ -221,20 +222,15 @@ static PyMethodDef _functions[] = { static void install(PyObject *d, char* name, void* value) { - PyObject *v = PyInt_FromLong((long) value); + PyObject *v = PyInt_FromSsize_t((Py_ssize_t) value); if (!v || PyDict_SetItemString(d, name, v)) PyErr_Clear(); Py_XDECREF(v); } -DL_EXPORT(void) -init_imagingmath(void) -{ - PyObject* m; - PyObject* d; - - m = Py_InitModule("_imagingmath", _functions); - d = PyModule_GetDict(m); +static int +setup_module(PyObject* m) { + PyObject* d = PyModule_GetDict(m); install(d, "abs_I", abs_I); install(d, "neg_I", neg_I); @@ -281,4 +277,35 @@ init_imagingmath(void) install(d, "gt_F", gt_F); install(d, "ge_F", ge_F); + return 0; } + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingmath(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingmath", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + _functions, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + + if (setup_module(m) < 0) + return NULL; + + return m; +} +#else +PyMODINIT_FUNC +init_imagingmath(void) +{ + PyObject* m = Py_InitModule("_imagingmath", _functions); + setup_module(m); +} +#endif + diff --git a/_imagingtk.c b/_imagingtk.c index 6165a2acb..c379f7dc5 100644 --- a/_imagingtk.c +++ b/_imagingtk.c @@ -30,14 +30,14 @@ typedef struct { Tcl_Interp* interp; } TkappObject; -static PyObject* +static PyObject* _tkinit(PyObject* self, PyObject* args) { Tcl_Interp* interp; - long arg; + Py_ssize_t arg; int is_interp; - if (!PyArg_ParseTuple(args, "li", &arg, &is_interp)) + if (!PyArg_ParseTuple(args, "ni", &arg, &is_interp)) return NULL; if (is_interp) @@ -63,8 +63,24 @@ static PyMethodDef functions[] = { {NULL, NULL} /* sentinel */ }; -DL_EXPORT(void) +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__imagingtk(void) { + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_imagingtk", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + functions, /* m_methods */ + }; + + return PyModule_Create(&module_def); +} +#else +PyMODINIT_FUNC init_imagingtk(void) { Py_InitModule("_imagingtk", functions); } +#endif + diff --git a/_webp.c b/_webp.c new file mode 100644 index 000000000..c201813d7 --- /dev/null +++ b/_webp.c @@ -0,0 +1,279 @@ +#define PY_SSIZE_T_CLEAN +#include +#include "py3.h" +#include +#include +#include + +#ifdef HAVE_WEBPMUX +#include +#endif + +PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) +{ + int width; + int height; + int lossless; + float quality_factor; + uint8_t *rgb; + uint8_t *icc_bytes; + uint8_t *exif_bytes; + uint8_t *output; + char *mode; + Py_ssize_t size; + Py_ssize_t icc_size; + Py_ssize_t exif_size; + size_t ret_size; + + if (!PyArg_ParseTuple(args, "s#iiifss#s#", + (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode, + &icc_bytes, &icc_size, &exif_bytes, &exif_size)) { + Py_RETURN_NONE; + } + if (strcmp(mode, "RGBA")==0){ + if (size < width * height * 4){ + Py_RETURN_NONE; + } + #if WEBP_ENCODER_ABI_VERSION >= 0x0100 + if (lossless) { + ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output); + } else + #endif + { + ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output); + } + } else if (strcmp(mode, "RGB")==0){ + if (size < width * height * 3){ + Py_RETURN_NONE; + } + #if WEBP_ENCODER_ABI_VERSION >= 0x0100 + if (lossless) { + ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output); + } else + #endif + { + ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output); + } + } else { + Py_RETURN_NONE; + } + +#ifndef HAVE_WEBPMUX + if (ret_size > 0) { + PyObject *ret = PyBytes_FromStringAndSize((char*)output, ret_size); + free(output); + return ret; + } +#else + { + /* I want to truncate the *_size items that get passed into webp + data. Pypy2.1.0 had some issues where the Py_ssize_t items had + data in the upper byte. (Not sure why, it shouldn't have been there) + */ + int i_icc_size = (int)icc_size; + int i_exif_size = (int)exif_size; + WebPData output_data = {0}; + WebPData image = { output, ret_size }; + WebPData icc_profile = { icc_bytes, i_icc_size }; + WebPData exif = { exif_bytes, i_exif_size }; + WebPMuxError err; + int dbg = 0; + + int copy_data = 0; // value 1 indicates given data WILL be copied to the mux + // and value 0 indicates data will NOT be copied. + + WebPMux* mux = WebPMuxNew(); + WebPMuxSetImage(mux, &image, copy_data); + + if (dbg) { + /* was getting %ld icc_size == 0, icc_size>0 was true */ + fprintf(stderr, "icc size %d, %d \n", i_icc_size, i_icc_size > 0); + } + + if (i_icc_size > 0) { + if (dbg) { + fprintf (stderr, "Adding ICC Profile\n"); + } + err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); + if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) { + fprintf(stderr, "Invalid ICC Argument\n"); + } else if (dbg && err == WEBP_MUX_MEMORY_ERROR) { + fprintf(stderr, "ICC Memory Error\n"); + } + } + + if (dbg) { + fprintf(stderr, "exif size %d \n", i_exif_size); + } + if (i_exif_size > 0) { + if (dbg){ + fprintf (stderr, "Adding Exif Data\n"); + } + err = WebPMuxSetChunk(mux, "EXIF", &exif, copy_data); + if (dbg && err == WEBP_MUX_INVALID_ARGUMENT) { + fprintf(stderr, "Invalid Exif Argument\n"); + } else if (dbg && err == WEBP_MUX_MEMORY_ERROR) { + fprintf(stderr, "Exif Memory Error\n"); + } + } + + WebPMuxAssemble(mux, &output_data); + WebPMuxDelete(mux); + free(output); + + ret_size = output_data.size; + if (ret_size > 0) { + PyObject *ret = PyBytes_FromStringAndSize((char*)output_data.bytes, ret_size); + WebPDataClear(&output_data); + return ret; + } + } +#endif + Py_RETURN_NONE; +} + + +PyObject* WebPDecode_wrapper(PyObject* self, PyObject* args) +{ + PyBytesObject *webp_string; + uint8_t *webp; + Py_ssize_t size; + PyObject *ret, *bytes, *pymode, *icc_profile = Py_None, *exif = Py_None; + WebPDecoderConfig config; + VP8StatusCode vp8_status_code = VP8_STATUS_OK; + char* mode = "RGB"; + + if (!PyArg_ParseTuple(args, "S", &webp_string)) { + Py_RETURN_NONE; + } + + if (!WebPInitDecoderConfig(&config)) { + Py_RETURN_NONE; + } + + PyBytes_AsStringAndSize((PyObject *) webp_string, (char**)&webp, &size); + + vp8_status_code = WebPGetFeatures(webp, size, &config.input); + if (vp8_status_code == VP8_STATUS_OK) { + // If we don't set it, we don't get alpha. + // Initialized to MODE_RGB + if (config.input.has_alpha) { + config.output.colorspace = MODE_RGBA; + mode = "RGBA"; + } + +#ifndef HAVE_WEBPMUX + vp8_status_code = WebPDecode(webp, size, &config); +#else + { + int copy_data = 0; + WebPData data = { webp, size }; + WebPMuxFrameInfo image; + WebPData icc_profile_data = {0}; + WebPData exif_data = {0}; + + WebPMux* mux = WebPMuxCreate(&data, copy_data); + WebPMuxGetFrame(mux, 1, &image); + webp = (uint8_t*)image.bitstream.bytes; + size = image.bitstream.size; + + vp8_status_code = WebPDecode(webp, size, &config); + + WebPMuxGetChunk(mux, "ICCP", &icc_profile_data); + if (icc_profile_data.size > 0) { + icc_profile = PyBytes_FromStringAndSize((const char*)icc_profile_data.bytes, icc_profile_data.size); + } + + WebPMuxGetChunk(mux, "EXIF", &exif_data); + if (exif_data.size > 0) { + exif = PyBytes_FromStringAndSize((const char*)exif_data.bytes, exif_data.size); + } + + WebPMuxDelete(mux); + } +#endif + } + + if (vp8_status_code != VP8_STATUS_OK) { + WebPFreeDecBuffer(&config.output); + Py_RETURN_NONE; + } + + if (config.output.colorspace < MODE_YUV) { + bytes = PyBytes_FromStringAndSize((char *)config.output.u.RGBA.rgba, + config.output.u.RGBA.size); + } else { + // Skipping YUV for now. Need Test Images. + // UNDONE -- unclear if we'll ever get here if we set mode_rgb* + bytes = PyBytes_FromStringAndSize((char *)config.output.u.YUVA.y, + config.output.u.YUVA.y_size); + } + +#if PY_VERSION_HEX >= 0x03000000 + pymode = PyUnicode_FromString(mode); +#else + pymode = PyString_FromString(mode); +#endif + ret = Py_BuildValue("SiiSSS", bytes, config.output.width, + config.output.height, pymode, icc_profile, exif); + WebPFreeDecBuffer(&config.output); + return ret; +} + +// Return the decoder's version number, packed in hexadecimal using 8bits for +// each of major/minor/revision. E.g: v2.5.7 is 0x020507. +PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){ + return Py_BuildValue("i", WebPGetDecoderVersion()); +} + +/* + * The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well. + * Files that are valid with 0.3 are reported as being invalid. + */ +PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ + return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103); +} + +static PyMethodDef webpMethods[] = +{ + {"WebPEncode", WebPEncode_wrapper, METH_VARARGS, "WebPEncode"}, + {"WebPDecode", WebPDecode_wrapper, METH_VARARGS, "WebPDecode"}, + {"WebPDecoderVersion", WebPDecoderVersion_wrapper, METH_VARARGS, "WebPVersion"}, + {"WebPDecoderBuggyAlpha", WebPDecoderBuggyAlpha_wrapper, METH_VARARGS, "WebPDecoderBuggyAlpha"}, + {NULL, NULL} +}; + +void addMuxFlagToModule(PyObject* m) { +#ifdef HAVE_WEBPMUX + PyModule_AddObject(m, "HAVE_WEBPMUX", Py_True); +#else + PyModule_AddObject(m, "HAVE_WEBPMUX", Py_False); +#endif +} + + +#if PY_VERSION_HEX >= 0x03000000 +PyMODINIT_FUNC +PyInit__webp(void) { + PyObject* m; + + static PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + "_webp", /* m_name */ + NULL, /* m_doc */ + -1, /* m_size */ + webpMethods, /* m_methods */ + }; + + m = PyModule_Create(&module_def); + addMuxFlagToModule(m); + return m; +} +#else +PyMODINIT_FUNC +init_webp(void) +{ + PyObject* m = Py_InitModule("_webp", webpMethods); + addMuxFlagToModule(m); +} +#endif diff --git a/decode.c b/decode.c index 6ea8d9c3f..f3ac60e51 100644 --- a/decode.c +++ b/decode.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library. * * standard decoder interfaces for the Imaging library @@ -31,12 +31,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" #include "Gif.h" #include "Lzw.h" @@ -45,19 +41,20 @@ /* -------------------------------------------------------------------- */ -/* Common */ +/* Common */ /* -------------------------------------------------------------------- */ typedef struct { PyObject_HEAD int (*decode)(Imaging im, ImagingCodecState state, - UINT8* buffer, int bytes); + UINT8* buffer, int bytes); + int (*cleanup)(ImagingCodecState state); struct ImagingCodecStateInstance state; Imaging im; PyObject* lock; } ImagingDecoderObject; -staticforward PyTypeObject ImagingDecoderType; +static PyTypeObject ImagingDecoderType; static ImagingDecoderObject* PyImaging_DecoderNew(int contextsize) @@ -65,25 +62,26 @@ PyImaging_DecoderNew(int contextsize) ImagingDecoderObject *decoder; void *context; - ImagingDecoderType.ob_type = &PyType_Type; + if(PyType_Ready(&ImagingDecoderType) < 0) + return NULL; decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType); if (decoder == NULL) - return NULL; + return NULL; /* Clear the decoder state */ memset(&decoder->state, 0, sizeof(decoder->state)); /* Allocate decoder context */ if (contextsize > 0) { - context = (void*) calloc(1, contextsize); - if (!context) { - Py_DECREF(decoder); - (void) PyErr_NoMemory(); - return NULL; - } + context = (void*) calloc(1, contextsize); + if (!context) { + Py_DECREF(decoder); + (void) PyErr_NoMemory(); + return NULL; + } } else - context = 0; + context = 0; /* Initialize decoder context */ decoder->state.context = context; @@ -92,6 +90,9 @@ PyImaging_DecoderNew(int contextsize) decoder->lock = NULL; decoder->im = NULL; + /* Initialize the cleanup function pointer */ + decoder->cleanup = NULL; + return decoder; } @@ -104,20 +105,34 @@ _dealloc(ImagingDecoderObject* decoder) PyObject_Del(decoder); } -static PyObject* +static PyObject* _decode(ImagingDecoderObject* decoder, PyObject* args) { UINT8* buffer; int bufsize, status; - if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) - return NULL; + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH, &buffer, &bufsize)) + return NULL; status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); return Py_BuildValue("ii", status, decoder->state.errcode); } +static PyObject* +_decode_cleanup(ImagingDecoderObject* decoder, PyObject* args) +{ + int status = 0; + + if (decoder->cleanup){ + status = decoder->cleanup(&decoder->state); + } + + return Py_BuildValue("i", status); +} + + + extern Imaging PyImaging_AsImaging(PyObject *op); static PyObject* @@ -132,10 +147,10 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) /* FIXME: should publish the ImagingType descriptor */ if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) - return NULL; + return NULL; im = PyImaging_AsImaging(op); if (!im) - return NULL; + return NULL; decoder->im = im; @@ -143,30 +158,30 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) /* Setup decoding tile extent */ if (x0 == 0 && x1 == 0) { - state->xsize = im->xsize; - state->ysize = im->ysize; + state->xsize = im->xsize; + state->ysize = im->ysize; } else { - state->xoff = x0; - state->yoff = y0; - state->xsize = x1 - x0; - state->ysize = y1 - y0; + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; } if (state->xsize <= 0 || - state->xsize + state->xoff > (int) im->xsize || - state->ysize <= 0 || - state->ysize + state->yoff > (int) im->ysize) { - PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); - return NULL; + state->xsize + state->xoff > (int) im->xsize || + state->ysize <= 0 || + state->ysize + state->yoff > (int) im->ysize) { + PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); + return NULL; } /* Allocate memory buffer (if bits field is set) */ if (state->bits > 0) { if (!state->bytes) state->bytes = (state->bits * state->xsize+7)/8; - state->buffer = (UINT8*) malloc(state->bytes); - if (!state->buffer) - return PyErr_NoMemory(); + state->buffer = (UINT8*) malloc(state->bytes); + if (!state->buffer) + return PyErr_NoMemory(); } /* Keep a reference to the image object, to make sure it doesn't @@ -181,30 +196,43 @@ _setimage(ImagingDecoderObject* decoder, PyObject* args) static struct PyMethodDef methods[] = { {"decode", (PyCFunction)_decode, 1}, + {"cleanup", (PyCFunction)_decode_cleanup, 1}, {"setimage", (PyCFunction)_setimage, 1}, {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDecoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingDecoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "ImagingDecoder", /*tp_name*/ - sizeof(ImagingDecoderObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ +static PyTypeObject ImagingDecoderType = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingDecoder", /*tp_name*/ + sizeof(ImagingDecoderObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ @@ -218,9 +246,9 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode, unpack = ImagingFindUnpacker(mode, rawmode, &bits); if (!unpack) { - Py_DECREF(decoder); - PyErr_SetString(PyExc_ValueError, "unknown raw mode"); - return -1; + Py_DECREF(decoder); + PyErr_SetString(PyExc_ValueError, "unknown raw mode"); + return -1; } decoder->state.shuffle = unpack; @@ -231,7 +259,7 @@ get_unpacker(ImagingDecoderObject* decoder, const char* mode, /* -------------------------------------------------------------------- */ -/* BIT (packed fields) */ +/* BIT (packed fields) */ /* -------------------------------------------------------------------- */ PyObject* @@ -247,16 +275,16 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) int ystep = 1; if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill, &sign, &ystep)) - return NULL; + return NULL; if (strcmp(mode, "F") != 0) { - PyErr_SetString(PyExc_ValueError, "bad image mode"); + PyErr_SetString(PyExc_ValueError, "bad image mode"); return NULL; } decoder = PyImaging_DecoderNew(sizeof(BITSTATE)); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingBitDecode; @@ -272,7 +300,7 @@ PyImaging_BitDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* FLI */ +/* FLI */ /* -------------------------------------------------------------------- */ PyObject* @@ -282,7 +310,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingFliDecode; @@ -291,7 +319,7 @@ PyImaging_FliDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* GIF */ +/* GIF */ /* -------------------------------------------------------------------- */ PyObject* @@ -303,16 +331,16 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) int bits = 8; int interlace = 0; if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) - return NULL; + return NULL; if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) { - PyErr_SetString(PyExc_ValueError, "bad image mode"); + PyErr_SetString(PyExc_ValueError, "bad image mode"); return NULL; } decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE)); if (decoder == NULL) - return NULL; + return NULL; decoder->decode = ImagingGifDecode; @@ -324,7 +352,7 @@ PyImaging_GifDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* HEX */ +/* HEX */ /* -------------------------------------------------------------------- */ PyObject* @@ -335,14 +363,14 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingHexDecode; @@ -351,7 +379,7 @@ PyImaging_HexDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* LZW */ +/* LZW */ /* -------------------------------------------------------------------- */ PyObject* @@ -363,14 +391,14 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) char* rawmode; int filter = 0; if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &filter)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(LZWSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingLzwDecode; @@ -379,9 +407,55 @@ PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) return (PyObject*) decoder; } +/* -------------------------------------------------------------------- */ +/* LibTiff */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBTIFF + +#include "TiffDecode.h" + +#include +#ifdef __WIN32__ +#define strcasecmp(s1, s2) stricmp(s1, s2) +#endif + +PyObject* +PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + char* mode; + char* rawmode; + char* compname; + int fp; + + if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp)) + return NULL; + + TRACE(("new tiff decoder %s\n", compname)); + + decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + if (! ImagingLibTiffInit(&decoder->state, fp)) { + Py_DECREF(decoder); + PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed"); + return NULL; + } + + decoder->decode = ImagingLibTiffDecode; + + return (PyObject*) decoder; +} + +#endif /* -------------------------------------------------------------------- */ -/* MSP */ +/* MSP */ /* -------------------------------------------------------------------- */ PyObject* @@ -391,10 +465,10 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, "1", "1") < 0) - return NULL; + return NULL; decoder->decode = ImagingMspDecode; @@ -403,7 +477,7 @@ PyImaging_MspDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PackBits */ +/* PackBits */ /* -------------------------------------------------------------------- */ PyObject* @@ -414,14 +488,14 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingPackbitsDecode; @@ -430,7 +504,7 @@ PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCD */ +/* PCD */ /* -------------------------------------------------------------------- */ PyObject* @@ -440,11 +514,11 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; /* Unpack from PhotoYCC to RGB */ if (get_unpacker(decoder, "RGB", "YCC;P") < 0) - return NULL; + return NULL; decoder->decode = ImagingPcdDecode; @@ -453,7 +527,7 @@ PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* PCX */ +/* PCX */ /* -------------------------------------------------------------------- */ PyObject* @@ -465,14 +539,14 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) char* rawmode; int stride; if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->state.bytes = stride; @@ -483,7 +557,7 @@ PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* RAW */ +/* RAW */ /* -------------------------------------------------------------------- */ PyObject* @@ -496,14 +570,14 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) int stride = 0; int ystep = 1; if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(RAWSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingRawDecode; @@ -516,7 +590,7 @@ PyImaging_RawDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* SUN RLE */ +/* SUN RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -527,14 +601,14 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingSunRleDecode; @@ -543,7 +617,7 @@ PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* TGA RLE */ +/* TGA RLE */ /* -------------------------------------------------------------------- */ PyObject* @@ -556,14 +630,14 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) int ystep = 1; int depth = 8; if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingTgaRleDecode; @@ -575,7 +649,7 @@ PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* XBM */ +/* XBM */ /* -------------------------------------------------------------------- */ PyObject* @@ -585,10 +659,10 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) decoder = PyImaging_DecoderNew(0); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, "1", "1;R") < 0) - return NULL; + return NULL; decoder->decode = ImagingXbmDecode; @@ -597,7 +671,7 @@ PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* ZIP */ +/* ZIP */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBZ @@ -613,14 +687,14 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) char* rawmode; int interlaced = 0; if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) - return NULL; + return NULL; decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingZipDecode; @@ -632,7 +706,7 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) /* -------------------------------------------------------------------- */ -/* JPEG */ +/* JPEG */ /* -------------------------------------------------------------------- */ #ifdef HAVE_LIBJPEG @@ -640,15 +714,15 @@ PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) /* We better define this decoder last in this file, so the following undef's won't mess things up for the Imaging library proper. */ -#undef HAVE_PROTOTYPES -#undef HAVE_STDDEF_H -#undef HAVE_STDLIB_H -#undef UINT8 -#undef UINT16 -#undef UINT32 -#undef INT8 -#undef INT16 -#undef INT32 +#undef HAVE_PROTOTYPES +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT8 +#undef INT16 +#undef INT32 #include "Jpeg.h" @@ -664,19 +738,20 @@ PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) int draft = 0; if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode, &scale, &draft)) - return NULL; + return NULL; if (!jpegmode) - jpegmode = ""; + jpegmode = ""; decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE)); if (decoder == NULL) - return NULL; + return NULL; if (get_unpacker(decoder, mode, rawmode) < 0) - return NULL; + return NULL; decoder->decode = ImagingJpegDecode; + decoder->cleanup = ImagingJpegDecodeCleanup; strncpy(((JPEGSTATE*)decoder->state.context)->rawmode, rawmode, 8); strncpy(((JPEGSTATE*)decoder->state.context)->jpegmode, jpegmode, 8); diff --git a/display.c b/display.c index 1a08fadd5..9cc1ae3ad 100644 --- a/display.c +++ b/display.c @@ -25,12 +25,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" /* -------------------------------------------------------------------- */ /* Windows DIB support */ @@ -44,13 +40,16 @@ typedef struct { ImagingDIB dib; } ImagingDisplayObject; -staticforward PyTypeObject ImagingDisplayType; +static PyTypeObject ImagingDisplayType; static ImagingDisplayObject* _new(const char* mode, int xsize, int ysize) { ImagingDisplayObject *display; + if (PyType_Ready(&ImagingDisplayType) < 0) + return NULL; + display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); if (display == NULL) return NULL; @@ -72,7 +71,7 @@ _delete(ImagingDisplayObject* display) PyObject_Del(display); } -static PyObject* +static PyObject* _expose(ImagingDisplayObject* display, PyObject* args) { int hdc; @@ -85,7 +84,7 @@ _expose(ImagingDisplayObject* display, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _draw(ImagingDisplayObject* display, PyObject* args) { int hdc; @@ -129,7 +128,7 @@ _paste(ImagingDisplayObject* display, PyObject* args) return Py_None; } -static PyObject* +static PyObject* _query_palette(ImagingDisplayObject* display, PyObject* args) { int hdc; @@ -176,12 +175,18 @@ _releasedc(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_fromstring(ImagingDisplayObject* display, PyObject* args) +_frombytes(ImagingDisplayObject* display, PyObject* args) { char* ptr; int bytes; + +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, "y#:frombytes", &ptr, &bytes)) + return NULL; +#else if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes)) - return NULL; + return NULL; +#endif if (display->dib->ysize * display->dib->linesize != bytes) { PyErr_SetString(PyExc_ValueError, "wrong size"); @@ -195,12 +200,17 @@ _fromstring(ImagingDisplayObject* display, PyObject* args) } static PyObject* -_tostring(ImagingDisplayObject* display, PyObject* args) +_tobytes(ImagingDisplayObject* display, PyObject* args) { +#if PY_VERSION_HEX >= 0x03000000 + if (!PyArg_ParseTuple(args, ":tobytes")) + return NULL; +#else if (!PyArg_ParseTuple(args, ":tostring")) - return NULL; + return NULL; +#endif - return PyString_FromStringAndSize( + return PyBytes_FromStringAndSize( display->dib->bits, display->dib->ysize * display->dib->linesize ); } @@ -212,42 +222,63 @@ static struct PyMethodDef methods[] = { {"query_palette", (PyCFunction)_query_palette, 1}, {"getdc", (PyCFunction)_getdc, 1}, {"releasedc", (PyCFunction)_releasedc, 1}, - {"fromstring", (PyCFunction)_fromstring, 1}, - {"tostring", (PyCFunction)_tostring, 1}, + {"frombytes", (PyCFunction)_frombytes, 1}, + {"tobytes", (PyCFunction)_tobytes, 1}, + {"fromstring", (PyCFunction)_frombytes, 1}, + {"tostring", (PyCFunction)_tobytes, 1}, {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingDisplayObject* self, char* name) +static PyObject* +_getattr_mode(ImagingDisplayObject* self, void* closure) { - PyObject* res; - - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; - PyErr_Clear(); - if (!strcmp(name, "mode")) return Py_BuildValue("s", self->dib->mode); - if (!strcmp(name, "size")) - return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); - PyErr_SetString(PyExc_AttributeError, name); - return NULL; } -statichere PyTypeObject ImagingDisplayType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyObject* +_getattr_size(ImagingDisplayObject* self, void* closure) +{ + return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); +} + +static struct PyGetSetDef getsetters[] = { + { "mode", (getter) _getattr_mode }, + { "size", (getter) _getattr_size }, + { NULL } +}; + +static PyTypeObject ImagingDisplayType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingDisplay", /*tp_name*/ sizeof(ImagingDisplayObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_delete, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; PyObject* @@ -289,20 +320,20 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) BITMAPCOREHEADER core; HDC screen, screen_copy; PyObject* buffer; - + /* step 1: create a memory DC large enough to hold the entire screen */ - screen = CreateDC("DISPLAY", NULL, NULL, NULL); - screen_copy = CreateCompatibleDC(screen); + screen = CreateDC("DISPLAY", NULL, NULL, NULL); + screen_copy = CreateCompatibleDC(screen); width = GetDeviceCaps(screen, HORZRES); height = GetDeviceCaps(screen, VERTRES); - + bitmap = CreateCompatibleBitmap(screen, width, height); if (!bitmap) goto error; - + if (!SelectObject(screen_copy, bitmap)) goto error; @@ -313,7 +344,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) /* step 3: extract bits from bitmap */ - buffer = PyString_FromStringAndSize(NULL, height * ((width*3 + 3) & -4)); + buffer = PyBytes_FromStringAndSize(NULL, height * ((width*3 + 3) & -4)); if (!buffer) return NULL; @@ -322,7 +353,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) core.bcHeight = height; core.bcPlanes = 1; core.bcBitCount = 24; - if (!GetDIBits(screen_copy, bitmap, 0, height, PyString_AS_STRING(buffer), + if (!GetDIBits(screen_copy, bitmap, 0, height, PyBytes_AS_STRING(buffer), (BITMAPINFO*) &core, DIB_RGB_COLORS)) goto error; @@ -349,15 +380,15 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) RECT inner, outer; int title_size; int status; - + /* get window title */ title_size = GetWindowTextLength(hwnd); if (title_size > 0) { - title = PyString_FromStringAndSize(NULL, title_size); + title = PyUnicode_FromStringAndSize(NULL, title_size); if (title) - GetWindowText(hwnd, PyString_AS_STRING(title), title_size+1); + GetWindowText(hwnd, PyUnicode_AS_UNICODE(title), title_size+1); } else - title = PyString_FromString(""); + title = PyUnicode_FromString(""); if (!title) return 0; @@ -366,7 +397,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) GetWindowRect(hwnd, &outer); item = Py_BuildValue( - "lN(iiii)(iiii)", (long) hwnd, title, + "nN(iiii)(iiii)", (Py_ssize_t) hwnd, title, inner.left, inner.top, inner.right, inner.bottom, outer.left, outer.top, outer.right, outer.bottom ); @@ -379,7 +410,7 @@ static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) if (status < 0) return 0; - + return 1; } @@ -387,7 +418,7 @@ PyObject* PyImaging_ListWindowsWin32(PyObject* self, PyObject* args) { PyObject* window_list; - + window_list = PyList_New(0); if (!window_list) return NULL; @@ -413,7 +444,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) int size; void* data; PyObject* result; - + int verbose = 0; /* debugging; will be removed in future versions */ if (!PyArg_ParseTuple(args, "|i", &verbose)) return NULL; @@ -421,7 +452,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) clip = OpenClipboard(NULL); /* FIXME: check error status */ - + if (verbose) { UINT format = EnumClipboardFormats(0); char buffer[200]; @@ -512,7 +543,7 @@ PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) size = wcslen(data) * 2; #endif - result = PyString_FromStringAndSize(data, size); + result = PyBytes_FromStringAndSize(data, size); GlobalUnlock(handle); @@ -534,7 +565,7 @@ static void callback_error(const char* handler) { PyObject* sys_stderr; - + sys_stderr = PySys_GetObject("stderr"); if (sys_stderr) { @@ -691,7 +722,7 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) wnd = CreateWindowEx( 0, windowClass.lpszClassName, title, WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, width, height, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, HWND_DESKTOP, NULL, NULL, NULL ); @@ -702,15 +733,15 @@ PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) /* register window callback */ Py_INCREF(callback); - SetWindowLong(wnd, 0, (LONG) callback); - SetWindowLong(wnd, sizeof(callback), (LONG) PyThreadState_Get()); + SetWindowLongPtr(wnd, 0, (LONG_PTR) callback); + SetWindowLongPtr(wnd, sizeof(callback), (LONG_PTR) PyThreadState_Get()); Py_BEGIN_ALLOW_THREADS ShowWindow(wnd, SW_SHOWNORMAL); SetForegroundWindow(wnd); /* to make sure it's visible */ Py_END_ALLOW_THREADS - return Py_BuildValue("l", (long) wnd); + return Py_BuildValue("n", (Py_ssize_t) wnd); } PyObject* @@ -749,7 +780,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) int datasize; int width, height; int x0, y0, x1, y1; - if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize, + if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"(ii)(iiii):_load", &data, &datasize, &width, &height, &x0, &x1, &y0, &y1)) return NULL; @@ -820,7 +851,7 @@ PyImaging_DrawWmf(PyObject* self, PyObject* args) GdiFlush(); - buffer = PyString_FromStringAndSize(ptr, height * ((width*3 + 3) & -4)); + buffer = PyBytes_FromStringAndSize(ptr, height * ((width*3 + 3) & -4)); error: DeleteEnhMetaFile(meta); diff --git a/BUILDME b/docs/BUILDME similarity index 100% rename from BUILDME rename to docs/BUILDME diff --git a/docs/COPYING b/docs/COPYING new file mode 100644 index 000000000..fbdf1a8a5 --- /dev/null +++ b/docs/COPYING @@ -0,0 +1,25 @@ +The Python Imaging Library is + +Copyright (c) 1997-2009 by Secret Labs AB +Copyright (c) 1995-2009 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/docs/Guardfile b/docs/Guardfile new file mode 100644 index 000000000..f8f3051ed --- /dev/null +++ b/docs/Guardfile @@ -0,0 +1,10 @@ +#!/usr/bin/env python +from livereload.task import Task +from livereload.compiler import shell + +Task.add('*.rst', shell('make html')) +Task.add('*/*.rst', shell('make html')) +Task.add('_static/*.css', shell('make clean html')) +Task.add('_templates/*', shell('make clean html')) +Task.add('Makefile', shell('make html')) +Task.add('conf.py', shell('make html')) diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 000000000..c0f6cde5b --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,10 @@ +The Python Imaging Library (PIL) is + + Copyright © 1997-2011 by Secret Labs AB + Copyright © 1995-2011 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..f28f802a1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,156 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PillowPILfork.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PillowPILfork.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/PillowPILfork" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PillowPILfork" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +livehtml: html + livereload $(BUILDDIR)/html -p 33233 diff --git a/docs/PIL.rst b/docs/PIL.rst new file mode 100644 index 000000000..6726f661f --- /dev/null +++ b/docs/PIL.rst @@ -0,0 +1,168 @@ +PIL Package (autodoc of remaining modules) +========================================== + +Reference for modules whose documentation has not yet been ported or written +can be found here. + +:mod:`BdfFontFile` Module +------------------------- + +.. automodule:: PIL.BdfFontFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`ContainerIO` Module +------------------------- + +.. automodule:: PIL.ContainerIO + :members: + :undoc-members: + :show-inheritance: + +:mod:`ExifTags` Module +---------------------- + +.. automodule:: PIL.ExifTags + :members: + :undoc-members: + :show-inheritance: + +:mod:`FontFile` Module +---------------------- + +.. automodule:: PIL.FontFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`GdImageFile` Module +------------------------- + +.. automodule:: PIL.GdImageFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`GimpGradientFile` Module +------------------------------ + +.. automodule:: PIL.GimpGradientFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`GimpPaletteFile` Module +----------------------------- + +.. automodule:: PIL.GimpPaletteFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`ImageCms` Module +---------------------- + +.. automodule:: PIL.ImageCms + :members: + :undoc-members: + :show-inheritance: + +.. intentionally skipped documenting this because it's not documented anywhere +:mod:`ImageDraw2` Module +------------------------ + +.. automodule:: PIL.ImageDraw2 + :members: + :undoc-members: + :show-inheritance: + +.. intentionally skipped documenting this because it's deprecated +:mod:`ImageFileIO` Module +------------------------- + +.. automodule:: PIL.ImageFileIO + :members: + :undoc-members: + :show-inheritance: + +:mod:`ImageShow` Module +----------------------- + +.. automodule:: PIL.ImageShow + :members: + :undoc-members: + :show-inheritance: + +:mod:`ImageTransform` Module +---------------------------- + +.. automodule:: PIL.ImageTransform + :members: + :undoc-members: + :show-inheritance: + +:mod:`JpegPresets` Module +------------------------- + +.. automodule:: PIL.JpegPresets + :members: + :undoc-members: + :show-inheritance: + +:mod:`OleFileIO` Module +----------------------- + +.. automodule:: PIL.OleFileIO + :members: + :undoc-members: + :show-inheritance: + +:mod:`PaletteFile` Module +------------------------- + +.. automodule:: PIL.PaletteFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`PcfFontFile` Module +------------------------- + +.. automodule:: PIL.PcfFontFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`TarIO` Module +------------------- + +.. automodule:: PIL.TarIO + :members: + :undoc-members: + :show-inheritance: + +:mod:`TiffTags` Module +---------------------- + +.. automodule:: PIL.TiffTags + :members: + :undoc-members: + :show-inheritance: + +:mod:`WalImageFile` Module +-------------------------- + +.. automodule:: PIL.WalImageFile + :members: + :undoc-members: + :show-inheritance: + +:mod:`_binary` Module +--------------------- + +.. automodule:: PIL._binary + :members: + :undoc-members: + :show-inheritance: + diff --git a/docs/_build/.gitignore b/docs/_build/.gitignore new file mode 100644 index 000000000..b1f9a2ade --- /dev/null +++ b/docs/_build/.gitignore @@ -0,0 +1 @@ +# Empty file, to make the directory available in the repository diff --git a/docs/_static/.gitignore b/docs/_static/.gitignore new file mode 100644 index 000000000..b1f9a2ade --- /dev/null +++ b/docs/_static/.gitignore @@ -0,0 +1 @@ +# Empty file, to make the directory available in the repository diff --git a/docs/_templates/.gitignore b/docs/_templates/.gitignore new file mode 100644 index 000000000..b1f9a2ade --- /dev/null +++ b/docs/_templates/.gitignore @@ -0,0 +1 @@ +# Empty file, to make the directory available in the repository diff --git a/docs/_templates/sidebarhelp.html b/docs/_templates/sidebarhelp.html new file mode 100644 index 000000000..330b3de45 --- /dev/null +++ b/docs/_templates/sidebarhelp.html @@ -0,0 +1,18 @@ +

Need help?

+ +

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

+ +

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

+ diff --git a/docs/about.rst b/docs/about.rst new file mode 100644 index 000000000..dfd88b605 --- /dev/null +++ b/docs/about.rst @@ -0,0 +1,47 @@ +About Pillow +============ + +Goals +----- + +The fork authors' goal is to foster active development of PIL through: + +- Continuous integration testing via `Travis CI`_ +- Publicized development activity on `GitHub`_ +- Regular releases to the `Python Package Index`_ +- Solicitation for community contributions and involvement on `Image-SIG`_ + +.. _Travis CI: https://travis-ci.org/python-imaging/Pillow +.. _GitHub: https://github.com/python-imaging/Pillow +.. _Python Package Index: https://pypi.python.org/pypi/Pillow +.. _Image-SIG: http://mail.python.org/mailman/listinfo/image-sig + +Why a fork? +----------- + +PIL is not setuptools compatible. Please see `this Image-SIG post`_ for a more +detailed explanation. Also, PIL's current bi-yearly (or greater) release +schedule is too infrequent to accomodate the large number and frequency of +issues reported. + +.. _this Image-SIG post: https://mail.python.org/pipermail/image-sig/2010-August/006480.html + +What about the official PIL? +---------------------------- + +.. note:: + + Prior to Pillow 2.0.0, very few image code changes were made. Pillow 2.0.0 + added Python 3 support and includes many bug fixes from many contributors. + +As more time passes since the last PIL release, the likelyhood of a new PIL +release decreases. However, we've yet to hear an official "PIL is dead" +announcement. So if you still want to support PIL, please +`report issues here first`_, then +`open the corresponding Pillow tickets here`_. + +.. _report issues here first: https://bitbucket.org/effbot/pil-2009-raclette/issues + +.. _open the corresponding Pillow tickets here: https://github.com/python-imaging/Pillow/issues + +Please provide a link to the PIL ticket so we can track the issue(s) upstream. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..987b6dbb3 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +import os +import sys + +sys.path.insert(0, os.path.abspath('../')) +import PIL + +### general configuration ### + +needs_sphinx = '1.0' + +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', + 'sphinx.ext.intersphinx'] +intersphinx_mapping = {'http://docs.python.org/2/': None} + +source_suffix = '.rst' +templates_path = ['_templates'] +#source_encoding = 'utf-8-sig' +master_doc = 'index' + +project = u'Pillow (PIL fork)' +copyright = (u'1997-2011 by Secret Labs AB,' + u' 1995-2011 by Fredrik Lundh, 2010-2013 Alex Clark') + +# The short X.Y version. +version = PIL.PILLOW_VERSION +# The full version, including alpha/beta/rc tags. +release = version + +# currently excluding autodoc'd plugs +exclude_patterns = ['_build', 'plugins.rst'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +### HTML output ### + +from better import better_theme_path +html_theme_path = [better_theme_path] +html_theme = 'better' + +html_title = "Pillow v{release} (PIL fork)".format(release=release) +html_short_title = "Home" +html_static_path = ['_static'] + +html_theme_options = {} + +html_sidebars = { + '**': ['localtoc.html', 'sourcelink.html', 'sidebarhelp.html', + 'searchbox.html'], + 'index': ['globaltoc.html', 'sidebarhelp.html', 'searchbox.html'], +} + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Pillowdoc' + + +### LaTeX output (RtD PDF output as well) ### + +latex_elements = {} + +latex_documents = [ + ('index', 'Pillow.tex', u'Pillow (PIL fork) Documentation', u'Author', + 'manual'), +] + + +# skip_api_docs setting will skip PIL.rst if True. Used for working on the +# guides; makes livereload basically instantaneous. +def setup(app): + app.add_config_value('skip_api_docs', False, True) + +skip_api_docs = False + +if skip_api_docs: + exclude_patterns += ['PIL.rst'] diff --git a/docs/guides.rst b/docs/guides.rst new file mode 100644 index 000000000..926d3cfcc --- /dev/null +++ b/docs/guides.rst @@ -0,0 +1,10 @@ +Guides +====== + +.. toctree:: + :maxdepth: 2 + + handbook/overview + handbook/tutorial + handbook/concepts + porting-pil-to-pillow diff --git a/docs/handbook/appendices.rst b/docs/handbook/appendices.rst new file mode 100644 index 000000000..f5482ffcd --- /dev/null +++ b/docs/handbook/appendices.rst @@ -0,0 +1,8 @@ +Appendices +========== + +.. toctree:: + :maxdepth: 2 + + image-file-formats + writing-your-own-file-decoder diff --git a/docs/handbook/concepts.rst b/docs/handbook/concepts.rst new file mode 100644 index 000000000..93f964e41 --- /dev/null +++ b/docs/handbook/concepts.rst @@ -0,0 +1,109 @@ +Concepts +======== + +The Python Imaging Library handles *raster images*; that is, rectangles of +pixel data. + +Bands +----- + +An image can consist of one or more bands of data. The Python Imaging Library +allows you to store several bands in a single image, provided they all have the +same dimensions and depth. + +To get the number and names of bands in an image, use the +:py:meth:`~PIL.Image.Image.getbands` method. + +Mode +---- + +The :term:`mode` of an image defines the type and depth of a pixel in the +image. The current release supports the following standard modes: + + * ``1`` (1-bit pixels, black and white, stored with one pixel per byte) + * ``L`` (8-bit pixels, black and white) + * ``P`` (8-bit pixels, mapped to any other mode using a color palette) + * ``RGB`` (3x8-bit pixels, true color) + * ``RGBA`` (4x8-bit pixels, true color with transparency mask) + * ``CMYK`` (4x8-bit pixels, color separation) + * ``YCbCr`` (3x8-bit pixels, color video format) + * ``I`` (32-bit signed integer pixels) + * ``F`` (32-bit floating point pixels) + +PIL also provides limited support for a few special modes, including ``LA`` (L +with alpha), ``RGBX`` (true color with padding) and ``RGBa`` (true color with +premultiplied alpha). However, PIL doesn’t support user-defined modes; if you +to handle band combinations that are not listed above, use a sequence of Image +objects. + +You can read the mode of an image through the :py:attr:`~PIL.Image.Image.mode` +attribute. This is a string containing one of the above values. + +Size +---- + +You can read the image size through the :py:attr:`~PIL.Image.Image.size` +attribute. This is a 2-tuple, containing the horizontal and vertical size in +pixels. + +Coordinate System +----------------- + +The Python Imaging Library uses a Cartesian pixel coordinate system, with (0,0) +in the upper left corner. Note that the coordinates refer to the implied pixel +corners; the centre of a pixel addressed as (0, 0) actually lies at (0.5, 0.5). + +Coordinates are usually passed to the library as 2-tuples (x, y). Rectangles +are represented as 4-tuples, with the upper left corner given first. For +example, a rectangle covering all of an 800x600 pixel image is written as (0, +0, 800, 600). + +Palette +------- + +The palette mode (``P``) uses a color palette to define the actual color for +each pixel. + +Info +---- + +You can attach auxiliary information to an image using the +:py:attr:`~PIL.Image.Image.info` attribute. This is a dictionary object. + +How such information is handled when loading and saving image files is up to +the file format handler (see the chapter on :ref:`image-file-formats`). Most +handlers add properties to the :py:attr:`~PIL.Image.Image.info` attribute when +loading an image, but ignore it when saving images. + +Filters +------- + +For geometry operations that may map multiple input pixels to a single output +pixel, the Python Imaging Library provides four different resampling *filters*. + +``NEAREST`` + Pick the nearest pixel from the input image. Ignore all other input pixels. + +``BILINEAR`` + Use linear interpolation over a 2x2 environment in the input image. Note + that in the current version of PIL, this filter uses a fixed input + environment when downsampling. + +``BICUBIC`` + Use cubic interpolation over a 4x4 environment in the input image. Note + that in the current version of PIL, this filter uses a fixed input + environment when downsampling. + +``ANTIALIAS`` + Calculate the output pixel value using a high-quality resampling filter (a + truncated sinc) on all pixels that may contribute to the output value. In + the current version of PIL, this filter can only be used with the resize + and thumbnail methods. + + .. versionadded:: 1.1.3 + +Note that in the current version of PIL, the ``ANTIALIAS`` filter is the only +filter that behaves properly when downsampling (that is, when converting a +large image to a small one). The ``BILINEAR`` and ``BICUBIC`` filters use a +fixed input environment, and are best used for scale-preserving geometric +transforms and upsamping. diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst new file mode 100644 index 000000000..21cd615fc --- /dev/null +++ b/docs/handbook/image-file-formats.rst @@ -0,0 +1,617 @@ +.. _image-file-formats: + +Image file formats +================== + +The Python Imaging Library supports a wide variety of raster file formats. +Nearly 30 different file formats can be identified and read by the library. +Write support is less extensive, but most common interchange and presentation +formats are supported. + +The :py:meth:`~PIL.Image.Image.open` function identifies files from their +contents, not their names, but the :py:meth:`~PIL.Image.Image.save` method +looks at the name to determine which format to use, unless the format is given +explicitly. + +Fully supported formats +----------------------- + +BMP +^^^ + +PIL reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``, +or ``RGB`` data. 16-colour images are read as ``P`` images. Run-length encoding +is not supported. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**compression** + Set to ``bmp_rle`` if the file is run-length encoded. + +EPS +^^^ + +PIL identifies EPS files containing image data, and can read files that contain +embedded raster images (ImageData descriptors). If Ghostscript is available, +other EPS files can be read as well. The EPS driver can also write EPS images. + +If Ghostscript is available, you can call the :py:meth:`~PIL.Image.Image.load` +method with the following parameter to affect how Ghostscript renders the EPS + +**scale** + Affects the scale of the resultant rasterized image. If the EPS suggests + that the image be rendered at 100px x 100px, setting this parameter to + 2 will make the Ghostscript render a 200px x 200px image instead. The + relative position of the bounding box is maintained:: + + im = Image.open(...) + im.size #(100,100) + im.load(scale=2) + im.size #(200,200) + +GIF +^^^ + +PIL reads GIF87a and GIF89a versions of the GIF file format. The library writes +run-length encoded GIF87a files. Note that GIF files are always read as +grayscale (``L``) or palette mode (``P``) images. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**background** + Default background color (a palette color index). + +**duration** + Time between frames in an animation (in milliseconds). + +**transparency** + Transparency color index. This key is omitted if the image is not + transparent. + +**version** + Version (either ``GIF87a`` or ``GIF89a``). + +Reading sequences +~~~~~~~~~~~~~~~~~ + +The GIF loader supports the :py:meth:`~file.seek` and :py:meth:`~file.tell` +methods. You can seek to the next frame (``im.seek(im.tell() + 1``), or rewind +the file by seeking to the first frame. Random access is not supported. + +Reading local images +~~~~~~~~~~~~~~~~~~~~ + +The GIF loader creates an image memory the same size as the GIF file’s *logical +screen size*, and pastes the actual pixel data (the *local image*) into this +image. If you only want the actual pixel rectangle, you can manipulate the +:py:attr:`~PIL.Image.Image.size` and :py:attr:`~PIL.Image.Image.tile` +attributes before loading the file:: + + im = Image.open(...) + + if im.tile[0][0] == "gif": + # only read the first "local image" from this GIF file + tag, (x0, y0, x1, y1), offset, extra = im.tile[0] + im.size = (x1 - x0, y1 - y0) + im.tile = [(tag, (0, 0) + im.size, offset, extra)] + +IM +^^ + +IM is a format used by LabEye and other applications based on the IFUNC image +processing library. The library reads and writes most uncompressed interchange +versions of this format. + +IM is the only format that can store all internal PIL formats. + +JPEG +^^^^ + +PIL reads JPEG, JFIF, and Adobe JPEG files containing ``L``, ``RGB``, or +``CMYK`` data. It writes standard and progressive JFIF files. + +Using the :py:meth:`~PIL.Image.Image.draft` method, you can speed things up by +converting ``RGB`` images to ``L``, and resize images to 1/2, 1/4 or 1/8 of +their original size while loading them. The :py:meth:`~PIL.Image.Image.draft` +method also configures the JPEG decoder to trade some quality for speed. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**jfif** + JFIF application marker found. If the file is not a JFIF file, this key is + not present. + +**adobe** + Adobe application marker found. If the file is not an Adobe JPEG file, this + key is not present. + +**progression** + Indicates that this is a progressive JPEG file. + +The :py:meth:`~PIL.Image.Image.save` method supports the following options: + +**quality** + The image quality, on a scale from 1 (worst) to 95 (best). The default is + 75. Values above 95 should be avoided; 100 disables portions of the JPEG + compression algorithm, and results in large files with hardly any gain in = + image quality. + +**optimize** + If present, indicates that the encoder should make an extra pass over the + image in order to select optimal encoder settings. + +**progressive** + If present, indicates that this image should be stored as a progressive + JPEG file. + +.. note:: + + To enable JPEG support, you need to build and install the IJG JPEG library + before building the Python Imaging Library. See the distribution README for + details. + +MSP +^^^ + +PIL identifies and reads MSP files from Windows 1 and 2. The library writes +uncompressed (Windows 1) versions of this format. + +PCX +^^^ + +PIL reads and writes PCX files containing ``1``, ``L``, ``P``, or ``RGB`` data. + +PNG +^^^ + +PIL identifies, reads, and writes PNG files containing ``1``, ``L``, ``P``, +``RGB``, or ``RGBA`` data. Interlaced files are supported as of v1.1.7. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties, when appropriate: + +**gamma** + Gamma, given as a floating point number. + +**transparency** + Transparency color index. This key is omitted if the image is not a + transparent palette image. + +The :py:meth:`~PIL.Image.Image.save` method supports the following options: + +**optimize** + If present, instructs the PNG writer to make the output file as small as + possible. This includes extra processing in order to find optimal encoder + settings. + +**transparency** + For ``P``, ``L``, and ``RGB`` images, this option controls what + color image to mark as transparent. + +**bits (experimental)** + For ``P`` images, this option controls how many bits to store. If omitted, + the PNG writer uses 8 bits (256 colors). + +**dictionary (experimental)** + Set the ZLIB encoder dictionary. + +.. note:: + + To enable PNG support, you need to build and install the ZLIB compression + library before building the Python Imaging Library. See the distribution + README for details. + +PPM +^^^ + +PIL reads and writes PBM, PGM and PPM files containing ``1``, ``L`` or ``RGB`` +data. + +SPIDER +^^^^^^ + +PIL reads and writes SPIDER image files of 32-bit floating point data +("F;32F"). + +PIL also reads SPIDER stack files containing sequences of SPIDER images. The +:py:meth:`~file.seek` and :py:meth:`~file.tell` methods are supported, and +random access is allowed. + +The :py:meth:`~PIL.Image.Image.open` method sets the following attributes: + +**format** + Set to ``SPIDER`` + +**istack** + Set to 1 if the file is an image stack, else 0. + +**nimages** + Set to the number of images in the stack. + +A convenience method, :py:meth:`~PIL.Image.Image.convert2byte`, is provided for +converting floating point data to byte data (mode ``L``):: + + im = Image.open('image001.spi').convert2byte() + +Writing files in SPIDER format +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The extension of SPIDER files may be any 3 alphanumeric characters. Therefore +the output format must be specified explicitly:: + + im.save('newimage.spi', format='SPIDER') + +For more information about the SPIDER image processing package, see the +`SPIDER home page`_ at `Wadsworth Center`_. + +.. _SPIDER home page: http://www.wadsworth.org/spider_doc/spider/docs/master.html +.. _Wadsworth Center: http://www.wadsworth.org/ + +TIFF +^^^^ + +PIL reads and writes TIFF files. It can read both striped and tiled images, +pixel and plane interleaved multi-band images, and either uncompressed, or +Packbits, LZW, or JPEG compressed images. + +If you have libtiff and its headers installed, PIL can read and write many more +kinds of compressed TIFF files. If not, PIL will always write uncompressed +files. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**compression** + Compression mode. + +**dpi** + Image resolution as an (xdpi, ydpi) tuple, where applicable. You can use + the :py:attr:`~PIL.Image.Image.tag` attribute to get more detailed + information about the image resolution. + + .. versionadded:: 1.1.5 + +In addition, the :py:attr:`~PIL.Image.Image.tag` attribute contains a +dictionary of decoded TIFF fields. Values are stored as either strings or +tuples. Note that only short, long and ASCII tags are correctly unpacked by +this release. + +Saving Tiff Images +~~~~~~~~~~~~~~~~~~ + +The :py:meth:`~PIL.Image.Image.save` method can take the following keyword arguments: + +**tiffinfo** + A :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` object or dict + object containing tiff tags and values. The TIFF field type is + autodetected for Numeric and string values, any other types + require using an :py:class:`~PIL.TiffImagePlugin.ImageFileDirectory` + object and setting the type in + :py:attr:`~PIL.TiffImagePlugin.ImageFileDirectory.tagtype` with + the appropriate numerical value from + ``TiffTags.TYPES``. + + .. versionadded:: 2.3.0 + +**compression** + A string containing the desired compression method for the + file. (valid only with libtiff installed) Valid compression + methods are: ``[None, "tiff_ccitt", "group3", "group4", + "tiff_jpeg", "tiff_adobe_deflate", "tiff_thunderscan", + "tiff_deflate", "tiff_sgilog", "tiff_sgilog24", "tiff_raw_16"]`` + +These arguments to set the tiff header fields are an alternative to using the general tags available through tiffinfo. + +**description** + +**software** + +**date time** + +**artist** + +**copyright** + Strings + +**resolution unit** + A string of "inch", "centimeter" or "cm" + +**resolution** + +**x resolution** + +**y resolution** + +**dpi** + Either a Float, Integer, or 2 tuple of (numerator, + denominator). Resolution implies an equal x and y resolution, dpi + also implies a unit of inches. + +WebP +^^^^ + +PIL reads and writes WebP files. The specifics of PIL's capabilities with this +format are currently undocumented. + +The :py:meth:`~PIL.Image.Image.save` method supports the following options: + +**lossless** + If present, instructs the WEBP writer to use lossless + compression. + +**quality** + Integer, 1-100, Defaults to 80. Sets the quality level for + lossy compression. + +**icc_procfile** + The ICC Profile to include in the saved file. Only supported if + the system webp library was built with webpmux support. + +**exif** + The exif data to include in the saved file. Only supported if + the system webp library was built with webpmux support. + +XBM +^^^ + +PIL reads and writes X bitmap files (mode ``1``). + +XV Thumbnails +^^^^^^^^^^^^^ + +PIL can read XV thumbnail files. + +Read-only formats +----------------- + +CUR +^^^ + +CUR is used to store cursors on Windows. The CUR decoder reads the largest +available cursor. Animated cursors are not supported. + +DCX +^^^ + +DCX is a container file format for PCX files, defined by Intel. The DCX format +is commonly used in fax applications. The DCX decoder can read files containing +``1``, ``L``, ``P``, or ``RGB`` data. + +When the file is opened, only the first image is read. You can use +:py:meth:`~file.seek` or :py:mod:`~PIL.ImageSequence` to read other images. + +FLI, FLC +^^^^^^^^ + +PIL reads Autodesk FLI and FLC animations. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**duration** + The delay (in milliseconds) between each frame. + +FPX +^^^ + +PIL reads Kodak FlashPix files. In the current version, only the highest +resolution image is read from the file, and the viewing transform is not taken +into account. + +.. note:: + + To enable full FlashPix support, you need to build and install the IJG JPEG + library before building the Python Imaging Library. See the distribution + README for details. + +GBR +^^^ + +The GBR decoder reads GIMP brush files. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**description** + The brush name. + +GD +^^ + +PIL reads uncompressed GD files. Note that this file format cannot be +automatically identified, so you must use :py:func:`PIL.GdImageFile.open` to +read such a file. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**transparency** + Transparency color index. This key is omitted if the image is not + transparent. + +ICO +^^^ + +ICO is used to store icons on Windows. The largest available icon is read. + +IMT +^^^ + +PIL reads Image Tools images containing ``L`` data. + +IPTC/NAA +^^^^^^^^ + +PIL provides limited read support for IPTC/NAA newsphoto files. + +MCIDAS +^^^^^^ + +PIL identifies and reads 8-bit McIdas area files. + +MIC (read only) + +PIL identifies and reads Microsoft Image Composer (MIC) files. When opened, the +first sprite in the file is loaded. You can use :py:meth:`~file.seek` and +:py:meth:`~file.tell` to read other sprites from the file. + +PCD +^^^ + +PIL reads PhotoCD files containing ``RGB`` data. By default, the 768x512 +resolution is read. You can use the :py:meth:`~PIL.Image.Image.draft` method to +read the lower resolution versions instead, thus effectively resizing the image +to 384x256 or 192x128. Higher resolutions cannot be read by the Python Imaging +Library. + +PSD +^^^ + +PIL identifies and reads PSD files written by Adobe Photoshop 2.5 and 3.0. + +SGI +^^^ + +PIL reads uncompressed ``L``, ``RGB``, and ``RGBA`` files. + +TGA +^^^ + +PIL reads 24- and 32-bit uncompressed and run-length encoded TGA files. + +WAL +^^^ + +.. versionadded:: 1.1.4 + +PIL reads Quake2 WAL texture files. + +Note that this file format cannot be automatically identified, so you must use +the open function in the :py:mod:`~PIL.WalImageFile` module to read files in +this format. + +By default, a Quake2 standard palette is attached to the texture. To override +the palette, use the putpalette method. + +XPM +^^^ + +PIL reads X pixmap files (mode ``P``) with 256 colors or less. + +The :py:meth:`~PIL.Image.Image.open` method sets the following +:py:attr:`~PIL.Image.Image.info` properties: + +**transparency** + Transparency color index. This key is omitted if the image is not + transparent. + +Write-only formats +------------------ + +PALM +^^^^ + +PIL provides write-only support for PALM pixmap files. + +The format code is ``Palm``, the extension is ``.palm``. + +PDF +^^^ + +PIL can write PDF (Acrobat) images. Such images are written as binary PDF 1.1 +files, using either JPEG or HEX encoding depending on the image mode (and +whether JPEG support is available or not). + +PIXAR (read only) + +PIL provides limited support for PIXAR raster files. The library can identify +and read “dumped” RGB files. + +The format code is ``PIXAR``. + +Identify-only formats +--------------------- + +BUFR +^^^^ + +.. versionadded:: 1.1.3 + +PIL provides a stub driver for BUFR files. + +To add read or write support to your application, use +:py:func:`PIL.BufrStubImagePlugin.register_handler`. + +FITS +^^^^ + +.. versionadded:: 1.1.5 + +PIL provides a stub driver for FITS files. + +To add read or write support to your application, use +:py:func:`PIL.FitsStubImagePlugin.register_handler`. + +GRIB +^^^^ + +.. versionadded:: 1.1.5 + +PIL provides a stub driver for GRIB files. + +The driver requires the file to start with a GRIB header. If you have files +with embedded GRIB data, or files with multiple GRIB fields, your application +has to seek to the header before passing the file handle to PIL. + +To add read or write support to your application, use +:py:func:`PIL.GribStubImagePlugin.register_handler`. + +HDF5 +^^^^ + +.. versionadded:: 1.1.5 + +PIL provides a stub driver for HDF5 files. + +To add read or write support to your application, use +:py:func:`PIL.Hdf5StubImagePlugin.register_handler`. + +MPEG +^^^^ + +PIL identifies MPEG files. + +WMF +^^^ + +PIL can identify placable WMF files. + +In PIL 1.1.4 and earlier, the WMF driver provides some limited rendering +support, but not enough to be useful for any real application. + +In PIL 1.1.5 and later, the WMF driver is a stub driver. To add WMF read or +write support to your application, use +:py:func:`PIL.WmfImagePlugin.register_handler` to register a WMF handler. + +:: + + from PIL import Image + from PIL import WmfImagePlugin + + class WmfHandler: + def open(self, im): + ... + def load(self, im): + ... + return image + def save(self, im, fp, filename): + ... + + wmf_handler = WmfHandler() + + WmfImagePlugin.register_handler(wmf_handler) + + im = Image.open("sample.wmf") diff --git a/docs/handbook/overview.rst b/docs/handbook/overview.rst new file mode 100644 index 000000000..f1c26e616 --- /dev/null +++ b/docs/handbook/overview.rst @@ -0,0 +1,46 @@ +Overview +======== + +The **Python Imaging Library** adds image processing capabilities to your +Python interpreter. + +This library provides extensive file format support, an efficient internal +representation, and fairly powerful image processing capabilities. + +The core image library is designed for fast access to data stored in a few +basic pixel formats. It should provide a solid foundation for a general image +processing tool. + +Let’s look at a few possible uses of this library. + +Image Archives +-------------- + +The Python Imaging Library is ideal for for image archival and batch processing +applications. You can use the library to create thumbnails, convert between +file formats, print images, etc. + +The current version identifies and reads a large number of formats. Write +support is intentionally restricted to the most commonly used interchange and +presentation formats. + +Image Display +------------- + +The current release includes Tk :py:class:`~PIL.ImageTk.PhotoImage` and +:py:class:`~PIL.ImageTk.BitmapImage` interfaces, as well as a :py:mod:`Windows +DIB interface ` that can be used with PythonWin and other +Windows-based toolkits. Many other GUI toolkits come with some kind of PIL +support. + +For debugging, there’s also a :py:meth:`show` method which saves an image to +disk, and calls an external display utility. + +Image Processing +---------------- + +The library contains basic image processing functionality, including point operations, filtering with a set of built-in convolution kernels, and colour space conversions. + +The library also supports image resizing, rotation and arbitrary affine transforms. + +There’s a histogram method allowing you to pull some statistics out of an image. This can be used for automatic contrast enhancement, and for global statistical analysis. diff --git a/docs/handbook/tutorial.rst b/docs/handbook/tutorial.rst new file mode 100644 index 000000000..d7bb98386 --- /dev/null +++ b/docs/handbook/tutorial.rst @@ -0,0 +1,539 @@ +Tutorial +======== + +Using the Image class +--------------------- + +The most important class in the Python Imaging Library is the +:py:class:`~PIL.Image.Image` class, defined in the module with the same name. +You can create instances of this class in several ways; either by loading +images from files, processing other images, or creating images from scratch. + +To load an image from a file, use the :py:func:`~PIL.Image.open` function +in the :py:mod:`~PIL.Image` module:: + + >>> from PIL import Image + >>> im = Image.open("lena.ppm") + +If successful, this function returns an :py:class:`~PIL.Image.Image` object. +You can now use instance attributes to examine the file contents:: + + >>> from __future__ import print_function + >>> print(im.format, im.size, im.mode) + PPM (512, 512) RGB + +The :py:attr:`~PIL.Image.Image.format` attribute identifies the source of an +image. If the image was not read from a file, it is set to None. The size +attribute is a 2-tuple containing width and height (in pixels). The +:py:attr:`~PIL.Image.Image.mode` attribute defines the number and names of the +bands in the image, and also the pixel type and depth. Common modes are “L” +(luminance) for greyscale images, “RGB” for true color images, and “CMYK” for +pre-press images. + +If the file cannot be opened, an :py:exc:`IOError` exception is raised. + +Once you have an instance of the :py:class:`~PIL.Image.Image` class, you can use +the methods defined by this class to process and manipulate the image. For +example, let’s display the image we just loaded:: + + >>> im.show() + +.. note:: + + The standard version of :py:meth:`~PIL.Image.Image.show` is not very + efficient, since it saves the image to a temporary file and calls the + :command:`xv` utility to display the image. If you don’t have :command:`xv` + installed, it won’t even work. When it does work though, it is very handy + for debugging and tests. + +The following sections provide an overview of the different functions provided in this library. + +Reading and writing images +-------------------------- + +The Python Imaging Library supports a wide variety of image file formats. To +read files from disk, use the :py:func:`~PIL.Image.open` function in the +:py:mod:`~PIL.Image` module. You don’t have to know the file format to open a +file. The library automatically determines the format based on the contents of +the file. + +To save a file, use the :py:meth:`~PIL.Image.Image.save` method of the +:py:class:`~PIL.Image.Image` class. When saving files, the name becomes +important. Unless you specify the format, the library uses the filename +extension to discover which file storage format to use. + +Convert files to JPEG +^^^^^^^^^^^^^^^^^^^^^ + +:: + + from __future__ import print_function + import os, sys + from PIL import Image + + for infile in sys.argv[1:]: + f, e = os.path.splitext(infile) + outfile = f + ".jpg" + if infile != outfile: + try: + Image.open(infile).save(outfile) + except IOError: + print("cannot convert", infile) + +A second argument can be supplied to the :py:meth:`~PIL.Image.Image.save` +method which explicitly specifies a file format. If you use a non-standard +extension, you must always specify the format this way: + +Create JPEG thumbnails +^^^^^^^^^^^^^^^^^^^^^^ + +:: + + from __future__ import print_function + import os, sys + from PIL import Image + + size = (128, 128) + + for infile in sys.argv[1:]: + outfile = os.path.splitext(infile)[0] + ".thumbnail" + if infile != outfile: + try: + im = Image.open(infile) + im.thumbnail(size) + im.save(outfile, "JPEG") + except IOError: + print("cannot create thumbnail for", infile) + + +It is important to note that the library doesn’t decode or load the raster data +unless it really has to. When you open a file, the file header is read to +determine the file format and extract things like mode, size, and other +properties required to decode the file, but the rest of the file is not +processed until later. + +This means that opening an image file is a fast operation, which is independent +of the file size and compression type. Here’s a simple script to quickly +identify a set of image files: + +Identify Image Files +^^^^^^^^^^^^^^^^^^^^ + +:: + + from __future__ import print_function + import sys + from PIL import Image + + for infile in sys.argv[1:]: + try: + im = Image.open(infile) + print(infile, im.format, "%dx%d" % im.size, im.mode) + except IOError: + pass + +Cutting, pasting, and merging images +------------------------------------ + +The :py:class:`~PIL.Image.Image` class contains methods allowing you to +manipulate regions within an image. To extract a sub-rectangle from an image, +use the :py:meth:`~PIL.Image.Image.crop` method. + +Copying a subrectangle from an image +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + box = (100, 100, 400, 400) + region = im.crop(box) + +The region is defined by a 4-tuple, where coordinates are (left, upper, right, +lower). The Python Imaging Library uses a coordinate system with (0, 0) in the +upper left corner. Also note that coordinates refer to positions between the +pixels, so the region in the above example is exactly 300x300 pixels. + +The region could now be processed in a certain manner and pasted back. + +Processing a subrectangle, and pasting it back +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + region = region.transpose(Image.ROTATE_180) + im.paste(region, box) + +When pasting regions back, the size of the region must match the given region +exactly. In addition, the region cannot extend outside the image. However, the +modes of the original image and the region do not need to match. If they don’t, +the region is automatically converted before being pasted (see the section on +:ref:`color-transforms` below for details). + +Here’s an additional example: + +Rolling an image +^^^^^^^^^^^^^^^^ + +:: + + def roll(image, delta): + "Roll an image sideways" + + xsize, ysize = image.size + + delta = delta % xsize + if delta == 0: return image + + part1 = image.crop((0, 0, delta, ysize)) + part2 = image.crop((delta, 0, xsize, ysize)) + image.paste(part2, (0, 0, xsize-delta, ysize)) + image.paste(part1, (xsize-delta, 0, xsize, ysize)) + + return image + +For more advanced tricks, the paste method can also take a transparency mask as +an optional argument. In this mask, the value 255 indicates that the pasted +image is opaque in that position (that is, the pasted image should be used as +is). The value 0 means that the pasted image is completely transparent. Values +in-between indicate different levels of transparency. + +The Python Imaging Library also allows you to work with the individual bands of +an multi-band image, such as an RGB image. The split method creates a set of +new images, each containing one band from the original multi-band image. The +merge function takes a mode and a tuple of images, and combines them into a new +image. The following sample swaps the three bands of an RGB image: + +Splitting and merging bands +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + r, g, b = im.split() + im = Image.merge("RGB", (b, g, r)) + +Note that for a single-band image, :py:meth:`~PIL.Image.Image.split` returns +the image itself. To work with individual color bands, you may want to convert +the image to “RGB” first. + +Geometrical transforms +---------------------- + +The :py:class:`PIL.Image.Image` class contains methods to +:py:meth:`~PIL.Image.Image.resize` and :py:meth:`~PIL.Image.Image.rotate` an +image. The former takes a tuple giving the new size, the latter the angle in +degrees counter-clockwise. + +Simple geometry transforms +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + out = im.resize((128, 128)) + out = im.rotate(45) # degrees counter-clockwise + +To rotate the image in 90 degree steps, you can either use the +:py:meth:`~PIL.Image.Image.rotate` method or the +:py:meth:`~PIL.Image.Image.transpose` method. The latter can also be used to +flip an image around its horizontal or vertical axis. + +Transposing an image +^^^^^^^^^^^^^^^^^^^^ + +:: + + out = im.transpose(Image.FLIP_LEFT_RIGHT) + out = im.transpose(Image.FLIP_TOP_BOTTOM) + out = im.transpose(Image.ROTATE_90) + out = im.transpose(Image.ROTATE_180) + out = im.transpose(Image.ROTATE_270) + +There’s no difference in performance or result between ``transpose(ROTATE)`` +and corresponding :py:meth:`~PIL.Image.Image.rotate` operations. + +A more general form of image transformations can be carried out via the +:py:meth:`~PIL.Image.Image.transform` method. + +.. _color-transforms: + +Color transforms +---------------- + +The Python Imaging Library allows you to convert images between different pixel +representations using the :py:meth:`~PIL.Image.Image.convert` method. + +Converting between modes +^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + im = Image.open("lena.ppm").convert("L") + +The library supports transformations between each supported mode and the “L” +and “RGB” modes. To convert between other modes, you may have to use an +intermediate image (typically an “RGB” image). + +Image enhancement +----------------- + +The Python Imaging Library provides a number of methods and modules that can be +used to enhance images. + +Filters +^^^^^^^ + +The :py:mod:`~PIL.ImageFilter` module contains a number of pre-defined +enhancement filters that can be used with the +:py:meth:`~PIL.Image.Image.filter` method. + +Applying filters +~~~~~~~~~~~~~~~~ + +:: + + from PIL import ImageFilter + out = im.filter(ImageFilter.DETAIL) + +Point Operations +^^^^^^^^^^^^^^^^ + +The :py:meth:`~PIL.Image.Image.point` method can be used to translate the pixel +values of an image (e.g. image contrast manipulation). In most cases, a +function object expecting one argument can be passed to the this method. Each +pixel is processed according to that function: + +Applying point transforms +~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + # multiply each pixel by 1.2 + out = im.point(lambda i: i * 1.2) + +Using the above technique, you can quickly apply any simple expression to an +image. You can also combine the :py:meth:`~PIL.Image.Image.point` and +:py:meth:`~PIL.Image.Image.paste` methods to selectively modify an image: + +Processing individual bands +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + # split the image into individual bands + source = im.split() + + R, G, B = 0, 1, 2 + + # select regions where red is less than 100 + mask = source[R].point(lambda i: i < 100 and 255) + + # process the green band + out = source[G].point(lambda i: i * 0.7) + + # paste the processed band back, but only where red was < 100 + source[G].paste(out, None, mask) + + # build a new multiband image + im = Image.merge(im.mode, source) + +Note the syntax used to create the mask:: + + imout = im.point(lambda i: expression and 255) + +Python only evaluates the portion of a logical expression as is necessary to +determine the outcome, and returns the last value examined as the result of the +expression. So if the expression above is false (0), Python does not look at +the second operand, and thus returns 0. Otherwise, it returns 255. + +Enhancement +^^^^^^^^^^^ + +For more advanced image enhancement, you can use the classes in the +:py:mod:`~PIL.ImageEnhance` module. Once created from an image, an enhancement +object can be used to quickly try out different settings. + +You can adjust contrast, brightness, color balance and sharpness in this way. + +Enhancing images +~~~~~~~~~~~~~~~~ + +:: + + from PIL import ImageEnhance + + enh = ImageEnhance.Contrast(im) + enh.enhance(1.3).show("30% more contrast") + +Image sequences +--------------- + +The Python Imaging Library contains some basic support for image sequences +(also called animation formats). Supported sequence formats include FLI/FLC, +GIF, and a few experimental formats. TIFF files can also contain more than one +frame. + +When you open a sequence file, PIL automatically loads the first frame in the +sequence. You can use the seek and tell methods to move between different +frames: + +Reading sequences +^^^^^^^^^^^^^^^^^ + +:: + + from PIL import Image + + im = Image.open("animation.gif") + im.seek(1) # skip to the second frame + + try: + while 1: + im.seek(im.tell()+1) + # do something to im + except EOFError: + pass # end of sequence + +As seen in this example, you’ll get an :py:exc:`EOFError` exception when the +sequence ends. + +Note that most drivers in the current version of the library only allow you to +seek to the next frame (as in the above example). To rewind the file, you may +have to reopen it. + +The following iterator class lets you to use the for-statement to loop over the +sequence: + +A sequence iterator class +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + class ImageSequence: + def __init__(self, im): + self.im = im + def __getitem__(self, ix): + try: + if ix: + self.im.seek(ix) + return self.im + except EOFError: + raise IndexError # end of sequence + + for frame in ImageSequence(im): + # ...do something to frame... + + +Postscript printing +------------------- + +The Python Imaging Library includes functions to print images, text and +graphics on Postscript printers. Here’s a simple example: + +Drawing Postscript +^^^^^^^^^^^^^^^^^^ + +:: + + from PIL import Image + from PIL import PSDraw + + im = Image.open("lena.ppm") + title = "lena" + box = (1*72, 2*72, 7*72, 10*72) # in points + + ps = PSDraw.PSDraw() # default is sys.stdout + ps.begin_document(title) + + # draw the image (75 dpi) + ps.image(box, im, 75) + ps.rectangle(box) + + # draw centered title + ps.setfont("HelveticaNarrow-Bold", 36) + w, h, b = ps.textsize(title) + ps.text((4*72-w/2, 1*72-h), title) + + ps.end_document() + +More on reading images +---------------------- + +As described earlier, the :py:func:`~PIL.Image.open` function of the +:py:mod:`~PIL.Image` module is used to open an image file. In most cases, you +simply pass it the filename as an argument:: + + im = Image.open("lena.ppm") + +If everything goes well, the result is an :py:class:`PIL.Image.Image` object. +Otherwise, an :exc:`IOError` exception is raised. + +You can use a file-like object instead of the filename. The object must +implement :py:meth:`~file.read`, :py:meth:`~file.seek` and +:py:meth:`~file.tell` methods, and be opened in binary mode. + +Reading from an open file +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + fp = open("lena.ppm", "rb") + im = Image.open(fp) + +To read an image from string data, use the :py:class:`~StringIO.StringIO` +class: + +Reading from a string +^^^^^^^^^^^^^^^^^^^^^ + +:: + + import StringIO + + im = Image.open(StringIO.StringIO(buffer)) + +Note that the library rewinds the file (using ``seek(0)``) before reading the +image header. In addition, seek will also be used when the image data is read +(by the load method). If the image file is embedded in a larger file, such as a +tar file, you can use the :py:class:`~PIL.ContainerIO` or +:py:class:`~PIL.TarIO` modules to access it. + +Reading from a tar archive +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + from PIL import TarIO + + fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm") + im = Image.open(fp) + +Controlling the decoder +----------------------- + +Some decoders allow you to manipulate the image while reading it from a file. +This can often be used to speed up decoding when creating thumbnails (when +speed is usually more important than quality) and printing to a monochrome +laser printer (when only a greyscale version of the image is needed). + +The :py:meth:`~PIL.Image.Image.draft` method manipulates an opened but not yet +loaded image so it as closely as possible matches the given mode and size. This +is done by reconfiguring the image decoder. + +Reading in draft mode +^^^^^^^^^^^^^^^^^^^^^ + +:: + + from __future__ import print_function + im = Image.open(file) + print("original =", im.mode, im.size) + + im.draft("L", (100, 100)) + print("draft =", im.mode, im.size) + +This prints something like:: + + original = RGB (512, 512) + draft = L (128, 128) + +Note that the resulting image may not exactly match the requested mode and +size. To make sure that the image is not larger than the given size, use the +thumbnail method instead. diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst new file mode 100644 index 000000000..10833a53e --- /dev/null +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -0,0 +1,281 @@ +Writing your own file decoder +============================= + +The Python Imaging Library uses a plug-in model which allows you to +add your own decoders to the library, without any changes to the +library itself. Such plug-ins usually have names like +:file:`XxxImagePlugin.py`, where ``Xxx`` is a unique format name +(usually an abbreviation). + +.. warning:: Pillow >= 2.1.0 no longer automatically imports any file in the Python path with a name ending in :file:`ImagePlugin.py`. You will need to import your decoder manually. + +A decoder plug-in should contain a decoder class, based on the +:py:class:`PIL.ImageFile.ImageFile` base class. This class should provide an +:py:meth:`_open` method, which reads the file header and sets up at least the +:py:attr:`~PIL.Image.Image.mode` and :py:attr:`~PIL.Image.Image.size` +attributes. To be able to load the file, the method must also create a list of +:py:attr:`tile` descriptors. The class must be explicitly registered, via a +call to the :py:mod:`~PIL.Image` module. + +For performance reasons, it is important that the :py:meth:`_open` method +quickly rejects files that do not have the appropriate contents. + +Example +------- + +The following plug-in supports a simple format, which has a 128-byte header +consisting of the words “SPAM” followed by the width, height, and pixel size in +bits. The header fields are separated by spaces. The image data follows +directly after the header, and can be either bi-level, greyscale, or 24-bit +true color. + +**SpamImagePlugin.py**:: + + from PIL import Image, ImageFile + import string + + class SpamImageFile(ImageFile.ImageFile): + + format = "SPAM" + format_description = "Spam raster image" + + def _open(self): + + # check header + header = self.fp.read(128) + if header[:4] != "SPAM": + raise SyntaxError, "not a SPAM file" + + header = string.split(header) + + # size in pixels (width, height) + self.size = int(header[1]), int(header[2]) + + # mode setting + bits = int(header[3]) + if bits == 1: + self.mode = "1" + elif bits == 8: + self.mode = "L" + elif bits == 24: + self.mode = "RGB" + else: + raise SyntaxError, "unknown number of bits" + + # data descriptor + self.tile = [ + ("raw", (0, 0) + self.size, 128, (self.mode, 0, 1)) + ] + + Image.register_open("SPAM", SpamImageFile) + + Image.register_extension("SPAM", ".spam") + Image.register_extension("SPAM", ".spa") # dos version + +The format handler must always set the :py:attr:`~PIL.Image.Image.size` and +:py:attr:`~PIL.Image.Image.mode` attributes. If these are not set, the file +cannot be opened. To simplify the decoder, the calling code considers +exceptions like :py:exc:`SyntaxError`, :py:exc:`KeyError`, and +:py:exc:`IndexError`, as a failure to identify the file. + +Note that the decoder must be explicitly registered using +:py:func:`PIL.Image.register_open`. Although not required, it is also a good +idea to register any extensions used by this format. + +The :py:attr:`tile` attribute +----------------------------- + +To be able to read the file as well as just identifying it, the :py:attr:`tile` +attribute must also be set. This attribute consists of a list of tile +descriptors, where each descriptor specifies how data should be loaded to a +given region in the image. In most cases, only a single descriptor is used, +covering the full image. + +The tile descriptor is a 4-tuple with the following contents:: + + (decoder, region, offset, parameters) + +The fields are used as follows: + +**decoder** + Specifies which decoder to use. The ``raw`` decoder used here supports + uncompressed data, in a variety of pixel formats. For more information on + this decoder, see the description below. + +**region** + A 4-tuple specifying where to store data in the image. + +**offset** + Byte offset from the beginning of the file to image data. + +**parameters** + Parameters to the decoder. The contents of this field depends on the + decoder specified by the first field in the tile descriptor tuple. If the + decoder doesn’t need any parameters, use None for this field. + +Note that the :py:attr:`tile` attribute contains a list of tile descriptors, +not just a single descriptor. + +The ``raw`` decoder + +The ``raw`` decoder is used to read uncompressed data from an image file. It +can be used with most uncompressed file formats, such as PPM, BMP, uncompressed +TIFF, and many others. To use the raw decoder with the +:py:func:`PIL.Image.fromstring` function, use the following syntax:: + + image = Image.fromstring( + mode, size, data, "raw", + raw mode, stride, orientation + ) + +When used in a tile descriptor, the parameter field should look like:: + + (raw mode, stride, orientation) + +The fields are used as follows: + +**raw mode** + The pixel layout used in the file, and is used to properly convert data to + PIL’s internal layout. For a summary of the available formats, see the + table below. + +**stride** + The distance in bytes between two consecutive lines in the image. If 0, the + image is assumed to be packed (no padding between lines). If omitted, the + stride defaults to 0. + +**orientation** + + Whether the first line in the image is the top line on the screen (1), or + the bottom line (-1). If omitted, the orientation defaults to 1. + +The **raw mode** field is used to determine how the data should be unpacked to +match PIL’s internal pixel layout. PIL supports a large set of raw modes; for a +complete list, see the table in the :py:mod:`Unpack.c` module. The following +table describes some commonly used **raw modes**: + ++-----------+-----------------------------------------------------------------+ +| mode | description | ++===========+=================================================================+ +| ``1`` | 1-bit bilevel, stored with the leftmost pixel in the most | +| | significant bit. 0 means black, 1 means white. | ++-----------+-----------------------------------------------------------------+ +| ``1;I`` | 1-bit inverted bilevel, stored with the leftmost pixel in the | +| | most significant bit. 0 means white, 1 means black. | ++-----------+-----------------------------------------------------------------+ +| ``1;R`` | 1-bit reversed bilevel, stored with the leftmost pixel in the | +| | least significant bit. 0 means black, 1 means white. | ++-----------+-----------------------------------------------------------------+ +| ``L`` | 8-bit greyscale. 0 means black, 255 means white. | ++-----------+-----------------------------------------------------------------+ +| ``L;I`` | 8-bit inverted greyscale. 0 means white, 255 means black. | ++-----------+-----------------------------------------------------------------+ +| ``P`` | 8-bit palette-mapped image. | ++-----------+-----------------------------------------------------------------+ +| ``RGB`` | 24-bit true colour, stored as (red, green, blue). | ++-----------+-----------------------------------------------------------------+ +| ``BGR`` | 24-bit true colour, stored as (blue, green, red). | ++-----------+-----------------------------------------------------------------+ +| ``RGBX`` | 24-bit true colour, stored as (blue, green, red, pad). | ++-----------+-----------------------------------------------------------------+ +| ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, the | +| | all green pixels, finally all blue pixels). | ++-----------+-----------------------------------------------------------------+ + +Note that for the most common cases, the raw mode is simply the same as the mode. + +The Python Imaging Library supports many other decoders, including JPEG, PNG, +and PackBits. For details, see the :file:`decode.c` source file, and the +standard plug-in implementations provided with the library. + +Decoding floating point data +---------------------------- + +PIL provides some special mechanisms to allow you to load a wide variety of +formats into a mode ``F`` (floating point) image memory. + +You can use the ``raw`` decoder to read images where data is packed in any +standard machine data type, using one of the following raw modes: + +============ ======================================= +mode description +============ ======================================= +``F`` 32-bit native floating point. +``F;8`` 8-bit unsigned integer. +``F;8S`` 8-bit signed integer. +``F;16`` 16-bit little endian unsigned integer. +``F;16S`` 16-bit little endian signed integer. +``F;16B`` 16-bit big endian unsigned integer. +``F;16BS`` 16-bit big endian signed integer. +``F;16N`` 16-bit native unsigned integer. +``F;16NS`` 16-bit native signed integer. +``F;32`` 32-bit little endian unsigned integer. +``F;32S`` 32-bit little endian signed integer. +``F;32B`` 32-bit big endian unsigned integer. +``F;32BS`` 32-bit big endian signed integer. +``F;32N`` 32-bit native unsigned integer. +``F;32NS`` 32-bit native signed integer. +``F;32F`` 32-bit little endian floating point. +``F;32BF`` 32-bit big endian floating point. +``F;32NF`` 32-bit native floating point. +``F;64F`` 64-bit little endian floating point. +``F;64BF`` 64-bit big endian floating point. +``F;64NF`` 64-bit native floating point. +============ ======================================= + +The bit decoder +--------------- + +If the raw decoder cannot handle your format, PIL also provides a special “bit” +decoder that can be used to read various packed formats into a floating point +image memory. + +To use the bit decoder with the fromstring function, use the following syntax:: + + image = fromstring( + mode, size, data, "bit", + bits, pad, fill, sign, orientation + ) + +When used in a tile descriptor, the parameter field should look like:: + + (bits, pad, fill, sign, orientation) + +The fields are used as follows: + +**bits** + Number of bits per pixel (2-32). No default. + +**pad** + Padding between lines, in bits. This is either 0 if there is no padding, or + 8 if lines are padded to full bytes. If omitted, the pad value defaults to + 8. + +**fill** + Controls how data are added to, and stored from, the decoder bit buffer. + +**fill=0** + Add bytes to the LSB end of the decoder buffer; store pixels from the MSB + end. + +**fill=1** + Add bytes to the MSB end of the decoder buffer; store pixels from the MSB + end. + +**fill=2** + Add bytes to the LSB end of the decoder buffer; store pixels from the LSB + end. + +**fill=3** + Add bytes to the MSB end of the decoder buffer; store pixels from the LSB + end. + + If omitted, the fill order defaults to 0. + +**sign** + If non-zero, bit fields are sign extended. If zero or omitted, bit fields + are unsigned. + +**orientation** + Whether the first line in the image is the top line on the screen (1), or + the bottom line (-1). If omitted, the orientation defaults to 1. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..52a054e22 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,67 @@ +Pillow: a modern fork of PIL +============================ + +Pillow is the "friendly" PIL fork by Alex Clark and Contributors. PIL is the +Python Imaging Library by Fredrik Lundh and Contributors. + +.. image:: https://travis-ci.org/python-imaging/Pillow.png + :target: https://travis-ci.org/python-imaging/Pillow + +.. image:: https://pypip.in/v/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Latest PyPI version + +.. image:: https://pypip.in/d/Pillow/badge.png + :target: https://pypi.python.org/pypi/Pillow/ + :alt: Number of PyPI downloads + +To start using Pillow, read the :doc:`installation +instructions `. + +If you can't find the information you need, try the old `PIL Handbook`_, but be +aware that it was last updated for PIL 1.1.5. You can download archives and old +versions from `PyPI `_. You can get the +source and contribute at https://github.com/python-imaging/Pillow. + +.. _PIL Handbook: http://effbot.org/imagingbook/pil-index.htm + +.. toctree:: + :maxdepth: 2 + + installation + about + guides + reference/index.rst + handbook/appendices + original-readme + +Support Pillow! +=============== + +PIL needs you! Please help us maintain the Python Imaging Library here: + +- `GitHub `_ +- `Freenode `_ +- `Image-SIG `_ + +Financial +--------- + +Pillow is a volunteer effort led by Alex Clark. If you can't help with +development please consider helping us financially. Your assistance would +be very much appreciated! + +.. note:: Contributors please add your name and donation preference here. + +======================================= ======================================= +**Developer** **Preference** +======================================= ======================================= +Alex Clark (fork author) http://gittip.com/aclark4life +======================================= ======================================= + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 000000000..6c6fe3196 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,213 @@ +Installation +============ + +.. warning:: Pillow >= 2.1.0 no longer supports "import _imaging". Please use "from PIL.Image import core as _imaging" instead. + +.. warning:: Pillow >= 1.0 no longer supports "import Image". Please use "from PIL import Image" instead. + +.. warning:: PIL and Pillow currently cannot co-exist in the same environment. + If you want to use Pillow, please remove PIL first. + +.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4 + +.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. + +Simple installation +------------------- + +.. note:: + + The following instructions will install Pillow with support for most formats. + See :ref:`external-libraries` for the features you would gain by installing + the external libraries first. This page probably also include specific + instructions for your platform. + +You can install Pillow with :command:`pip`:: + + $ pip install Pillow + +Or :command:`easy_install` (for installing `Python Eggs +`_, as :command:`pip` does +not support them):: + + $ easy_install Pillow + +Or download the `compressed archive from PyPI`_, extract it, and inside it +run:: + + $ python setup.py install + +.. _compressed archive from PyPI: https://pypi.python.org/pypi/Pillow + +.. _external-libraries: + +External libraries +------------------ + +Many of Pillow's features require external libraries: + +* **libjpeg** provides JPEG functionality. + + * Pillow has been tested with libjpeg versions **6b**, **8**, and **9** + +* **zlib** provides access to compressed PNGs + +* **libtiff** provides group4 tiff functionality + + * Pillow has been tested with libtiff versions **3.x** and **4.0** + +* **libfreetype** provides type related services + +* **littlecms** provides color management + + * Pillow version 2.2.1 and below uses liblcms1, Pillow 2.3.0 and + above uses liblcms2. Tested with **1.19** and **2.2**. + +* **libwebp** provides the Webp format. + + * Pillow has been tested with version **0.1.3**, which does not read + transparent webp files. Version **0.3.0** supports transparency. + +* **tcl/tk** provides support for tkinter bitmap and photo images. + +If the prerequisites are installed in the standard library locations for your +machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration +should be required. If they are installed in a non-standard location, you may +need to configure setuptools to use those locations (i.e. by editing +:file:`setup.py` and/or :file:`setup.cfg`). Once you have installed the +prerequisites, run:: + + $ pip install Pillow + +Linux installation +------------------ + +.. note:: + + Fedora, Debian/Ubuntu, and ArchLinux include Pillow (instead of PIL) with + their distributions. Consider using those instead of installing manually. + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for Linux.** If you didn't build Python from +source, make sure you have Python's development libraries installed. In Debian +or Ubuntu:: + + $ sudo apt-get install python-dev python-setuptools + +Or for Python 3:: + + $ sudo apt-get install python3-dev python3-setuptools + +In Fedora, the command is:: + + $ sudo yum install python-devel + +Prerequisites are installed on **Ubuntu 10.04 LTS** with:: + + $ sudo apt-get install libtiff4-dev libjpeg62-dev zlib1g-dev \ + libfreetype6-dev tcl8.5-dev tk8.5-dev + +Prerequisites are installed with on **Ubuntu 12.04 LTS** or **Raspian Wheezy +7.0** with:: + + $ sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \ + libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev + +Prerequisites are installed on **Fedora 20** with:: + + $ sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \ + lcms2-devel libwebp-devel tcl-devel tk-devel + + +Mac OS X installation +--------------------- + +.. note:: + + You *do not* need to install all of the external libraries to get Pillow's + basics to work. + +**We do not provide binaries for OS X**, so you'll need XCode to install +Pillow. (XCode 4.2 on 10.6 will work with the Official Python binary +distribution. Otherwise, use whatever XCode you used to compile Python.) + +The easiest way to install the prerequisites is via `Homebrew +`_. After you install Homebrew, run:: + + $ brew install libtiff libjpeg webp littlecms + +If you've built your own Python, then you should be able to install Pillow +using:: + + $ pip install Pillow + +Windows installation +-------------------- + +We provide binaries for Windows in the form of Python Eggs and `Python Wheels +`_: + +Python Eggs +^^^^^^^^^^^ + +.. note:: + + :command:`pip` does not support Python Eggs; use :command:`easy_install` + instead. + +:: + + $ easy_install Pillow + +Python Wheels +^^^^^^^^^^^^^ + +.. Note:: Experimental. Requires setuptools >=0.8 and pip >=1.4.1 + +:: + + $ pip install --use-wheel Pillow + +Platform support +---------------- + +Current platform support for Pillow. Binary distributions are contributed for +each release on a volunteer basis, but the source should compile and run +everywhere platform support is listed. In general, we aim to support all +current versions of Linux, OS X, and Windows. + +.. note:: + + Contributors please test on your platform, edit this document, and send a + pull request. + ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +|**Operating system** |**Supported**|**Tested Python versions** |**Tested Pillow versions** |**Tested processors** | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| CentOS 6.3 |Yes | 2.7,3.3 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.8 Mountain Lion |Yes | 2.6,2.7,3.2,3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Mac OS X 10.7 Lion |Yes | 2.6,2.7,3.2,3.3 | 2.2.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Redhat Linux 6 |Yes | 2.6 | |x86 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 10.04 LTS |Yes | 2.6 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Ubuntu Linux 12.04 LTS |Yes | 2.6,2.7,3.2,3.3,PyPy2.1 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Raspian Wheezy |Yes | 2.7,3.2 | 2.2.0 |arm | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Gentoo Linux |Yes | 2.7,3.2 | 2.1.0 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows 7 Pro |Yes | 2.7,3.2,3.3 | 2.2.1 |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows Server 2008 R2 Enterprise|Yes | 3.3 | |x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ +| Windows 8 Pro |Yes | 2.6,2.7,3.2,3.3,3.4a3 | 2.2.0 |x86,x86-64 | ++----------------------------------+-------------+------------------------------+------------------------------+-----------------------+ + diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 000000000..c943319ad --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,190 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PillowPILfork.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PillowPILfork.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/docs/original-readme.rst b/docs/original-readme.rst new file mode 100644 index 000000000..73b941f37 --- /dev/null +++ b/docs/original-readme.rst @@ -0,0 +1,306 @@ +Original PIL README +=================== + +What follows is the original PIL 1.1.7 README file contents. + +:: + + The Python Imaging Library + $Id$ + + Release 1.1.7 (November 15, 2009) + + ==================================================================== + The Python Imaging Library 1.1.7 + ==================================================================== + + Contents + -------- + + + Introduction + + Support Options + - Commercial support + - Free support + + Software License + + Build instructions (all platforms) + - Additional notes for Mac OS X + - Additional notes for Windows + + -------------------------------------------------------------------- + Introduction + -------------------------------------------------------------------- + + The Python Imaging Library (PIL) adds image processing capabilities + to your Python environment. This library provides extensive file + format support, an efficient internal representation, and powerful + image processing capabilities. + + This source kit has been built and tested with Python 2.0 and newer, + on Windows, Mac OS X, and major Unix platforms. Large parts of the + library also work on 1.5.2 and 1.6. + + The main distribution site for this software is: + + http://www.pythonware.com/products/pil/ + + That site also contains information about free and commercial support + options, PIL add-ons, answers to frequently asked questions, and more. + + + Development versions (alphas, betas) are available here: + + http://effbot.org/downloads/ + + + The PIL handbook is not included in this distribution; to get the + latest version, check: + + http://www.pythonware.com/library/ + http://effbot.org/books/imagingbook/ (drafts) + + + For installation and licensing details, see below. + + + -------------------------------------------------------------------- + Support Options + -------------------------------------------------------------------- + + + Commercial Support + + Secret Labs (PythonWare) offers support contracts for companies using + the Python Imaging Library in commercial applications, and in mission- + critical environments. The support contract includes technical support, + bug fixes, extensions to the PIL library, sample applications, and more. + + For the full story, check: + + http://www.pythonware.com/products/pil/support.htm + + + + Free Support + + For support and general questions on the Python Imaging Library, send + e-mail to the Image SIG mailing list: + + image-sig@python.org + + You can join the Image SIG by sending a mail to: + + image-sig-request@python.org + + Put "subscribe" in the message body to automatically subscribe to the + list, or "help" to get additional information. Alternatively, you can + send your questions to the Python mailing list, python-list@python.org, + or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT + QUESTIONS TO PYTHONWARE ADDRESSES. + + + -------------------------------------------------------------------- + Software License + -------------------------------------------------------------------- + + The Python Imaging Library is + + Copyright (c) 1997-2009 by Secret Labs AB + Copyright (c) 1995-2009 by Fredrik Lundh + + By obtaining, using, and/or copying this software and/or its + associated documentation, you agree that you have read, understood, + and will comply with the following terms and conditions: + + Permission to use, copy, modify, and distribute this software and its + associated documentation for any purpose and without fee is hereby + granted, provided that the above copyright notice appears in all + copies, and that both that copyright notice and this permission notice + appear in supporting documentation, and that the name of Secret Labs + AB or the author not be used in advertising or publicity pertaining to + distribution of the software without specific, written prior + permission. + + SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR + ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + + -------------------------------------------------------------------- + Build instructions (all platforms) + -------------------------------------------------------------------- + + For a list of changes in this release, see the CHANGES document. + + 0. If you're in a hurry, try this: + + $ tar xvfz Imaging-1.1.7.tar.gz + $ cd Imaging-1.1.7 + $ python setup.py install + + If you prefer to know what you're doing, read on. + + + 1. Prerequisites. + + If you need any of the features described below, make sure you + have the necessary libraries before building PIL. + + feature library + ----------------------------------------------------------------- + JPEG support libjpeg (6a or 6b) + + http://www.ijg.org + http://www.ijg.org/files/jpegsrc.v6b.tar.gz + ftp://ftp.uu.net/graphics/jpeg/ + + PNG support zlib (1.2.3 or later is recommended) + + http://www.gzip.org/zlib/ + + OpenType/TrueType freetype2 (2.3.9 or later is recommended) + support + http://www.freetype.org + http://freetype.sourceforge.net + + CMS support littleCMS (1.1.5 or later is recommended) + support + http://www.littlecms.com/ + + If you have a recent Linux version, the libraries provided with the + operating system usually work just fine. If some library is + missing, installing a prebuilt version (jpeg-devel, zlib-devel, + etc) is usually easier than building from source. For example, for + Ubuntu 9.10 (karmic), you can install the following libraries: + + sudo apt-get install libjpeg62-dev + sudo apt-get install zlib1g-dev + sudo apt-get install libfreetype6-dev + sudo apt-get install liblcms1-dev + + If you're using Mac OS X, you can use the 'fink' tool to install + missing libraries (also see the Mac OS X section below). + + Similar tools are available for many other platforms. + + + 2. To build under Python 1.5.2, you need to install the stand-alone + version of the distutils library: + + http://www.python.org/sigs/distutils-sig/download.html + + You can fetch distutils 1.0.2 from the Python source repository: + + svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ + + For newer releases, the distutils library is included in the + Python standard library. + + NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some + more recent additions to the library may not work, but the core + functionality is available. + + + 3. If you didn't build Python from sources, make sure you have + Python's build support files on your machine. If you've down- + loaded a prebuilt package (e.g. a Linux RPM), you probably + need additional developer packages. Look for packages named + "python-dev", "python-devel", or similar. For example, for + Ubuntu 9.10 (karmic), use the following command: + + sudo apt-get install python-dev + + + 4. When you have everything you need, unpack the PIL distribution + (the file Imaging-1.1.7.tar.gz) in a suitable work directory: + + $ cd MyExtensions # example + $ gunzip Imaging-1.1.7.tar.gz + $ tar xvf Imaging-1.1.7.tar + + + 5. Build the library. We recommend that you do an in-place build, + and run the self test before installing. + + $ cd Imaging-1.1.7 + $ python setup.py build_ext -i + $ python selftest.py + + During the build process, the setup.py will display a summary + report that lists what external components it found. The self- + test will display a similar report, with what external components + the tests found in the actual build files: + + ---------------------------------------------------------------- + PIL 1.1.7 SETUP SUMMARY + ---------------------------------------------------------------- + *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) + --- JPEG support available + --- ZLIB (PNG/ZIP) support available + --- FREETYPE support available + ---------------------------------------------------------------- + + Make sure that the optional components you need are included. + + If the build script won't find a given component, you can edit the + setup.py file and set the appropriate ROOT variable. For details, + see instructions in the file. + + If the build script finds the component, but the tests cannot + identify it, try rebuilding *all* modules: + + $ python setup.py clean + $ python setup.py build_ext -i + + + 6. If the setup.py and selftest.py commands finish without any + errors, you're ready to install the library: + + $ python setup.py install + + (depending on how Python has been installed on your machine, + you might have to log in as a superuser to run the 'install' + command, or use the 'sudo' command to run 'install'.) + + + -------------------------------------------------------------------- + Additional notes for Mac OS X + -------------------------------------------------------------------- + + On Mac OS X you will usually install additional software such as + libjpeg or freetype with the "fink" tool, and then it ends up in + "/sw". If you have installed the libraries elsewhere, you may have + to tweak the "setup.py" file before building. + + + -------------------------------------------------------------------- + Additional notes for Windows + -------------------------------------------------------------------- + + On Windows, you need to tweak the ROOT settings in the "setup.py" + file, to make it find the external libraries. See comments in the + file for details. + + Make sure to build PIL and the external libraries with the same + runtime linking options as was used for the Python interpreter + (usually /MD, under Visual Studio). + + + Note that most Python distributions for Windows include libraries + compiled for Microsoft Visual Studio. You can get the free Express + edition of Visual Studio from: + + http://www.microsoft.com/Express/ + + To build extensions using other tool chains, see the "Using + non-Microsoft compilers on Windows" section in the distutils handbook: + + http://www.python.org/doc/current/inst/non-ms-compilers.html + + For additional information on how to build extensions using the + popular MinGW compiler, see: + + http://mingw.org (compiler) + http://sebsauvage.net/python/mingw.html (build instructions) + http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) diff --git a/docs/plugins.rst b/docs/plugins.rst new file mode 100644 index 000000000..b92b500c1 --- /dev/null +++ b/docs/plugins.rst @@ -0,0 +1,330 @@ +Plugin reference +================ + +:mod:`ArgImagePlugin` Module +---------------------------- + +.. automodule:: PIL.ArgImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`BmpImagePlugin` Module +---------------------------- + +.. automodule:: PIL.BmpImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`BufrStubImagePlugin` Module +--------------------------------- + +.. automodule:: PIL.BufrStubImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`CurImagePlugin` Module +---------------------------- + +.. automodule:: PIL.CurImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`DcxImagePlugin` Module +---------------------------- + +.. automodule:: PIL.DcxImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`EpsImagePlugin` Module +---------------------------- + +.. automodule:: PIL.EpsImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`FitsStubImagePlugin` Module +--------------------------------- + +.. automodule:: PIL.FitsStubImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`FliImagePlugin` Module +---------------------------- + +.. automodule:: PIL.FliImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`FpxImagePlugin` Module +---------------------------- + +.. automodule:: PIL.FpxImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`GbrImagePlugin` Module +---------------------------- + +.. automodule:: PIL.GbrImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`GifImagePlugin` Module +---------------------------- + +.. automodule:: PIL.GifImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`GribStubImagePlugin` Module +--------------------------------- + +.. automodule:: PIL.GribStubImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`Hdf5StubImagePlugin` Module +--------------------------------- + +.. automodule:: PIL.Hdf5StubImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`IcnsImagePlugin` Module +----------------------------- + +.. automodule:: PIL.IcnsImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`IcoImagePlugin` Module +---------------------------- + +.. automodule:: PIL.IcoImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`ImImagePlugin` Module +--------------------------- + +.. automodule:: PIL.ImImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`ImtImagePlugin` Module +---------------------------- + +.. automodule:: PIL.ImtImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`IptcImagePlugin` Module +----------------------------- + +.. automodule:: PIL.IptcImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`JpegImagePlugin` Module +----------------------------- + +.. automodule:: PIL.JpegImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`McIdasImagePlugin` Module +------------------------------- + +.. automodule:: PIL.McIdasImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`MicImagePlugin` Module +---------------------------- + +.. automodule:: PIL.MicImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`MpegImagePlugin` Module +----------------------------- + +.. automodule:: PIL.MpegImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`MspImagePlugin` Module +---------------------------- + +.. automodule:: PIL.MspImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PalmImagePlugin` Module +----------------------------- + +.. automodule:: PIL.PalmImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PcdImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PcdImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PcxImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PcxImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PdfImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PdfImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PixarImagePlugin` Module +------------------------------ + +.. automodule:: PIL.PixarImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PngImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PngImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PpmImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PpmImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`PsdImagePlugin` Module +---------------------------- + +.. automodule:: PIL.PsdImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`SgiImagePlugin` Module +---------------------------- + +.. automodule:: PIL.SgiImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`SpiderImagePlugin` Module +------------------------------- + +.. automodule:: PIL.SpiderImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`SunImagePlugin` Module +---------------------------- + +.. automodule:: PIL.SunImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`TgaImagePlugin` Module +---------------------------- + +.. automodule:: PIL.TgaImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`TiffImagePlugin` Module +----------------------------- + +.. automodule:: PIL.TiffImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`WebPImagePlugin` Module +----------------------------- + +.. automodule:: PIL.WebPImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`WmfImagePlugin` Module +---------------------------- + +.. automodule:: PIL.WmfImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`XVThumbImagePlugin` Module +-------------------------------- + +.. automodule:: PIL.XVThumbImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`XbmImagePlugin` Module +---------------------------- + +.. automodule:: PIL.XbmImagePlugin + :members: + :undoc-members: + :show-inheritance: + +:mod:`XpmImagePlugin` Module +---------------------------- + +.. automodule:: PIL.XpmImagePlugin + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/porting-pil-to-pillow.rst b/docs/porting-pil-to-pillow.rst new file mode 100644 index 000000000..88a6768e9 --- /dev/null +++ b/docs/porting-pil-to-pillow.rst @@ -0,0 +1,23 @@ +Porting existing PIL-based code to Pillow +========================================= + +Pillow is a functional drop-in replacement for the Python Imaging Library. To +run your existing PIL-compatible code with Pillow, it needs to be modified to +import the ``Image`` module from the ``PIL`` namespace *instead* of the +global namespace. Change this:: + + import Image + +to this:: + + from PIL import Image + +The :py:mod:`_imaging` module has been moved. You can now import it like this:: + + from PIL.Image import core as _imaging + +The image plugin loading mechanism has changed. Pillow no longer +automatically imports any file in the Python path with a name ending +in :file:`ImagePlugin.py`. You will need to import your image plugin +manually. + diff --git a/docs/reference/Image.rst b/docs/reference/Image.rst new file mode 100644 index 000000000..fe13c882b --- /dev/null +++ b/docs/reference/Image.rst @@ -0,0 +1,189 @@ +.. py:module:: PIL.Image +.. py:currentmodule:: PIL.Image + +:py:mod:`Image` Module +====================== + +The :py:mod:`~PIL.Image` module provides a class with the same name which is +used to represent a PIL image. The module also provides a number of factory +functions, including functions to load images from files, and to create new +images. + +Examples +-------- + +The following script loads an image, rotates it 45 degrees, and displays it +using an external viewer (usually xv on Unix, and the paint program on +Windows). + +Open, rotate, and display an image (using the default viewer) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + im = Image.open("bride.jpg") + im.rotate(45).show() + +The following script creates nice 128x128 thumbnails of all JPEG images in the +current directory. + +Create thumbnails +^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + from PIL import Image + import glob, os + + size = 128, 128 + + for infile in glob.glob("*.jpg"): + file, ext = os.path.splitext(infile) + im = Image.open(infile) + im.thumbnail(size, Image.ANTIALIAS) + im.save(file + ".thumbnail", "JPEG") + +Functions +--------- + +.. autofunction:: open + +Image processing +^^^^^^^^^^^^^^^^ + +.. autofunction:: alpha_composite +.. autofunction:: blend +.. autofunction:: composite +.. autofunction:: eval +.. autofunction:: merge + +Constructing images +^^^^^^^^^^^^^^^^^^^ + +.. autofunction:: new +.. autofunction:: fromarray +.. autofunction:: frombytes +.. autofunction:: fromstring +.. autofunction:: frombuffer + +Registering plugins +^^^^^^^^^^^^^^^^^^^ + +.. note:: + + These functions are for use by plugin authors. Application authors can + ignore them. + +.. autofunction:: register_open +.. autofunction:: register_mime +.. autofunction:: register_save +.. autofunction:: register_extension + +The Image Class +--------------- + +.. autoclass:: PIL.Image.Image + +An instance of the :py:class:`~PIL.Image.Image` class has the following +methods. Unless otherwise stated, all methods return a new instance of the +:py:class:`~PIL.Image.Image` class, holding the resulting image. + +.. automethod:: PIL.Image.Image.convert + +The following example converts an RGB image (linearly calibrated according to +ITU-R 709, using the D65 luminant) to the CIE XYZ color space: + +.. code-block:: python + + rgb2xyz = ( + 0.412453, 0.357580, 0.180423, 0, + 0.212671, 0.715160, 0.072169, 0, + 0.019334, 0.119193, 0.950227, 0 ) + out = im.convert("RGB", rgb2xyz) + +.. automethod:: PIL.Image.Image.copy +.. automethod:: PIL.Image.Image.crop +.. automethod:: PIL.Image.Image.draft +.. automethod:: PIL.Image.Image.filter +.. automethod:: PIL.Image.Image.getbands +.. automethod:: PIL.Image.Image.getbbox +.. automethod:: PIL.Image.Image.getcolors +.. automethod:: PIL.Image.Image.getdata +.. automethod:: PIL.Image.Image.getextrema +.. automethod:: PIL.Image.Image.getpixel +.. automethod:: PIL.Image.Image.histogram +.. automethod:: PIL.Image.Image.offset +.. automethod:: PIL.Image.Image.paste +.. automethod:: PIL.Image.Image.point +.. automethod:: PIL.Image.Image.putalpha +.. automethod:: PIL.Image.Image.putdata +.. automethod:: PIL.Image.Image.putpalette +.. automethod:: PIL.Image.Image.putpixel +.. automethod:: PIL.Image.Image.quantize +.. automethod:: PIL.Image.Image.resize +.. automethod:: PIL.Image.Image.rotate +.. automethod:: PIL.Image.Image.save +.. automethod:: PIL.Image.Image.seek +.. automethod:: PIL.Image.Image.show +.. automethod:: PIL.Image.Image.split +.. automethod:: PIL.Image.Image.tell +.. automethod:: PIL.Image.Image.thumbnail +.. automethod:: PIL.Image.Image.tobitmap +.. automethod:: PIL.Image.Image.tostring +.. automethod:: PIL.Image.Image.transform +.. automethod:: PIL.Image.Image.transpose +.. automethod:: PIL.Image.Image.verify + +.. automethod:: PIL.Image.Image.fromstring +.. deprecated:: 2.0 + +.. automethod:: PIL.Image.Image.load + +Attributes +---------- + +Instances of the :py:class:`Image` class have the following attributes: + +.. py:attribute:: format + + The file format of the source file. For images created by the library + itself (via a factory function, or by running a method on an existing + image), this attribute is set to ``None``. + + :type: :py:class:`string` or ``None`` + +.. py:attribute:: mode + + Image mode. This is a string specifying the pixel format used by the image. + Typical values are “1”, “L”, “RGB”, or “CMYK.” See + :doc:`../handbook/concepts` for a full list. + + :type: :py:class:`string` + +.. py:attribute:: size + + Image size, in pixels. The size is given as a 2-tuple (width, height). + + :type: ``(width, height)`` + +.. py:attribute:: palette + + Colour palette table, if any. If mode is “P”, this should be an instance of + the :py:class:`~PIL.ImagePalette.ImagePalette` class. Otherwise, it should + be set to ``None``. + + :type: :py:class:`~PIL.ImagePalette.ImagePalette` or ``None`` + +.. py:attribute:: info + + A dictionary holding data associated with the image. This dictionary is + used by file handlers to pass on various non-image information read from + the file. See documentation for the various file handlers for details. + + Most methods ignore the dictionary when returning new images; since the + keys are not standardized, it’s not possible for a method to know if the + operation affects the dictionary. If you need the information later on, + keep a reference to the info dictionary returned from the open method. + + :type: :py:class:`dict` diff --git a/docs/reference/ImageChops.rst b/docs/reference/ImageChops.rst new file mode 100644 index 000000000..8d08315b0 --- /dev/null +++ b/docs/reference/ImageChops.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageChops +.. py:currentmodule:: PIL.ImageChops + +:py:mod:`ImageChops` ("Channel Operations") Module +================================================== + +The :py:mod:`ImageChops` module contains a number of arithmetical image +operations, called channel operations (“chops”). These can be used for various +purposes, including special effects, image compositions, algorithmic painting, +and more. + +For more pre-made operations, see :py:mod:`ImageOps`. + +At this time, most channel operations are only implemented for 8-bit images +(e.g. “L” and “RGB”). + +Functions +--------- + +Most channel operations take one or two image arguments and returns a new +image. Unless otherwise noted, the result of a channel operation is always +clipped to the range 0 to MAX (which is 255 for all modes supported by the +operations in this module). + +.. autofunction:: PIL.ImageChops.add +.. autofunction:: PIL.ImageChops.add_modulo +.. autofunction:: PIL.ImageChops.blend +.. autofunction:: PIL.ImageChops.composite +.. autofunction:: PIL.ImageChops.constant +.. autofunction:: PIL.ImageChops.darker +.. autofunction:: PIL.ImageChops.difference +.. autofunction:: PIL.ImageChops.duplicate +.. autofunction:: PIL.ImageChops.invert +.. autofunction:: PIL.ImageChops.lighter +.. autofunction:: PIL.ImageChops.logical_and +.. autofunction:: PIL.ImageChops.logical_or +.. autofunction:: PIL.ImageChops.multiply +.. autofunction:: PIL.ImageChops.offset +.. autofunction:: PIL.ImageChops.screen +.. autofunction:: PIL.ImageChops.subtract +.. autofunction:: PIL.ImageChops.subtract_modulo diff --git a/docs/reference/ImageColor.rst b/docs/reference/ImageColor.rst new file mode 100644 index 000000000..da9b406e1 --- /dev/null +++ b/docs/reference/ImageColor.rst @@ -0,0 +1,43 @@ +.. py:module:: PIL.ImageColor +.. py:currentmodule:: PIL.ImageColor + +:py:mod:`ImageColor` Module +=========================== + +The :py:mod:`ImageColor` module contains color tables and converters from +CSS3-style color specifiers to RGB tuples. This module is used by +:py:meth:`PIL.Image.Image.new` and the :py:mod:`~PIL.ImageDraw` module, among +others. + +.. _color-names: + +Color Names +----------- + +The ImageColor module supports the following string formats: + +* Hexadecimal color specifiers, given as ``#rgb`` or ``#rrggbb``. For example, + ``#ff0000`` specifies pure red. + +* RGB functions, given as ``rgb(red, green, blue)`` where the color values are + integers in the range 0 to 255. Alternatively, the color values can be given + as three percentages (0% to 100%). For example, ``rgb(255,0,0)`` and + ``rgb(100%,0%,0%)`` both specify pure red. + +* Hue-Saturation-Lightness (HSL) functions, given as ``hsl(hue, saturation%, + lightness%)`` where hue is the color given as an angle between 0 and 360 + (red=0, green=120, blue=240), saturation is a value between 0% and 100% + (gray=0%, full color=100%), and lightness is a value between 0% and 100% + (black=0%, normal=50%, white=100%). For example, ``hsl(0,100%,50%)`` is pure + red. + +* Common HTML color names. The :py:mod:`~PIL.ImageColor` module provides some + 140 standard color names, based on the colors supported by the X Window + system and most web browsers. color names are case insensitive. For example, + ``red`` and ``Red`` both specify pure red. + +Functions +--------- + +.. autofunction:: getrgb +.. autofunction:: getcolor diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst new file mode 100644 index 000000000..68855eb5b --- /dev/null +++ b/docs/reference/ImageDraw.rst @@ -0,0 +1,239 @@ +.. py:module:: PIL.ImageDraw +.. py:currentmodule:: PIL.ImageDraw + +:py:mod:`ImageDraw` Module +========================== + +The :py:mod:`ImageDraw` module provide simple 2D graphics for +:py:class:`~PIL.Image.Image` objects. You can use this module to create new +images, annotate or retouch existing images, and to generate graphics on the +fly for web use. + +For a more advanced drawing library for PIL, see the `aggdraw module`_. + +.. _aggdraw module: http://effbot.org/zone/aggdraw-index.htm + +Example: Draw a gray cross over an image +---------------------------------------- + +.. code-block:: python + + from PIL import Image, ImageDraw + + im = Image.open("lena.pgm") + + draw = ImageDraw.Draw(im) + draw.line((0, 0) + im.size, fill=128) + draw.line((0, im.size[1], im.size[0], 0), fill=128) + del draw + + # write to stdout + im.save(sys.stdout, "PNG") + + +Concepts +-------- + +Coordinates +^^^^^^^^^^^ + +The graphics interface uses the same coordinate system as PIL itself, with (0, +0) in the upper left corner. + +Colors +^^^^^^ + +To specify colors, you can use numbers or tuples just as you would use with +:py:meth:`PIL.Image.Image.new` or :py:meth:`PIL.Image.Image.putpixel`. For “1”, +“L”, and “I” images, use integers. For “RGB” images, use a 3-tuple containing +integer values. For “F” images, use integer or floating point values. + +For palette images (mode “P”), use integers as color indexes. In 1.1.4 and +later, you can also use RGB 3-tuples or color names (see below). The drawing +layer will automatically assign color indexes, as long as you don’t draw with +more than 256 colors. + +Color Names +^^^^^^^^^^^ + +See :ref:`color-names` for the color names supported by Pillow. + +Fonts +^^^^^ + +PIL can use bitmap fonts or OpenType/TrueType fonts. + +Bitmap fonts are stored in PIL’s own format, where each font typically consists +of a two files, one named .pil and the other usually named .pbm. The former +contains font metrics, the latter raster data. + +To load a bitmap font, use the load functions in the :py:mod:`~PIL.ImageFont` +module. + +To load a OpenType/TrueType font, use the truetype function in the +:py:mod:`~PIL.ImageFont` module. Note that this function depends on third-party +libraries, and may not available in all PIL builds. + +Functions +--------- + +.. py:class:: PIL.ImageDraw.Draw(im, mode=None) + + Creates an object that can be used to draw in the given image. + + Note that the image will be modified in place. + +Methods +------- + +.. py:method:: PIL.ImageDraw.Draw.arc(xy, start, end, fill=None) + + Draws an arc (a portion of a circle outline) between the start and end + angles, inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + +.. py:method:: PIL.ImageDraw.Draw.bitmap(xy, bitmap, fill=None) + + Draws a bitmap (mask) at the given position, using the current fill color + for the non-zero portions. The bitmap should be a valid transparency mask + (mode “1”) or matte (mode “L” or “RGBA”). + + This is equivalent to doing ``image.paste(xy, color, bitmap)``. + + To paste pixel data into an image, use the + :py:meth:`~PIL.Image.Image.paste` method on the image itself. + +.. py:method:: PIL.ImageDraw.Draw.chord(xy, start, end, fill=None, outline=None) + + Same as :py:meth:`~PIL.ImageDraw.Draw.arc`, but connects the end points + with a straight line. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.ellipse(xy, fill=None, outline=None) + + Draws an ellipse inside the given bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.line(xy, fill=None, width=0) + + Draws a line between the coordinates in the **xy** list. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the line. + :param width: The line width, in pixels. Note that line + joins are not handled well, so wide polylines will not look good. + + .. versionadded:: 1.1.5 + + .. note:: This option was broken until version 1.1.6. + +.. py:method:: PIL.ImageDraw.Draw.pieslice(xy, start, end, fill=None, outline=None) + + Same as arc, but also draws straight lines between the end points and the + center of the bounding box. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.point(xy, fill=None) + + Draws points (individual pixels) at the given coordinates. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param fill: Color to use for the point. + +.. py:method:: PIL.ImageDraw.Draw.polygon(xy, fill=None, outline=None) + + Draws a polygon. + + The polygon outline consists of straight lines between the given + coordinates, plus a straight line between the last and the first + coordinate. + + :param xy: Sequence of either 2-tuples like ``[(x, y), (x, y), ...]`` or + numeric values like ``[x, y, x, y, ...]``. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.rectangle(xy, fill=None, outline=None) + + Draws a rectangle. + + :param xy: Four points to define the bounding box. Sequence of either + ``[(x0, y0), (x1, y1)]`` or ``[x0, y0, x1, y1]``. The second point + is just outside the drawn rectangle. + :param outline: Color to use for the outline. + :param fill: Color to use for the fill. + +.. py:method:: PIL.ImageDraw.Draw.shape(shape, fill=None, outline=None) + + .. warning:: This method is experimental. + + Draw a shape. + +.. py:method:: PIL.ImageDraw.Draw.text(xy, text, fill=None, font=None, anchor=None) + + Draws the string at the given position. + + :param xy: Top left corner of the text. + :param text: Text to be drawn. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param fill: Color to use for the text. + +.. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) + + Return the size of the given string, in pixels. + + :param text: Text to be measured. + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + +Legacy API +---------- + +The :py:class:`~PIL.ImageDraw.Draw` class contains a constructor and a number +of methods which are provided for backwards compatibility only. For this to +work properly, you should either use options on the drawing primitives, or +these methods. Do not mix the old and new calling conventions. + + +.. py:function:: PIL.ImageDraw.ImageDraw(image) + + :rtype: :py:class:`~PIL.ImageDraw.Draw` + +.. py:method:: PIL.ImageDraw.Draw.setink(ink) + + .. deprecated:: 1.1.5 + + Sets the color to use for subsequent draw and fill operations. + +.. py:method:: PIL.ImageDraw.Draw.setfill(fill) + + .. deprecated:: 1.1.5 + + Sets the fill mode. + + If the mode is 0, subsequently drawn shapes (like polygons and rectangles) + are outlined. If the mode is 1, they are filled. + +.. py:method:: PIL.ImageDraw.Draw.setfont(font) + + .. deprecated:: 1.1.5 + + Sets the default font to use for the text method. + + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. diff --git a/docs/reference/ImageEnhance.rst b/docs/reference/ImageEnhance.rst new file mode 100644 index 000000000..e6eae85f0 --- /dev/null +++ b/docs/reference/ImageEnhance.rst @@ -0,0 +1,38 @@ +.. py:module:: PIL.ImageEnhance +.. py:currentmodule:: PIL.ImageEnhance + +:py:mod:`ImageEnhance` Module +============================= + +The :py:mod:`ImageEnhance` module contains a number of classes that can be used +for image enhancement. + +Example: Vary the sharpness of an image +--------------------------------------- + +.. code-block:: python + + from PIL import ImageEnhance + + enhancer = ImageEnhance.Sharpness(image) + + for i in range(8): + factor = i / 4.0 + enhancer.enhance(factor).show("Sharpness %f" % factor) + +Also see the :file:`enhancer.py` demo program in the :file:`Scripts/` +directory. + +Classes +------- + +All enhancement classes implement a common interface, containing a single +method: + +.. autoclass:: PIL.ImageEnhance._Enhance + :members: + +.. autoclass:: PIL.ImageEnhance.Color +.. autoclass:: PIL.ImageEnhance.Contrast +.. autoclass:: PIL.ImageEnhance.Brightness +.. autoclass:: PIL.ImageEnhance.Sharpness diff --git a/docs/reference/ImageFile.rst b/docs/reference/ImageFile.rst new file mode 100644 index 000000000..9612658e9 --- /dev/null +++ b/docs/reference/ImageFile.rst @@ -0,0 +1,41 @@ +.. py:module:: PIL.ImageFile +.. py:currentmodule:: PIL.ImageFile + +:py:mod:`ImageFile` Module +========================== + +The :py:mod:`ImageFile` module provides support functions for the image open +and save functions. + +In addition, it provides a :py:class:`Parser` class which can be used to decode +an image piece by piece (e.g. while receiving it over a network connection). +This class implements the same consumer interface as the standard **sgmllib** +and **xmllib** modules. + +Example: Parse an image +----------------------- + +.. code-block:: python + + from PIL import ImageFile + + fp = open("lena.pgm", "rb") + + p = ImageFile.Parser() + + while 1: + s = fp.read(1024) + if not s: + break + p.feed(s) + + im = p.close() + + im.save("copy.jpg") + + +:py:class:`~PIL.ImageFile.Parser` +--------------------------------- + +.. autoclass:: PIL.ImageFile.Parser() + :members: diff --git a/docs/reference/ImageFilter.rst b/docs/reference/ImageFilter.rst new file mode 100644 index 000000000..e89fafbcf --- /dev/null +++ b/docs/reference/ImageFilter.rst @@ -0,0 +1,47 @@ +.. py:module:: PIL.ImageFilter +.. py:currentmodule:: PIL.ImageFilter + +:py:mod:`ImageFilter` Module +============================ + +The :py:mod:`ImageFilter` module contains definitions for a pre-defined set of +filters, which can be be used with the :py:meth:`Image.filter() +` method. + +Example: Filter an image +------------------------ + +.. code-block:: python + + from PIL import ImageFilter + + im1 = im.filter(ImageFilter.BLUR) + + im2 = im.filter(ImageFilter.MinFilter(3)) + im3 = im.filter(ImageFilter.MinFilter) # same as MinFilter(3) + +Filters +------- + +The current version of the library provides the following set of predefined +image enhancement filters: + +* **BLUR** +* **CONTOUR** +* **DETAIL** +* **EDGE_ENHANCE** +* **EDGE_ENHANCE_MORE** +* **EMBOSS** +* **FIND_EDGES** +* **SMOOTH** +* **SMOOTH_MORE** +* **SHARPEN** + +.. autoclass:: PIL.ImageFilter.GaussianBlur +.. autoclass:: PIL.ImageFilter.UnsharpMask +.. autoclass:: PIL.ImageFilter.Kernel +.. autoclass:: PIL.ImageFilter.RankFilter +.. autoclass:: PIL.ImageFilter.MedianFilter +.. autoclass:: PIL.ImageFilter.MinFilter +.. autoclass:: PIL.ImageFilter.MaxFilter +.. autoclass:: PIL.ImageFilter.ModeFilter diff --git a/docs/reference/ImageFont.rst b/docs/reference/ImageFont.rst new file mode 100644 index 000000000..166d977a6 --- /dev/null +++ b/docs/reference/ImageFont.rst @@ -0,0 +1,69 @@ +.. py:module:: PIL.ImageFont +.. py:currentmodule:: PIL.ImageFont + +:py:mod:`ImageFont` Module +========================== + +The :py:mod:`ImageFont` module defines a class with the same name. Instances of +this class store bitmap fonts, and are used with the +:py:meth:`PIL.ImageDraw.Draw.text` method. + +PIL uses its own font file format to store bitmap fonts. You can use the +:command`pilfont` utility to convert BDF and PCF font descriptors (X window +font formats) to this format. + +Starting with version 1.1.4, PIL can be configured to support TrueType and +OpenType fonts (as well as other font formats supported by the FreeType +library). For earlier versions, TrueType support is only available as part of +the imToolkit package + +Example +------- + +.. code-block:: python + + from PIL import ImageFont, ImageDraw + + draw = ImageDraw.Draw(image) + + # use a bitmap font + font = ImageFont.load("arial.pil") + + draw.text((10, 10), "hello", font=font) + + # use a truetype font + font = ImageFont.truetype("arial.ttf", 15) + + draw.text((10, 25), "world", font=font) + +Functions +--------- + +.. autofunction:: PIL.ImageFont.load +.. autofunction:: PIL.ImageFont.load_path +.. autofunction:: PIL.ImageFont.truetype +.. autofunction:: PIL.ImageFont.load_default + +Methods +------- + +.. py:method:: PIL.ImageFont.ImageFont.getsize(text) + + :return: (width, height) + +.. py:method:: PIL.ImageFont.ImageFont.getmask(text, mode='') + + Create a bitmap for the text. + + If the font uses antialiasing, the bitmap should have mode “L” and use a + maximum value of 255. Otherwise, it should have mode “1”. + + :param text: Text to render. + :param mode: Used by some graphics drivers to indicate what mode the + driver prefers; if empty, the renderer may return either + mode. Note that the mode is always a string, to simplify + C-level implementations. + + .. versionadded:: 1.1.5 + :return: An internal PIL storage memory instance as defined by the + :py:mod:`PIL.Image.core` interface module. diff --git a/docs/reference/ImageGrab.rst b/docs/reference/ImageGrab.rst new file mode 100644 index 000000000..117be885b --- /dev/null +++ b/docs/reference/ImageGrab.rst @@ -0,0 +1,33 @@ +.. py:module:: PIL.ImageGrab +.. py:currentmodule:: PIL.ImageGrab + +:py:mod:`ImageGrab` Module (Windows-only) +========================================= + +The :py:mod:`ImageGrab` module can be used to copy the contents of the screen +or the clipboard to a PIL image memory. + +.. note:: The current version works on Windows only. + +.. versionadded:: 1.1.3 + +.. py:function:: PIL.ImageGrab.grab(bbox=None) + + Take a snapshot of the screen. The pixels inside the bounding box are + returned as an "RGB" image. If the bounding box is omitted, the entire + screen is copied. + + .. versionadded:: 1.1.3 + + :param bbox: What region to copy. Default is the entire screen. + :return: An image + +.. py:function:: PIL.ImageGrab.grabclipboard() + + Take a snapshot of the clipboard image, if any. + + .. versionadded:: 1.1.4 + + :return: An image, a list of filenames, or None if the clipboard does + not contain image data or filenames. Note that if a list is + returned, the filenames may not represent image files. diff --git a/docs/reference/ImageMath.rst b/docs/reference/ImageMath.rst new file mode 100644 index 000000000..e3f9ed8d6 --- /dev/null +++ b/docs/reference/ImageMath.rst @@ -0,0 +1,127 @@ +.. py:module:: PIL.ImageMath +.. py:currentmodule:: PIL.ImageMath + +:py:mod:`ImageMath` Module +========================== + +The :py:mod:`ImageMath` module can be used to evaluate “image expressions”. The +module provides a single eval function, which takes an expression string and +one or more images. + +Example: Using the :py:mod:`~PIL.ImageMath` module +-------------------------------------------------- + +.. code-block:: python + + import Image, ImageMath + + im1 = Image.open("image1.jpg") + im2 = Image.open("image2.jpg") + + out = ImageMath.eval("convert(min(a, b), 'L')", a=im1, b=im2) + out.save("result.png") + +.. py:function:: eval(expression, environment) + + Evaluate expression in the given environment. + + In the current version, :py:mod:`~PIL.ImageMath` only supports + single-layer images. To process multi-band images, use the + :py:meth:`~PIL.Image.Image.split` method or :py:func:`~PIL.Image.merge` + function. + + :param expression: A string which uses the standard Python expression + syntax. In addition to the standard operators, you can + also use the functions described below. + :param environment: A dictionary that maps image names to Image instances. + You can use one or more keyword arguments instead of a + dictionary, as shown in the above example. Note that + the names must be valid Python identifiers. + :return: An image, an integer value, a floating point value, + or a pixel tuple, depending on the expression. + +Expression syntax +----------------- + +Expressions are standard Python expressions, but they’re evaluated in a +non-standard environment. You can use PIL methods as usual, plus the following +set of operators and functions: + +Standard Operators +^^^^^^^^^^^^^^^^^^ + +You can use standard arithmetical operators for addition (+), subtraction (-), +multiplication (*), and division (/). + +The module also supports unary minus (-), modulo (%), and power (**) operators. + +Note that all operations are done with 32-bit integers or 32-bit floating +point values, as necessary. For example, if you add two 8-bit images, the +result will be a 32-bit integer image. If you add a floating point constant to +an 8-bit image, the result will be a 32-bit floating point image. + +You can force conversion using the :py:func:`~PIL.ImageMath.convert`, +:py:func:`~PIL.ImageMath.float`, and :py:func:`~PIL.ImageMath.int` functions +described below. + +Bitwise Operators +^^^^^^^^^^^^^^^^^ + +The module also provides operations that operate on individual bits. This +includes and (&), or (|), and exclusive or (^). You can also invert (~) all +pixel bits. + +Note that the operands are converted to 32-bit signed integers before the +bitwise operation is applied. This means that you’ll get negative values if +you invert an ordinary greyscale image. You can use the and (&) operator to +mask off unwanted bits. + +Bitwise operators don’t work on floating point images. + +Logical Operators +^^^^^^^^^^^^^^^^^ + +Logical operators like :keyword:`and`, :keyword:`or`, and :keyword:`not` work +on entire images, rather than individual pixels. + +An empty image (all pixels zero) is treated as false. All other images are +treated as true. + +Note that :keyword:`and` and :keyword:`or` return the last evaluated operand, +while not always returns a boolean value. + +Built-in Functions +^^^^^^^^^^^^^^^^^^ + +These functions are applied to each individual pixel. + +.. py:currentmodule:: None + +.. py:function:: abs(image) + + Absolute value. + +.. py:function:: convert(image, mode) + + Convert image to the given mode. The mode must be given as a string + constant. + +.. py:function:: float(image) + + Convert image to 32-bit floating point. This is equivalent to + convert(image, “F”). + +.. py:function:: int(image) + + Convert image to 32-bit integer. This is equivalent to convert(image, “I”). + + Note that 1-bit and 8-bit images are automatically converted to 32-bit + integers if necessary to get a correct result. + +.. py:function:: max(image1, image2) + + Maximum value. + +.. py:function:: min(image1, image2) + + Minimum value. diff --git a/docs/reference/ImageOps.rst b/docs/reference/ImageOps.rst new file mode 100644 index 000000000..50cea90ca --- /dev/null +++ b/docs/reference/ImageOps.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageOps +.. py:currentmodule:: PIL.ImageOps + +:py:mod:`ImageOps` Module +========================== + +The :py:mod:`ImageOps` module contains a number of ‘ready-made’ image +processing operations. This module is somewhat experimental, and most operators +only work on L and RGB images. + +Only bug fixes have been added since the Pillow fork. + +.. versionadded:: 1.1.3 + +.. autofunction:: autocontrast +.. autofunction:: colorize +.. autofunction:: crop +.. autofunction:: deform +.. autofunction:: equalize +.. autofunction:: expand +.. autofunction:: fit +.. autofunction:: flip +.. autofunction:: grayscale +.. autofunction:: invert +.. autofunction:: mirror +.. autofunction:: posterize +.. autofunction:: solarize diff --git a/docs/reference/ImagePalette.rst b/docs/reference/ImagePalette.rst new file mode 100644 index 000000000..15b8aed8f --- /dev/null +++ b/docs/reference/ImagePalette.rst @@ -0,0 +1,21 @@ +.. py:module:: PIL.ImagePalette +.. py:currentmodule:: PIL.ImagePalette + +:py:mod:`ImagePalette` Module +============================= + +The :py:mod:`ImagePalette` module contains a class of the same name to +represent the color palette of palette mapped images. + +.. note:: + + This module was never well-documented. It hasn't changed since 2001, + though, so it's probably safe for you to read the source code and puzzle + out the internals if you need to. + + The :py:class:`~PIL.ImagePalette.ImagePalette` class has several methods, + but they are all marked as "experimental." Read that as you will. The + ``[source]`` link is there for a reason. + +.. autoclass:: PIL.ImagePalette.ImagePalette + :members: diff --git a/docs/reference/ImagePath.rst b/docs/reference/ImagePath.rst new file mode 100644 index 000000000..700464144 --- /dev/null +++ b/docs/reference/ImagePath.rst @@ -0,0 +1,68 @@ +.. py:module:: PIL.ImagePath +.. py:currentmodule:: PIL.ImagePath + +:py:mod:`ImagePath` Module +========================== + +The :py:mod:`ImagePath` module is used to store and manipulate 2-dimensional +vector data. Path objects can be passed to the methods on the +:py:mod:`~PIL.ImageDraw` module. + +.. py:class:: PIL.ImagePath.Path + + A path object. The coordinate list can be any sequence object containing + either 2-tuples [(x, y), …] or numeric values [x, y, …]. + + You can also create a path object from another path object. + + In 1.1.6 and later, you can also pass in any object that implements + Python’s buffer API. The buffer should provide read access, and contain C + floats in machine byte order. + + The path object implements most parts of the Python sequence interface, and + behaves like a list of (x, y) pairs. You can use len(), item access, and + slicing as usual. However, the current version does not support slice + assignment, or item and slice deletion. + + :param xy: A sequence. The sequence can contain 2-tuples [(x, y), ...] + or a flat list of numbers [x, y, ...]. + +.. py:method:: PIL.ImagePath.Path.compact(distance=2) + + Compacts the path, by removing points that are close to each other. This + method modifies the path in place, and returns the number of points left in + the path. + + **distance** is measured as `Manhattan distance`_ and defaults to two + pixels. + +.. _Manhattan distance: http://en.wikipedia.org/wiki/Manhattan_distance + +.. py:method:: PIL.ImagePath.Path.getbbox() + + Gets the bounding box of the path. + + :return: ``(x0, y0, x1, y1)`` + +.. py:method:: PIL.ImagePath.Path.map(function) + + Maps the path through a function. + +.. py:method:: PIL.ImagePath.Path.tolist(flat=0) + + Converts the path to a Python list [(x, y), …]. + + :param flat: By default, this function returns a list of 2-tuples + [(x, y), ...]. If this argument is :keyword:`True`, it + returns a flat list [x, y, ...] instead. + :return: A list of coordinates. See **flat**. + +.. py:method:: PIL.ImagePath.Path.transform(matrix) + + Transforms the path in place, using an affine transform. The matrix is a + 6-tuple (a, b, c, d, e, f), and each point is mapped as follows: + + .. code-block:: python + + xOut = xIn * a + yIn * b + c + yOut = xIn * d + yIn * e + f diff --git a/docs/reference/ImageQt.rst b/docs/reference/ImageQt.rst new file mode 100644 index 000000000..e63fd99fe --- /dev/null +++ b/docs/reference/ImageQt.rst @@ -0,0 +1,20 @@ +.. py:module:: PIL.ImageQt +.. py:currentmodule:: PIL.ImageQt + +:py:mod:`ImageQt` Module +======================== + +The :py:mod:`ImageQt` module contains support for creating PyQt4 or PyQt5 QImage objects +from PIL images. + +.. versionadded:: 1.1.6 + +.. py:class:: ImageQt.ImageQt(image) + + Creates an :py:class:`~PIL.ImageQt.ImageQt` object from a PIL + :py:class:`~PIL.Image.Image` object. This class is a subclass of + QtGui.QImage, which means that you can pass the resulting objects directly + to PyQt4/5 API functions and methods. + + This operation is currently supported for mode 1, L, P, RGB, and RGBA + images. To handle other modes, you need to convert the image first. diff --git a/docs/reference/ImageSequence.rst b/docs/reference/ImageSequence.rst new file mode 100644 index 000000000..85539cb9e --- /dev/null +++ b/docs/reference/ImageSequence.rst @@ -0,0 +1,27 @@ +.. py:module:: PIL.ImageSequence +.. py:currentmodule:: PIL.ImageSequence + +:py:mod:`ImageSequence` Module +============================== + +The :py:mod:`ImageSequence` module contains a wrapper class that lets you +iterate over the frames of an image sequence. + +Extracting frames from an animation +----------------------------------- + +.. code-block:: python + + from PIL import Image, ImageSequence + + im = Image.open("animation.fli") + + index = 1 + for frame in ImageSequence.Iterator(im): + frame.save("frame%d.png" % index) + index = index + 1 + +The :py:class:`~PIL.ImageSequence.Iterator` class +------------------------------------------------- + +.. autoclass:: PIL.ImageSequence.Iterator diff --git a/docs/reference/ImageStat.rst b/docs/reference/ImageStat.rst new file mode 100644 index 000000000..c8dfe3062 --- /dev/null +++ b/docs/reference/ImageStat.rst @@ -0,0 +1,53 @@ +.. py:module:: PIL.ImageStat +.. py:currentmodule:: PIL.ImageStat + +:py:mod:`ImageStat` Module +========================== + +The :py:mod:`ImageStat` module calculates global statistics for an image, or +for a region of an image. + +.. py:class:: PIL.ImageStat.Stat(image_or_list, mask=None) + + Calculate statistics for the given image. If a mask is included, + only the regions covered by that mask are included in the + statistics. You can also pass in a previously calculated histogram. + + :param image: A PIL image, or a precalculated histogram. + :param mask: An optional mask. + + .. py:attribute:: extrema + + Min/max values for each band in the image. + + .. py:attribute:: count + + Total number of pixels. + + .. py:attribute:: sum + + Sum of all pixels. + + .. py:attribute:: sum2 + + Squared sum of all pixels. + + .. py:attribute:: pixel + + Average pixel level. + + .. py:attribute:: median + + Median pixel level. + + .. py:attribute:: rms + + RMS (root-mean-square). + + .. py:attribute:: var + + Variance. + + .. py:attribute:: stddev + + Standard deviation. diff --git a/docs/reference/ImageTk.rst b/docs/reference/ImageTk.rst new file mode 100644 index 000000000..7ee4af029 --- /dev/null +++ b/docs/reference/ImageTk.rst @@ -0,0 +1,16 @@ +.. py:module:: PIL.ImageTk +.. py:currentmodule:: PIL.ImageTk + +:py:mod:`ImageTk` Module +======================== + +The :py:mod:`ImageTk` module contains support to create and modify Tkinter +BitmapImage and PhotoImage objects from PIL images. + +For examples, see the demo programs in the Scripts directory. + +.. autoclass:: PIL.ImageTk.BitmapImage + :members: + +.. autoclass:: PIL.ImageTk.PhotoImage + :members: diff --git a/docs/reference/ImageWin.rst b/docs/reference/ImageWin.rst new file mode 100644 index 000000000..2696e7e99 --- /dev/null +++ b/docs/reference/ImageWin.rst @@ -0,0 +1,29 @@ +.. py:module:: PIL.ImageWin +.. py:currentmodule:: PIL.ImageWin + +:py:mod:`ImageWin` Module (Windows-only) +======================================== + +The :py:mod:`ImageWin` module contains support to create and display images on +Windows. + +ImageWin can be used with PythonWin and other user interface toolkits that +provide access to Windows device contexts or window handles. For example, +Tkinter makes the window handle available via the winfo_id method: + +.. code-block:: python + + from PIL import ImageWin + + dib = ImageWin.Dib(...) + + hwnd = ImageWin.HWND(widget.winfo_id()) + dib.draw(hwnd, xy) + + +.. autoclass:: PIL.ImageWin.Dib + :members: + + +.. autoclass:: PIL.ImageWin.HDC +.. autoclass:: PIL.ImageWin.HWND diff --git a/docs/reference/PSDraw.rst b/docs/reference/PSDraw.rst new file mode 100644 index 000000000..2b5b9b340 --- /dev/null +++ b/docs/reference/PSDraw.rst @@ -0,0 +1,11 @@ +.. py:module:: PIL.PSDraw +.. py:currentmodule:: PIL.PSDraw + +:py:mod:`PSDraw` Module +======================= + +The :py:mod:`PSDraw` module provides simple print support for Postscript +printers. You can print text, graphics and images through this module. + +.. autoclass:: PIL.PSDraw.PSDraw + :members: diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 000000000..2d57e37be --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,26 @@ +Reference +========= + +.. toctree:: + :maxdepth: 2 + + Image + ImageChops + ImageColor + ImageDraw + ImageEnhance + ImageFile + ImageFilter + ImageFont + ImageGrab + ImageMath + ImageOps + ImagePalette + ImagePath + ImageQt + ImageSequence + ImageStat + ImageTk + ImageWin + PSDraw + ../PIL diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..d825a5fcc --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,17 @@ +# requirements for working on docs + +# install pillow from master if you're into that, but RtD needs this +pillow>=2.2.1 + +Jinja2==2.7.1 +MarkupSafe==0.18 +Pygments==1.6 +Sphinx==1.1.3 +docopt==0.6.1 +docutils==0.11 +wsgiref==0.1.2 +sphinx-better-theme==0.1.5 + +# livereload not strictly necessary but really useful (make livehtml) +tornado==3.1.1 +livereload==1.0.1 diff --git a/doctest.py b/doctest.py deleted file mode 100644 index e8140ecdf..000000000 --- a/doctest.py +++ /dev/null @@ -1,1111 +0,0 @@ -# Module doctest version 0.9.6 -# Released to the public domain 16-Jan-2001, -# by Tim Peters (tim.one@home.com). - -# local modifications: -# 2001-02-13 fl: minor tweaks to make it run under both 1.5.2 and 2.0 - -# Provided as-is; use at your own risk; no warranty; no promises; enjoy! - -"""Module doctest -- a framework for running examples in docstrings. - -NORMAL USAGE - -In normal use, end each module M with: - -def _test(): - import doctest, M # replace M with your module's name - return doctest.testmod(M) # ditto - -if __name__ == "__main__": - _test() - -Then running the module as a script will cause the examples in the -docstrings to get executed and verified: - -python M.py - -This won't display anything unless an example fails, in which case the -failing example(s) and the cause(s) of the failure(s) are printed to stdout -(why not stderr? because stderr is a lame hack <0.2 wink>), and the final -line of output is "Test failed.". - -Run it with the -v switch instead: - -python M.py -v - -and a detailed report of all examples tried is printed to stdout, along -with assorted summaries at the end. - -You can force verbose mode by passing "verbose=1" to testmod, or prohibit -it by passing "verbose=0". In either of those cases, sys.argv is not -examined by testmod. - -In any case, testmod returns a 2-tuple of ints (f, t), where f is the -number of docstring examples that failed and t is the total number of -docstring examples attempted. - - -WHICH DOCSTRINGS ARE EXAMINED? - -+ M.__doc__. - -+ f.__doc__ for all functions f in M.__dict__.values(), except those - with private names. - -+ C.__doc__ for all classes C in M.__dict__.values(), except those with - private names. - -+ If M.__test__ exists and "is true", it must be a dict, and - each entry maps a (string) name to a function object, class object, or - string. Function and class object docstrings found from M.__test__ - are searched even if the name is private, and strings are searched - directly as if they were docstrings. In output, a key K in M.__test__ - appears with name - .__test__.K - -Any classes found are recursively searched similarly, to test docstrings in -their contained methods and nested classes. Private names reached from M's -globals are skipped, but all names reached from M.__test__ are searched. - -By default, a name is considered to be private if it begins with an -underscore (like "_my_func") but doesn't both begin and end with (at least) -two underscores (like "__init__"). You can change the default by passing -your own "isprivate" function to testmod. - -If you want to test docstrings in objects with private names too, stuff -them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your -own isprivate function to Tester's constructor, or call the rundoc method -of a Tester instance). - -Warning: imports can cause trouble; e.g., if you do - -from XYZ import XYZclass - -then XYZclass is a name in M.__dict__ too, and doctest has no way to know -that XYZclass wasn't *defined* in M. So it may try to execute the examples -in XYZclass's docstring, and those in turn may require a different set of -globals to work correctly. I prefer to do "import *"- friendly imports, -a la - -import XYY -_XYZclass = XYZ.XYZclass -del XYZ - -or (Python 2.0) - -from XYZ import XYZclass as _XYZclass - -and then the leading underscore stops testmod from going nuts. You may -prefer the method in the next section. - - -WHAT'S THE EXECUTION CONTEXT? - -By default, each time testmod finds a docstring to test, it uses a *copy* -of M's globals (so that running tests on a module doesn't change the -module's real globals, and so that one test in M can't leave behind crumbs -that accidentally allow another test to work). This means examples can -freely use any names defined at top-level in M. It also means that sloppy -imports (see above) can cause examples in external docstrings to use -globals inappropriate for them. - -You can force use of your own dict as the execution context by passing -"globs=your_dict" to testmod instead. Presumably this would be a copy of -M.__dict__ merged with the globals from other imported modules. - - -WHAT IF I WANT TO TEST A WHOLE PACKAGE? - -Piece o' cake, provided the modules do their testing from docstrings. -Here's the test.py I use for the world's most elaborate Rational/ -floating-base-conversion pkg (which I'll distribute some day): - -from Rational import Cvt -from Rational import Format -from Rational import machprec -from Rational import Rat -from Rational import Round -from Rational import utils - -modules = (Cvt, - Format, - machprec, - Rat, - Round, - utils) - -def _test(): - import doctest - import sys - verbose = "-v" in sys.argv - for mod in modules: - doctest.testmod(mod, verbose=verbose, report=0) - doctest.master.summarize() - -if __name__ == "__main__": - _test() - -IOW, it just runs testmod on all the pkg modules. testmod remembers the -names and outcomes (# of failures, # of tries) for each item it's seen, and -passing "report=0" prevents it from printing a summary in verbose mode. -Instead, the summary is delayed until all modules have been tested, and -then "doctest.master.summarize()" forces the summary at the end. - -So this is very nice in practice: each module can be tested individually -with almost no work beyond writing up docstring examples, and collections -of modules can be tested too as a unit with no more work than the above. - - -WHAT ABOUT EXCEPTIONS? - -No problem, as long as the only output generated by the example is the -traceback itself. For example: - - >>> a = [None] - >>> a[1] - Traceback (innermost last): - File "", line 1, in ? - IndexError: list index out of range - >>> - -Note that only the exception type and value are compared (specifically, -only the last line in the traceback). - - -ADVANCED USAGE - -doctest.testmod() captures the testing policy I find most useful most -often. You may want other policies. - -testmod() actually creates a local instance of class doctest.Tester, runs -appropriate methods of that class, and merges the results into global -Tester instance doctest.master. - -You can create your own instances of doctest.Tester, and so build your own -policies, or even run methods of doctest.master directly. See -doctest.Tester.__doc__ for details. - - -SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!? - -Oh ya. It's easy! In most cases a copy-and-paste of an interactive -console session works fine -- just make sure the leading whitespace is -rigidly consistent (you can mix tabs and spaces if you're too lazy to do it -right, but doctest is not in the business of guessing what you think a tab -means). - - >>> # comments are ignored - >>> x = 12 - >>> x - 12 - >>> if x == 13: - ... print "yes" - ... else: - ... print "no" - ... print "NO" - ... print "NO!!!" - ... - no - NO - NO!!! - >>> - -Any expected output must immediately follow the final ">>>" or "..." line -containing the code, and the expected output (if any) extends to the next -">>>" or all-whitespace line. That's it. - -Bummers: - -+ Expected output cannot contain an all-whitespace line, since such a line - is taken to signal the end of expected output. - -+ Output to stdout is captured, but not output to stderr (exception - tracebacks are captured via a different means). - -+ If you continue a line via backslashing in an interactive session, or for - any other reason use a backslash, you need to double the backslash in the - docstring version. This is simply because you're in a string, and so the - backslash must be escaped for it to survive intact. Like: - ->>> if "yes" == \\ -... "y" + \\ -... "es": # in the source code you'll see the doubled backslashes -... print 'yes' -yes - -The starting column doesn't matter: - ->>> assert "Easy!" - >>> import math - >>> math.floor(1.9) - 1.0 - -and as many leading whitespace characters are stripped from the expected -output as appeared in the initial ">>>" line that triggered it. - -If you execute this very file, the examples above will be found and -executed, leading to this output in verbose mode: - -Running doctest.__doc__ -Trying: a = [None] -Expecting: nothing -ok -Trying: a[1] -Expecting: -Traceback (innermost last): - File "", line 1, in ? -IndexError: list index out of range -ok -Trying: x = 12 -Expecting: nothing -ok -Trying: x -Expecting: 12 -ok -Trying: -if x == 13: - print "yes" -else: - print "no" - print "NO" - print "NO!!!" -Expecting: -no -NO -NO!!! -ok -... and a bunch more like that, with this summary at the end: - -5 items had no tests: - doctest.Tester.__init__ - doctest.Tester.run__test__ - doctest.Tester.summarize - doctest.run_docstring_examples - doctest.testmod -12 items passed all tests: - 9 tests in doctest - 6 tests in doctest.Tester - 10 tests in doctest.Tester.merge - 7 tests in doctest.Tester.rundict - 3 tests in doctest.Tester.rundoc - 3 tests in doctest.Tester.runstring - 2 tests in doctest.__test__._TestClass - 2 tests in doctest.__test__._TestClass.__init__ - 2 tests in doctest.__test__._TestClass.get - 1 tests in doctest.__test__._TestClass.square - 2 tests in doctest.__test__.string - 7 tests in doctest.is_private -54 tests in 17 items. -54 passed and 0 failed. -Test passed. -""" - -# 0,0,1 06-Mar-1999 -# initial version posted -# 0,0,2 06-Mar-1999 -# loosened parsing: -# cater to stinkin' tabs -# don't insist on a blank after PS2 prefix -# so trailing "... " line from a compound stmt no longer -# breaks if the file gets whitespace-trimmed -# better error msgs for inconsistent leading whitespace -# 0,9,1 08-Mar-1999 -# exposed the Tester class and added client methods -# plus docstring examples of their use (eww - head-twisting!) -# fixed logic error in reporting total # of tests & failures -# added __test__ support to testmod (a pale reflection of Christian -# Tismer's vision ...) -# removed the "deep" argument; fiddle __test__ instead -# simplified endcase logic for extracting tests, and running them. -# before, if no output was expected but some was produced -# anyway via an eval'ed result, the discrepancy wasn't caught -# made TestClass private and used __test__ to get at it -# many doc updates -# speed _SpoofOut for long expected outputs -# 0,9,2 09-Mar-1999 -# throw out comments from examples, enabling use of the much simpler -# exec compile(... "single") ... -# for simulating the runtime; that barfs on comment-only lines -# used the traceback module to do a much better job of reporting -# exceptions -# run __doc__ values thru str(), "just in case" -# privateness of names now determined by an overridable "isprivate" -# function -# by default a name now considered to be private iff it begins with -# an underscore but doesn't both begin & end with two of 'em; so -# e.g. Class.__init__ etc are searched now -- as they always -# should have been -# 0,9,3 18-Mar-1999 -# added .flush stub to _SpoofOut (JPython buglet diagnosed by -# Hugh Emberson) -# repaired ridiculous docs about backslashes in examples -# minor internal changes -# changed source to Unix line-end conventions -# moved __test__ logic into new Tester.run__test__ method -# 0,9,4 27-Mar-1999 -# report item name and line # in failing examples -# 0,9,5 29-Jun-1999 -# allow straightforward exceptions in examples - thanks to Mark Hammond! -# 0,9,6 16-Jan-2001 -# fiddling for changes in Python 2.0: some of the embedded docstring -# examples no longer worked *exactly* as advertised, due to minor -# language changes, and running doctest on itself pointed that out. -# Hard to think of a better example of why this is useful . - -__version__ = 0, 9, 6 - -import types -_FunctionType = types.FunctionType -_ClassType = types.ClassType -_ModuleType = types.ModuleType -_StringType = types.StringType -del types - -import string -_string_find = string.find -_string_join = string.join -_string_split = string.split -_string_rindex = string.rindex -del string - -import re -PS1 = ">>>" -PS2 = "..." -_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match -_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match -_isEmpty = re.compile(r"\s*$").match -_isComment = re.compile(r"\s*#").match -del re - -__all__ = [] - -# Extract interactive examples from a string. Return a list of triples, -# (source, outcome, lineno). "source" is the source code, and ends -# with a newline iff the source spans more than one line. "outcome" is -# the expected output if any, else an empty string. When not empty, -# outcome always ends with a newline. "lineno" is the line number, -# 0-based wrt the start of the string, of the first source line. - -def _extract_examples(s): - isPS1, isPS2 = _isPS1, _isPS2 - isEmpty, isComment = _isEmpty, _isComment - examples = [] - lines = _string_split(s, "\n") - i, n = 0, len(lines) - while i < n: - line = lines[i] - i = i + 1 - m = isPS1(line) - if m is None: - continue - j = m.end(0) # beyond the prompt - if isEmpty(line, j) or isComment(line, j): - # a bare prompt or comment -- not interesting - continue - lineno = i - 1 - if line[j] != " ": - raise ValueError("line " + `lineno` + " of docstring lacks " - "blank after " + PS1 + ": " + line) - j = j + 1 - blanks = m.group(1) - nblanks = len(blanks) - # suck up this and following PS2 lines - source = [] - while 1: - source.append(line[j:]) - line = lines[i] - m = isPS2(line) - if m: - if m.group(1) != blanks: - raise ValueError("inconsistent leading whitespace " - "in line " + `i` + " of docstring: " + line) - i = i + 1 - else: - break - if len(source) == 1: - source = source[0] - else: - # get rid of useless null line from trailing empty "..." - if source[-1] == "": - del source[-1] - source = _string_join(source, "\n") + "\n" - # suck up response - if isPS1(line) or isEmpty(line): - expect = "" - else: - expect = [] - while 1: - if line[:nblanks] != blanks: - raise ValueError("inconsistent leading whitespace " - "in line " + `i` + " of docstring: " + line) - expect.append(line[nblanks:]) - i = i + 1 - line = lines[i] - if isPS1(line) or isEmpty(line): - break - expect = _string_join(expect, "\n") + "\n" - examples.append( (source, expect, lineno) ) - return examples - -# Capture stdout when running examples. - -class _SpoofOut: - def __init__(self): - self.clear() - def write(self, s): - self.buf.append(s) - def get(self): - return _string_join(self.buf, "") - def clear(self): - self.buf = [] - def flush(self): - # JPython calls flush - pass - -# Display some tag-and-msg pairs nicely, keeping the tag and its msg -# on the same line when that makes sense. - -def _tag_out(printer, *tag_msg_pairs): - for tag, msg in tag_msg_pairs: - printer(tag + ":") - msg_has_nl = msg[-1:] == "\n" - msg_has_two_nl = msg_has_nl and \ - _string_find(msg, "\n") < len(msg) - 1 - if len(tag) + len(msg) < 76 and not msg_has_two_nl: - printer(" ") - else: - printer("\n") - printer(msg) - if not msg_has_nl: - printer("\n") - -# Run list of examples, in context globs. "out" can be used to display -# stuff to "the real" stdout, and fakeout is an instance of _SpoofOut -# that captures the examples' std output. Return (#failures, #tries). - -def _run_examples_inner(out, fakeout, examples, globs, verbose, name): - import sys, traceback - OK, BOOM, FAIL = range(3) - NADA = "nothing" - stderr = _SpoofOut() - failures = 0 - for source, want, lineno in examples: - if verbose: - _tag_out(out, ("Trying", source), - ("Expecting", want or NADA)) - fakeout.clear() - try: - exec compile(source, "", "single") in globs - got = fakeout.get() - state = OK - except: - # See whether the exception was expected. - if _string_find(want, "Traceback (innermost last):\n") == 0 or\ - _string_find(want, "Traceback (most recent call last):\n") == 0: - # Only compare exception type and value - the rest of - # the traceback isn't necessary. - want = _string_split(want, '\n')[-2] + '\n' - exc_type, exc_val, exc_tb = sys.exc_info() - got = traceback.format_exception_only(exc_type, exc_val)[0] - state = OK - else: - # unexpected exception - stderr.clear() - traceback.print_exc(file=stderr) - state = BOOM - - if state == OK: - if got == want: - if verbose: - out("ok\n") - continue - state = FAIL - - assert state in (FAIL, BOOM) - failures = failures + 1 - out("*" * 65 + "\n") - _tag_out(out, ("Failure in example", source)) - out("from line #" + `lineno` + " of " + name + "\n") - if state == FAIL: - _tag_out(out, ("Expected", want or NADA), ("Got", got)) - else: - assert state == BOOM - _tag_out(out, ("Exception raised", stderr.get())) - - return failures, len(examples) - -# Run list of examples, in context globs. Return (#failures, #tries). - -def _run_examples(examples, globs, verbose, name): - import sys - saveout = sys.stdout - try: - sys.stdout = fakeout = _SpoofOut() - x = _run_examples_inner(saveout.write, fakeout, examples, - globs, verbose, name) - finally: - sys.stdout = saveout - return x - -def run_docstring_examples(f, globs, verbose=0, name="NoName"): - """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__. - - Use dict globs as the globals for execution. - Return (#failures, #tries). - - If optional arg verbose is true, print stuff even if there are no - failures. - Use string name in failure msgs. - """ - - try: - doc = f.__doc__ - if not doc: - # docstring empty or None - return 0, 0 - # just in case CT invents a doc object that has to be forced - # to look like a string <0.9 wink> - doc = str(doc) - except: - return 0, 0 - - e = _extract_examples(doc) - if not e: - return 0, 0 - return _run_examples(e, globs, verbose, name) - -def is_private(prefix, base): - """prefix, base -> true iff name prefix + "." + base is "private". - - Prefix may be an empty string, and base does not contain a period. - Prefix is ignored (although functions you write conforming to this - protocol may make use of it). - Return true iff base begins with an (at least one) underscore, but - does not both begin and end with (at least) two underscores. - - >>> is_private("a.b", "my_func") - 0 - >>> is_private("____", "_my_func") - 1 - >>> is_private("someclass", "__init__") - 0 - >>> is_private("sometypo", "__init_") - 1 - >>> is_private("x.y.z", "_") - 1 - >>> is_private("_x.y.z", "__") - 0 - >>> is_private("", "") # senseless but consistent - 0 - """ - - return base[:1] == "_" and not base[:2] == "__" == base[-2:] - -class Tester: - """Class Tester -- runs docstring examples and accumulates stats. - -In normal use, function doctest.testmod() hides all this from you, -so use that if you can. Create your own instances of Tester to do -fancier things. - -Methods: - runstring(s, name) - Search string s for examples to run; use name for logging. - Return (#failures, #tries). - - rundoc(object, name=None) - Search object.__doc__ for examples to run; use name (or - object.__name__) for logging. Return (#failures, #tries). - - rundict(d, name) - Search for examples in docstrings in all of d.values(); use name - for logging. Return (#failures, #tries). - - run__test__(d, name) - Treat dict d like module.__test__. Return (#failures, #tries). - - summarize(verbose=None) - Display summary of testing results, to stdout. Return - (#failures, #tries). - - merge(other) - Merge in the test results from Tester instance "other". - ->>> from doctest import Tester ->>> t = Tester(globs={'x': 42}, verbose=0) ->>> t.runstring(r''' -... >>> x = x * 2 -... >>> print x -... 42 -... ''', 'XYZ') -***************************************************************** -Failure in example: print x -from line #2 of XYZ -Expected: 42 -Got: 84 -(1, 2) ->>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2') -(0, 2) ->>> t.summarize() -1 items had failures: - 1 of 2 in XYZ -***Test Failed*** 1 failures. -(1, 4) ->>> t.summarize(verbose=1) -1 items passed all tests: - 2 tests in example2 -1 items had failures: - 1 of 2 in XYZ -4 tests in 2 items. -3 passed and 1 failed. -***Test Failed*** 1 failures. -(1, 4) ->>> -""" - - def __init__(self, mod=None, globs=None, verbose=None, - isprivate=None): - """mod=None, globs=None, verbose=None, isprivate=None - -See doctest.__doc__ for an overview. - -Optional keyword arg "mod" is a module, whose globals are used for -executing examples. If not specified, globs must be specified. - -Optional keyword arg "globs" gives a dict to be used as the globals -when executing examples; if not specified, use the globals from -module mod. - -In either case, a copy of the dict is used for each docstring -examined. - -Optional keyword arg "verbose" prints lots of stuff if true, only -failures if false; by default, it's true iff "-v" is in sys.argv. - -Optional keyword arg "isprivate" specifies a function used to determine -whether a name is private. The default function is doctest.is_private; -see its docs for details. -""" - - if mod is None and globs is None: - raise TypeError("Tester.__init__: must specify mod or globs") - if mod is not None and type(mod) is not _ModuleType: - raise TypeError("Tester.__init__: mod must be a module; " + - `mod`) - if globs is None: - globs = mod.__dict__ - self.globs = globs - - if verbose is None: - import sys - verbose = "-v" in sys.argv - self.verbose = verbose - - if isprivate is None: - isprivate = is_private - self.isprivate = isprivate - - self.name2ft = {} # map name to (#failures, #trials) pair - - def runstring(self, s, name): - """ - s, name -> search string s for examples to run, logging as name. - - Use string name as the key for logging the outcome. - Return (#failures, #examples). - - >>> t = Tester(globs={}, verbose=1) - >>> test = r''' - ... # just an example - ... >>> x = 1 + 2 - ... >>> x - ... 3 - ... ''' - >>> t.runstring(test, "Example") - Running string Example - Trying: x = 1 + 2 - Expecting: nothing - ok - Trying: x - Expecting: 3 - ok - 0 of 2 examples failed in string Example - (0, 2) - """ - - if self.verbose: - print "Running string", name - f = t = 0 - e = _extract_examples(s) - if e: - f, t = _run_examples(e, self.globs.copy(), self.verbose, name) - if self.verbose: - print f, "of", t, "examples failed in string", name - self.__record_outcome(name, f, t) - return f, t - - def rundoc(self, object, name=None): - """ - object, name=None -> search object.__doc__ for examples to run. - - Use optional string name as the key for logging the outcome; - by default use object.__name__. - Return (#failures, #examples). - If object is a class object, search recursively for method - docstrings too. - object.__doc__ is examined regardless of name, but if object is - a class, whether private names reached from object are searched - depends on the constructor's "isprivate" argument. - - >>> t = Tester(globs={}, verbose=0) - >>> def _f(): - ... '''Trivial docstring example. - ... >>> assert 2 == 2 - ... ''' - ... return 32 - ... - >>> t.rundoc(_f) # expect 0 failures in 1 example - (0, 1) - """ - - if name is None: - try: - name = object.__name__ - except AttributeError: - raise ValueError("Tester.rundoc: name must be given " - "when object.__name__ doesn't exist; " + `object`) - if self.verbose: - print "Running", name + ".__doc__" - f, t = run_docstring_examples(object, self.globs.copy(), - self.verbose, name) - if self.verbose: - print f, "of", t, "examples failed in", name + ".__doc__" - self.__record_outcome(name, f, t) - if type(object) is _ClassType: - f2, t2 = self.rundict(object.__dict__, name) - f = f + f2 - t = t + t2 - return f, t - - def rundict(self, d, name): - """ - d. name -> search for docstring examples in all of d.values(). - - For k, v in d.items() such that v is a function or class, - do self.rundoc(v, name + "." + k). Whether this includes - objects with private names depends on the constructor's - "isprivate" argument. - Return aggregate (#failures, #examples). - - >>> def _f(): - ... '''>>> assert 1 == 1 - ... ''' - >>> def g(): - ... '''>>> assert 2 != 1 - ... ''' - >>> d = {"_f": _f, "g": g} - >>> t = Tester(globs={}, verbose=0) - >>> t.rundict(d, "rundict_test") # _f is skipped - (0, 1) - >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) - >>> t.rundict(d, "rundict_test_pvt") # both are searched - (0, 2) - """ - - if not hasattr(d, "items"): - raise TypeError("Tester.rundict: d must support .items(); " + - `d`) - f = t = 0 - for thisname, value in d.items(): - if type(value) in (_FunctionType, _ClassType): - f2, t2 = self.__runone(value, name + "." + thisname) - f = f + f2 - t = t + t2 - return f, t - - def run__test__(self, d, name): - """d, name -> Treat dict d like module.__test__. - - Return (#failures, #tries). - See testmod.__doc__ for details. - """ - - failures = tries = 0 - prefix = name + "." - savepvt = self.isprivate - try: - self.isprivate = lambda *args: 0 - for k, v in d.items(): - thisname = prefix + k - if type(v) is _StringType: - f, t = self.runstring(v, thisname) - elif type(v) in (_FunctionType, _ClassType): - f, t = self.rundoc(v, thisname) - else: - raise TypeError("Tester.run__test__: values in " - "dict must be strings, functions " - "or classes; " + `v`) - failures = failures + f - tries = tries + t - finally: - self.isprivate = savepvt - return failures, tries - - def summarize(self, verbose=None): - """ - verbose=None -> summarize results, return (#failures, #tests). - - Print summary of test results to stdout. - Optional arg 'verbose' controls how wordy this is. By - default, use the verbose setting established by the - constructor. - """ - - if verbose is None: - verbose = self.verbose - notests = [] - passed = [] - failed = [] - totalt = totalf = 0 - for x in self.name2ft.items(): - name, (f, t) = x - assert f <= t - totalt = totalt + t - totalf = totalf + f - if t == 0: - notests.append(name) - elif f == 0: - passed.append( (name, t) ) - else: - failed.append(x) - if verbose: - if notests: - print len(notests), "items had no tests:" - notests.sort() - for thing in notests: - print " ", thing - if passed: - print len(passed), "items passed all tests:" - passed.sort() - for thing, count in passed: - print " %3d tests in %s" % (count, thing) - if failed: - print len(failed), "items had failures:" - failed.sort() - for thing, (f, t) in failed: - print " %3d of %3d in %s" % (f, t, thing) - if verbose: - print totalt, "tests in", len(self.name2ft), "items." - print totalt - totalf, "passed and", totalf, "failed." - if totalf: - print "***Test Failed***", totalf, "failures." - elif verbose: - print "Test passed." - return totalf, totalt - - def merge(self, other): - """ - other -> merge in test results from the other Tester instance. - - If self and other both have a test result for something - with the same name, the (#failures, #tests) results are - summed, and a warning is printed to stdout. - - >>> from doctest import Tester - >>> t1 = Tester(globs={}, verbose=0) - >>> t1.runstring(''' - ... >>> x = 12 - ... >>> print x - ... 12 - ... ''', "t1example") - (0, 2) - >>> - >>> t2 = Tester(globs={}, verbose=0) - >>> t2.runstring(''' - ... >>> x = 13 - ... >>> print x - ... 13 - ... ''', "t2example") - (0, 2) - >>> common = ">>> assert 1 + 2 == 3\\n" - >>> t1.runstring(common, "common") - (0, 1) - >>> t2.runstring(common, "common") - (0, 1) - >>> t1.merge(t2) - *** Tester.merge: 'common' in both testers; summing outcomes. - >>> t1.summarize(1) - 3 items passed all tests: - 2 tests in common - 2 tests in t1example - 2 tests in t2example - 6 tests in 3 items. - 6 passed and 0 failed. - Test passed. - (0, 6) - >>> - """ - - d = self.name2ft - for name, (f, t) in other.name2ft.items(): - if d.has_key(name): - print "*** Tester.merge: '" + name + "' in both" \ - " testers; summing outcomes." - f2, t2 = d[name] - f = f + f2 - t = t + t2 - d[name] = f, t - - def __record_outcome(self, name, f, t): - if self.name2ft.has_key(name): - print "*** Warning: '" + name + "' was tested before;", \ - "summing outcomes." - f2, t2 = self.name2ft[name] - f = f + f2 - t = t + t2 - self.name2ft[name] = f, t - - def __runone(self, target, name): - if "." in name: - i = _string_rindex(name, ".") - prefix, base = name[:i], name[i+1:] - else: - prefix, base = "", base - if self.isprivate(prefix, base): - return 0, 0 - return self.rundoc(target, name) - -master = None - -def testmod(m, name=None, globs=None, verbose=None, isprivate=None, - report=1): - """m, name=None, globs=None, verbose=None, isprivate=None, report=1 - - Test examples in docstrings in functions and classes reachable from - module m, starting with m.__doc__. Private names are skipped. - - Also test examples reachable from dict m.__test__ if it exists and is - not None. m.__dict__ maps names to functions, classes and strings; - function and class docstrings are tested even if the name is private; - strings are tested directly, as if they were docstrings. - - Return (#failures, #tests). - - See doctest.__doc__ for an overview. - - Optional keyword arg "name" gives the name of the module; by default - use m.__name__. - - Optional keyword arg "globs" gives a dict to be used as the globals - when executing examples; by default, use m.__dict__. A copy of this - dict is actually used for each docstring, so that each docstring's - examples start with a clean slate. - - Optional keyword arg "verbose" prints lots of stuff if true, prints - only failures if false; by default, it's true iff "-v" is in sys.argv. - - Optional keyword arg "isprivate" specifies a function used to - determine whether a name is private. The default function is - doctest.is_private; see its docs for details. - - Optional keyword arg "report" prints a summary at the end when true, - else prints nothing at the end. In verbose mode, the summary is - detailed, else very brief (in fact, empty if all tests passed). - - Advanced tomfoolery: testmod runs methods of a local instance of - class doctest.Tester, then merges the results into (or creates) - global Tester instance doctest.master. Methods of doctest.master - can be called directly too, if you want to do something unusual. - Passing report=0 to testmod is especially useful then, to delay - displaying a summary. Invoke doctest.master.summarize(verbose) - when you're done fiddling. - """ - - global master - - if type(m) is not _ModuleType: - raise TypeError("testmod: module required; " + `m`) - if name is None: - name = m.__name__ - tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate) - failures, tries = tester.rundoc(m, name) - f, t = tester.rundict(m.__dict__, name) - failures = failures + f - tries = tries + t - if hasattr(m, "__test__"): - testdict = m.__test__ - if testdict: - if not hasattr(testdict, "items"): - raise TypeError("testmod: module.__test__ must support " - ".items(); " + `testdict`) - f, t = tester.run__test__(testdict, name + ".__test__") - failures = failures + f - tries = tries + t - if report: - tester.summarize() - if master is None: - master = tester - else: - master.merge(tester) - return failures, tries - -class _TestClass: - """ - A pointless class, for sanity-checking of docstring testing. - - Methods: - square() - get() - - >>> _TestClass(13).get() + _TestClass(-12).get() - 1 - >>> hex(_TestClass(13).square().get()) - '0xa9' - """ - - def __init__(self, val): - """val -> _TestClass object with associated value val. - - >>> t = _TestClass(123) - >>> print t.get() - 123 - """ - - self.val = val - - def square(self): - """square() -> square TestClass's associated value - - >>> _TestClass(13).square().get() - 169 - """ - - self.val = self.val ** 2 - return self - - def get(self): - """get() -> return TestClass's associated value. - - >>> x = _TestClass(-42) - >>> print x.get() - -42 - """ - - return self.val - -__test__ = {"_TestClass": _TestClass, - "string": r""" - Example of a string object, searched as-is. - >>> x = 1; y = 2 - >>> x + y, x * y - (3, 2) - """ - } - -def _test(): - import doctest - return doctest.testmod(doctest) - -if __name__ == "__main__": - _test() diff --git a/encode.c b/encode.c index 2ae13ad72..8be44d8ec 100644 --- a/encode.c +++ b/encode.c @@ -15,7 +15,7 @@ * 1999-02-07 fl Added PCX encoder * * Copyright (c) 1997-2001 by Secret Labs AB - * Copyright (c) 1996-1997 by Fredrik Lundh + * Copyright (c) 1996-1997 by Fredrik Lundh * * See the README file for information on usage and redistribution. */ @@ -24,12 +24,8 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" +#include "py3.h" #include "Gif.h" #ifdef HAVE_UNISTD_H @@ -49,7 +45,7 @@ typedef struct { PyObject* lock; } ImagingEncoderObject; -staticforward PyTypeObject ImagingEncoderType; +static PyTypeObject ImagingEncoderType; static ImagingEncoderObject* PyImaging_EncoderNew(int contextsize) @@ -57,7 +53,8 @@ PyImaging_EncoderNew(int contextsize) ImagingEncoderObject *encoder; void *context; - ImagingEncoderType.ob_type = &PyType_Type; + if(!PyType_Ready(&ImagingEncoderType) < 0) + return NULL; encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); if (encoder == NULL) @@ -96,7 +93,7 @@ _dealloc(ImagingEncoderObject* encoder) PyObject_Del(encoder); } -static PyObject* +static PyObject* _encode(ImagingEncoderObject* encoder, PyObject* args) { PyObject* buf; @@ -110,15 +107,15 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) if (!PyArg_ParseTuple(args, "|i", &bufsize)) return NULL; - buf = PyString_FromStringAndSize(NULL, bufsize); + buf = PyBytes_FromStringAndSize(NULL, bufsize); if (!buf) return NULL; status = encoder->encode(encoder->im, &encoder->state, - (UINT8*) PyString_AsString(buf), bufsize); + (UINT8*) PyBytes_AsString(buf), bufsize); /* adjust string length to avoid slicing in encoder */ - if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0) + if (_PyBytes_Resize(&buf, (status > 0) ? status : 0) < 0) return NULL; result = Py_BuildValue("iiO", status, encoder->state.errcode, buf); @@ -128,7 +125,7 @@ _encode(ImagingEncoderObject* encoder, PyObject* args) return result; } -static PyObject* +static PyObject* _encode_to_file(ImagingEncoderObject* encoder, PyObject* args) { UINT8* buf; @@ -241,26 +238,38 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_getattr(ImagingEncoderObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingEncoderType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject ImagingEncoderType = { + PyVarObject_HEAD_INIT(NULL, 0) "ImagingEncoder", /*tp_name*/ sizeof(ImagingEncoderObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; /* -------------------------------------------------------------------- */ @@ -436,11 +445,27 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) char* mode; char* rawmode; int optimize = 0; + int compress_level = -1; + int compress_type = -1; char* dictionary = NULL; int dictionary_size = 0; - if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize, - &dictionary, &dictionary_size)) - return NULL; + if (!PyArg_ParseTuple(args, "ss|iii"PY_ARG_BYTES_LENGTH, &mode, &rawmode, + &optimize, + &compress_level, &compress_type, + &dictionary, &dictionary_size)) + return NULL; + + /* Copy to avoid referencing Python's memory, but there's no mechanism to + free this memory later, so this function (and several others here) + leaks. */ + if (dictionary && dictionary_size > 0) { + char* p = malloc(dictionary_size); + if (!p) + return PyErr_NoMemory(); + memcpy(p, dictionary, dictionary_size); + dictionary = p; + } else + dictionary = NULL; encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE)); if (encoder == NULL) @@ -456,6 +481,8 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) ((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE; ((ZIPSTATE*)encoder->state.context)->optimize = optimize; + ((ZIPSTATE*)encoder->state.context)->compress_level = compress_level; + ((ZIPSTATE*)encoder->state.context)->compress_type = compress_type; ((ZIPSTATE*)encoder->state.context)->dictionary = dictionary; ((ZIPSTATE*)encoder->state.context)->dictionary_size = dictionary_size; @@ -485,6 +512,69 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) #include "Jpeg.h" +static unsigned int** get_qtables_arrays(PyObject* qtables) { + PyObject* tables; + PyObject* table; + PyObject* table_data; + int i, j, num_tables; + unsigned int **qarrays; + + if ((qtables == NULL) || (qtables == Py_None)) { + return NULL; + } + + if (!PySequence_Check(qtables)) { + PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); + return NULL; + } + + tables = PySequence_Fast(qtables, "expected a sequence"); + num_tables = PySequence_Size(qtables); + if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) { + PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4."); + return NULL; + } + qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int*)); + if (!qarrays) { + Py_DECREF(tables); + PyErr_NoMemory(); + return NULL; + } + for (i = 0; i < num_tables; i++) { + table = PySequence_Fast_GET_ITEM(tables, i); + if (!PySequence_Check(table)) { + Py_DECREF(tables); + PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); + return NULL; + } + if (PySequence_Size(table) != DCTSIZE2) { + Py_DECREF(tables); + PyErr_SetString(PyExc_ValueError, "Invalid quantization tables"); + return NULL; + } + table_data = PySequence_Fast(table, "expected a sequence"); + qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int)); + if (!qarrays[i]) { + Py_DECREF(tables); + PyErr_NoMemory(); + return NULL; + } + for (j = 0; j < DCTSIZE2; j++) { + qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j)); + } + } + + Py_DECREF(tables); + + if (PyErr_Occurred()) { + PyMem_Free(qarrays); + qarrays = NULL; + } + + return qarrays; +} + + PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) { @@ -499,10 +589,18 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */ int xdpi = 0, ydpi = 0; int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ - char* extra = NULL; int extra_size; - if (!PyArg_ParseTuple(args, "ss|iiiiiiiis#", &mode, &rawmode, &quality, - &progressive, &smooth, &optimize, &streamtype, - &xdpi, &ydpi, &subsampling, &extra, &extra_size)) + PyObject* qtables=NULL; + unsigned int **qarrays = NULL; + char* extra = NULL; + int extra_size; + char* rawExif = NULL; + int rawExifLen = 0; + + if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH, + &mode, &rawmode, &quality, + &progressive, &smooth, &optimize, &streamtype, + &xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size, + &rawExif, &rawExifLen)) return NULL; encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE)); @@ -512,6 +610,8 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) if (get_packer(encoder, mode, rawmode) < 0) return NULL; + qarrays = get_qtables_arrays(qtables); + if (extra && extra_size > 0) { char* p = malloc(extra_size); if (!p) @@ -521,9 +621,19 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) } else extra = NULL; + if (rawExif && rawExifLen > 0) { + char* pp = malloc(rawExifLen); + if (!pp) + return PyErr_NoMemory(); + memcpy(pp, rawExif, rawExifLen); + rawExif = pp; + } else + rawExif = NULL; + encoder->encode = ImagingJpegEncode; ((JPEGENCODERSTATE*)encoder->state.context)->quality = quality; + ((JPEGENCODERSTATE*)encoder->state.context)->qtables = qarrays; ((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling; ((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive; ((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth; @@ -533,8 +643,153 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) ((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi; ((JPEGENCODERSTATE*)encoder->state.context)->extra = extra; ((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size; + ((JPEGENCODERSTATE*)encoder->state.context)->rawExif = rawExif; + ((JPEGENCODERSTATE*)encoder->state.context)->rawExifLen = rawExifLen; return (PyObject*) encoder; } #endif + +/* -------------------------------------------------------------------- */ +/* LibTiff */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBTIFF + +#include "TiffDecode.h" + +#include +#ifdef __WIN32__ +#define strcasecmp(s1, s2) stricmp(s1, s2) +#endif + +PyObject* +PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char* mode; + char* rawmode; + char* compname; + char* filename; + int fp; + + PyObject *dir; + PyObject *key, *value; + Py_ssize_t pos = 0; + int status; + + Py_ssize_t d_size; + PyObject *keys, *values; + + + if (! PyArg_ParseTuple(args, "sssisO", &mode, &rawmode, &compname, &fp, &filename, &dir)) { + return NULL; + } + + if (!PyDict_Check(dir)) { + PyErr_SetString(PyExc_ValueError, "Invalid Dictionary"); + return NULL; + } else { + d_size = PyDict_Size(dir); + TRACE(("dict size: %d\n", (int)d_size)); + keys = PyDict_Keys(dir); + values = PyDict_Values(dir); + for (pos=0;posstate, filename, fp)) { + Py_DECREF(encoder); + PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed"); + return NULL; + } + + // While failes on 64 bit machines, complains that pos is an int instead of a Py_ssize_t + // while (PyDict_Next(dir, &pos, &key, &value)) { + for (pos=0;posstate, + (ttag_t) PyInt_AsLong(key), + PyInt_AsLong(value)); + } else if(PyBytes_Check(value)) { + TRACE(("Setting from Bytes: %d, %s \n", (int)PyInt_AsLong(key),PyBytes_AsString(value))); + status = ImagingLibTiffSetField(&encoder->state, + (ttag_t) PyInt_AsLong(key), + PyBytes_AsString(value)); + } else if(PyList_Check(value)) { + int len,i; + float *floatav; + int *intav; + TRACE(("Setting from List: %d \n", (int)PyInt_AsLong(key))); + len = (int)PyList_Size(value); + if (len) { + if (PyInt_Check(PyList_GetItem(value,0))) { + TRACE((" %d elements, setting as ints \n", len)); + intav = malloc(sizeof(int)*len); + if (intav) { + for (i=0;istate, + (ttag_t) PyInt_AsLong(key), + intav); + free(intav); + } + } else { + TRACE((" %d elements, setting as floats \n", len)); + floatav = malloc(sizeof(float)*len); + if (floatav) { + for (i=0;istate, + (ttag_t) PyInt_AsLong(key), + floatav); + free(floatav); + } + } + } + } else if (PyFloat_Check(value)) { + TRACE(("Setting from Float: %d, %f \n", (int)PyInt_AsLong(key),PyFloat_AsDouble(value))); + status = ImagingLibTiffSetField(&encoder->state, + (ttag_t) PyInt_AsLong(key), + (float)PyFloat_AsDouble(value)); + } else { + TRACE(("Unhandled type for key %d : %s \n", + (int)PyInt_AsLong(key), + PyBytes_AsString(PyObject_Str(value)))); + } + if (!status) { + TRACE(("Error setting Field\n")); + Py_DECREF(encoder); + PyErr_SetString(PyExc_RuntimeError, "Error setting from dictionary"); + return NULL; + } + } + + encoder->encode = ImagingLibTiffEncode; + + return (PyObject*) encoder; +} + +#endif + diff --git a/libImaging/Access.c b/libImaging/Access.c index 5ebc9b6f3..70eb1af4c 100644 --- a/libImaging/Access.c +++ b/libImaging/Access.c @@ -17,7 +17,7 @@ #define ACCESS_TABLE_HASH 30197 static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE]; - + static inline UINT32 hash(const char* mode) { @@ -237,6 +237,7 @@ ImagingAccessInit() ADD("RGBX", line_32, get_pixel_32, put_pixel_32); ADD("CMYK", line_32, get_pixel_32, put_pixel_32); ADD("YCbCr", line_32, get_pixel_32, put_pixel_32); + ADD("LAB", line_32, get_pixel_32, put_pixel_32); } ImagingAccess diff --git a/libImaging/AlphaComposite.c b/libImaging/AlphaComposite.c new file mode 100644 index 000000000..9433fae53 --- /dev/null +++ b/libImaging/AlphaComposite.c @@ -0,0 +1,98 @@ +/* + * The Python Imaging Library + * $Id$ + * + * Alpha composite imSrc over imDst. + * http://en.wikipedia.org/wiki/Alpha_compositing + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + + +typedef struct +{ + UINT8 r; + UINT8 g; + UINT8 b; + UINT8 a; +} rgba8; + + + +Imaging +ImagingAlphaComposite(Imaging imDst, Imaging imSrc) +{ + Imaging imOut; + int x, y; + + /* Check arguments */ + if (!imDst || !imSrc || + strcmp(imDst->mode, "RGBA") || + imDst->type != IMAGING_TYPE_UINT8 || + imDst->bands != 4) + return ImagingError_ModeError(); + + if (strcmp(imDst->mode, imSrc->mode) || + imDst->type != imSrc->type || + imDst->bands != imSrc->bands || + imDst->xsize != imSrc->xsize || + imDst->ysize != imSrc->ysize) + return ImagingError_Mismatch(); + + imOut = ImagingNew(imDst->mode, imDst->xsize, imDst->ysize); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, imDst); + + for (y = 0; y < imDst->ysize; y++) { + + rgba8* dst = (rgba8*) imDst->image[y]; + rgba8* src = (rgba8*) imSrc->image[y]; + rgba8* out = (rgba8*) imOut->image[y]; + + for (x = 0; x < imDst->xsize; x ++) { + + if (src->a == 0) { + // Copy 4 bytes at once. + *out = *dst; + } else { + // Integer implementation with increased precision. + // Each variable has extra meaningful bits. + // Divisions are rounded. + + // This code uses trick from Paste.c: + // (a + (2 << (n-1)) - 1) / ((2 << n)-1) + // almost equivalent to: + // tmp = a + (2 << (n-1)), ((tmp >> n) + tmp) >> n + + UINT32 tmpr, tmpg, tmpb; + UINT16 blend = dst->a * (255 - src->a); + UINT16 outa255 = src->a * 255 + blend; + // There we use 7 bits for precision. + // We could use more, but we go beyond 32 bits. + UINT16 coef1 = src->a * 255 * 255 * 128 / outa255; + UINT16 coef2 = 255 * 128 - coef1; + + #define SHIFTFORDIV255(a)\ + ((((a) >> 8) + a) >> 8) + + tmpr = src->r * coef1 + dst->r * coef2 + (0x80 << 7); + out->r = SHIFTFORDIV255(tmpr) >> 7; + tmpg = src->g * coef1 + dst->g * coef2 + (0x80 << 7); + out->g = SHIFTFORDIV255(tmpg) >> 7; + tmpb = src->b * coef1 + dst->b * coef2 + (0x80 << 7); + out->b = SHIFTFORDIV255(tmpb) >> 7; + out->a = SHIFTFORDIV255(outa255 + 0x80); + } + + dst++; src++; out++; + } + + } + + return imOut; +} diff --git a/libImaging/Antialias.c b/libImaging/Antialias.c index 53dfa3522..d413fbb6a 100644 --- a/libImaging/Antialias.c +++ b/libImaging/Antialias.c @@ -2,7 +2,7 @@ * The Python Imaging Library * $Id$ * - * pilopen antialiasing support + * pilopen antialiasing support * * history: * 2002-03-09 fl Created (for PIL 1.1.3) @@ -133,7 +133,7 @@ ImagingStretch(Imaging imOut, Imaging imIn, int filter) filterscale = 1.0; support = 0.5; } - + support = support * filterscale; /* coefficient buffer (with rounding safety margin) */ diff --git a/libImaging/Bands.c b/libImaging/Bands.c index fdfc6ae95..cc8d634dd 100644 --- a/libImaging/Bands.c +++ b/libImaging/Bands.c @@ -1,7 +1,7 @@ -/* +/* * The Python Imaging Library * $Id$ - * + * * stuff to extract and paste back individual bands * * history: diff --git a/libImaging/BitDecode.c b/libImaging/BitDecode.c index 572926f00..a78183542 100644 --- a/libImaging/BitDecode.c +++ b/libImaging/BitDecode.c @@ -75,7 +75,7 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) bitstate->bitbuffer = (bitstate->bitbuffer << 8) | byte; bitstate->bitcount += 8; - + while (bitstate->bitcount >= bitstate->bits) { /* get a pixel from the bit buffer */ @@ -127,7 +127,7 @@ ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) return -1; } state->x = 0; - /* reset bit buffer */ + /* reset bit buffer */ if (bitstate->pad > 0) bitstate->bitcount = 0; } diff --git a/libImaging/Blend.c b/libImaging/Blend.c index 0861c8ef4..885a1bb82 100644 --- a/libImaging/Blend.c +++ b/libImaging/Blend.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/Chops.c b/libImaging/Chops.c index e5993195a..73d961231 100644 --- a/libImaging/Chops.c +++ b/libImaging/Chops.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 25299aa82..631263b31 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -1,7 +1,7 @@ -/* +/* * The Python Imaging Library * $Id$ - * + * * convert images * * history: @@ -39,8 +39,8 @@ #define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v)) /* like (a * b + 127) / 255), but much faster on most platforms */ -#define MULDIV255(a, b, tmp)\ - (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) +#define MULDIV255(a, b, tmp)\ + (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ #define L(rgb)\ @@ -55,7 +55,7 @@ bit2l(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) - *out++ = (*in++ != 0) ? 255 : 0; + *out++ = (*in++ != 0) ? 255 : 0; } static void @@ -64,10 +64,10 @@ bit2rgb(UINT8* out, const UINT8* in, int xsize) int x; for (x = 0; x < xsize; x++) { UINT8 v = (*in++ != 0) ? 255 : 0; - *out++ = v; - *out++ = v; - *out++ = v; - *out++ = 255; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; } } @@ -76,10 +76,10 @@ bit2cmyk(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) { - *out++ = 0; - *out++ = 0; - *out++ = 0; - *out++ = (*in++ != 0) ? 0 : 255; + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = (*in++ != 0) ? 0 : 255; } } @@ -88,7 +88,7 @@ bit2ycbcr(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) { - *out++ = (*in++ != 0) ? 255 : 0; + *out++ = (*in++ != 0) ? 255 : 0; *out++ = 128; *out++ = 128; *out++ = 255; @@ -104,7 +104,7 @@ l2bit(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) - *out++ = (*in++ >= 128) ? 255 : 0; + *out++ = (*in++ >= 128) ? 255 : 0; } static void @@ -113,10 +113,10 @@ l2la(UINT8* out, const UINT8* in, int xsize) int x; for (x = 0; x < xsize; x++) { UINT8 v = *in++; - *out++ = v; - *out++ = v; - *out++ = v; - *out++ = 255; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; } } @@ -126,10 +126,10 @@ l2rgb(UINT8* out, const UINT8* in, int xsize) int x; for (x = 0; x < xsize; x++) { UINT8 v = *in++; - *out++ = v; - *out++ = v; - *out++ = v; - *out++ = 255; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; } } @@ -138,7 +138,7 @@ la2l(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) - *out++ = in[0]; + *out++ = in[0]; } static void @@ -147,10 +147,10 @@ la2rgb(UINT8* out, const UINT8* in, int xsize) int x; for (x = 0; x < xsize; x++, in += 4) { UINT8 v = in[0]; - *out++ = v; - *out++ = v; - *out++ = v; - *out++ = in[3]; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = in[3]; } } @@ -159,8 +159,8 @@ rgb2bit(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) - /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ - *out++ = (L(in) >= 128000) ? 255 : 0; + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + *out++ = (L(in) >= 128000) ? 255 : 0; } static void @@ -168,8 +168,8 @@ rgb2l(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) - /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ - *out++ = L(in) / 1000; + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + *out++ = L(in) / 1000; } static void @@ -177,8 +177,8 @@ rgb2la(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4, out += 4) { - /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ - out[0] = out[1] = out[2] = L(in) / 1000; + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + out[0] = out[1] = out[2] = L(in) / 1000; out[3] = 255; } } @@ -189,7 +189,7 @@ rgb2i(UINT8* out_, const UINT8* in, int xsize) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = L(in) / 1000; + *out++ = L(in) / 1000; } static void @@ -198,7 +198,7 @@ rgb2f(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = (float) L(in) / 1000.0F; + *out++ = (float) L(in) / 1000.0F; } static void @@ -207,7 +207,7 @@ rgb2bgr15(UINT8* out_, const UINT8* in, int xsize) int x; UINT16* out = (UINT16*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = + *out++ = ((((UINT16)in[0])<<7)&0x7c00) + ((((UINT16)in[1])<<2)&0x03e0) + ((((UINT16)in[2])>>3)&0x001f); @@ -219,7 +219,7 @@ rgb2bgr16(UINT8* out_, const UINT8* in, int xsize) int x; UINT16* out = (UINT16*) out_; for (x = 0; x < xsize; x++, in += 4) - *out++ = + *out++ = ((((UINT16)in[0])<<8)&0xf800) + ((((UINT16)in[1])<<3)&0x07e0) + ((((UINT16)in[2])>>3)&0x001f); @@ -230,9 +230,9 @@ rgb2bgr24(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) { - *out++ = in[2]; - *out++ = in[1]; - *out++ = in[0]; + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; } } @@ -257,8 +257,8 @@ rgba2la(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4, out += 4) { - /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ - out[0] = out[1] = out[2] = L(in) / 1000; + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + out[0] = out[1] = out[2] = L(in) / 1000; out[3] = in[3]; } } @@ -289,6 +289,57 @@ rgba2rgba(UINT8* out, const UINT8* in, int xsize) } } +/* RGBa -> RGBA conversion to remove premultiplication + Needed for correct transforms/resizing on RGBA images */ +static void +rgba2rgbA(UINT8* out, const UINT8* in, int xsize) +{ + int x; + unsigned int alpha; + for (x = 0; x < xsize; x++, in+=4) { + alpha = in[3]; + if (alpha) { + *out++ = CLIP((255 * in[0]) / alpha); + *out++ = CLIP((255 * in[1]) / alpha); + *out++ = CLIP((255 * in[2]) / alpha); + } else { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + } + *out++ = in[3]; + + } +} + +/* + * Conversion of RGB + single transparent color to RGBA, + * where any pixel that matches the color will have the + * alpha channel set to 0 + */ + +static void +rgbT2rgba(UINT8* out, int xsize, int r, int g, int b) +{ +#ifdef WORDS_BIGENDIAN + UINT32 trns = ((r & 0xff)<<24) | ((g & 0xff)<<16) | ((b & 0xff)<<8) | 0xff; + UINT32 repl = trns & 0xffffff00; +#else + UINT32 trns = (0xff <<24) | ((b & 0xff)<<16) | ((g & 0xff)<<8) | (r & 0xff); + UINT32 repl = trns & 0x00ffffff; +#endif + + UINT32* tmp = (UINT32 *)out; + int i; + + for (i=0; i < xsize; i++ ,tmp++) { + if (tmp[0]==trns) { + tmp[0]=repl; + } + } +} + + /* ---------------- */ /* CMYK conversions */ /* ---------------- */ @@ -310,7 +361,7 @@ rgb2cmyk(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) { - /* Note: no undercolour removal */ + /* Note: no undercolour removal */ *out++ = ~(*in++); *out++ = ~(*in++); *out++ = ~(*in++); @@ -324,9 +375,9 @@ cmyk2rgb(UINT8* out, const UINT8* in, int xsize) int x; for (x = 0; x < xsize; x++, in += 4) { *out++ = CLIP(255 - (in[0] + in[3])); - *out++ = CLIP(255 - (in[1] + in[3])); - *out++ = CLIP(255 - (in[2] + in[3])); - *out++ = 255; + *out++ = CLIP(255 - (in[1] + in[3])); + *out++ = CLIP(255 - (in[2] + in[3])); + *out++ = 255; } } @@ -340,7 +391,7 @@ bit2i(UINT8* out_, const UINT8* in, int xsize) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (*in++ != 0) ? 255 : 0; + *out++ = (*in++ != 0) ? 255 : 0; } static void @@ -349,7 +400,7 @@ l2i(UINT8* out_, const UINT8* in, int xsize) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + *out++ = (INT32) *in++; } static void @@ -374,7 +425,7 @@ i2f(UINT8* out_, const UINT8* in_, int xsize) INT32* in = (INT32*) in_; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (FLOAT32) *in++; + *out++ = (FLOAT32) *in++; } /* ------------- */ @@ -387,7 +438,7 @@ bit2f(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (*in++ != 0) ? 255.0F : 0.0F; + *out++ = (*in++ != 0) ? 255.0F : 0.0F; } static void @@ -396,7 +447,7 @@ l2f(UINT8* out_, const UINT8* in, int xsize) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (FLOAT32) *in++; + *out++ = (FLOAT32) *in++; } static void @@ -421,7 +472,7 @@ f2i(UINT8* out_, const UINT8* in_, int xsize) FLOAT32* in = (FLOAT32*) in_; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (INT32) *in++; + *out++ = (INT32) *in++; } /* ----------------- */ @@ -435,10 +486,10 @@ l2ycbcr(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++) { - *out++ = *in++; - *out++ = 128; - *out++ = 128; - *out++ = 255; + *out++ = *in++; + *out++ = 128; + *out++ = 128; + *out++ = 255; } } @@ -447,7 +498,7 @@ ycbcr2l(UINT8* out, const UINT8* in, int xsize) { int x; for (x = 0; x < xsize; x++, in += 4) - *out++ = in[0]; + *out++ = in[0]; } /* ------------------------- */ @@ -498,6 +549,25 @@ I16B_I(UINT8* out_, const UINT8* in, int xsize) *out++ = in[1] + ((int) in[0] << 8); } +static void +I16L_F(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++, in += 2) + *out++ = (FLOAT32) (in[0] + ((int) in[1] << 8)); +} + + +static void +I16B_F(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++, in += 2) + *out++ = (FLOAT32) (in[1] + ((int) in[0] << 8)); +} + static void L_I16L(UINT8* out, const UINT8* in, int xsize) { @@ -600,6 +670,8 @@ static struct { { "RGBA", "CMYK", rgb2cmyk }, { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr }, + { "RGBa", "RGBA", rgba2rgbA }, + { "RGBX", "1", rgb2bit }, { "RGBX", "L", rgb2l }, { "RGBA", "I", rgb2i }, @@ -630,6 +702,10 @@ static struct { { "L", "I;16B", L_I16B }, { "I;16B", "L", I16B_L }, + { "I;16", "F", I16L_F }, + { "I;16L", "F", I16L_F }, + { "I;16B", "F", I16B_F }, + { NULL } }; @@ -645,7 +721,7 @@ p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++) - *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0; + *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0; } static void @@ -654,7 +730,7 @@ p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++) - *out++ = L(&palette[in[x]*4]) / 1000; + *out++ = L(&palette[in[x]*4]) / 1000; } static void @@ -663,7 +739,7 @@ pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) int x; /* FIXME: precalculate greyscale palette? */ for (x = 0; x < xsize; x++, in += 2) { - *out++ = L(&palette[in[0]*4]) / 1000; + *out++ = L(&palette[in[0]*4]) / 1000; *out++ = in[1]; } } @@ -674,7 +750,7 @@ p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; INT32* out = (INT32*) out_; for (x = 0; x < xsize; x++) - *out++ = L(&palette[in[x]*4]) / 1000; + *out++ = L(&palette[in[x]*4]) / 1000; } static void @@ -683,7 +759,7 @@ p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) int x; FLOAT32* out = (FLOAT32*) out_; for (x = 0; x < xsize; x++) - *out++ = (float) L(&palette[in[x]*4]) / 1000.0F; + *out++ = (float) L(&palette[in[x]*4]) / 1000.0F; } static void @@ -691,11 +767,11 @@ p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; for (x = 0; x < xsize; x++) { - const UINT8* rgb = &palette[*in++ * 4]; - *out++ = rgb[0]; - *out++ = rgb[1]; - *out++ = rgb[2]; - *out++ = 255; + const UINT8* rgb = &palette[*in++ * 4]; + *out++ = rgb[0]; + *out++ = rgb[1]; + *out++ = rgb[2]; + *out++ = 255; } } @@ -704,11 +780,11 @@ p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; for (x = 0; x < xsize; x++) { - const UINT8* rgba = &palette[*in++ * 4]; - *out++ = rgba[0]; - *out++ = rgba[1]; - *out++ = rgba[2]; - *out++ = rgba[3]; + const UINT8* rgba = &palette[*in++ * 4]; + *out++ = rgba[0]; + *out++ = rgba[1]; + *out++ = rgba[2]; + *out++ = rgba[3]; } } @@ -717,11 +793,11 @@ pa2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) { int x; for (x = 0; x < xsize; x++, in += 4) { - const UINT8* rgb = &palette[in[0] * 4]; - *out++ = rgb[0]; - *out++ = rgb[1]; - *out++ = rgb[2]; - *out++ = in[3]; + const UINT8* rgb = &palette[in[0] * 4]; + *out++ = rgb[0]; + *out++ = rgb[1]; + *out++ = rgb[2]; + *out++ = in[3]; } } @@ -750,32 +826,32 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) /* Map palette image to L, RGB, RGBA, or CMYK */ if (!imIn->palette) - return (Imaging) ImagingError_ValueError("no palette"); + return (Imaging) ImagingError_ValueError("no palette"); alpha = !strcmp(imIn->mode, "PA"); if (strcmp(mode, "1") == 0) - convert = p2bit; + convert = p2bit; else if (strcmp(mode, "L") == 0) - convert = p2l; + convert = p2l; else if (strcmp(mode, "LA") == 0) - convert = (alpha) ? pa2la : p2l; + convert = (alpha) ? pa2la : p2l; else if (strcmp(mode, "I") == 0) - convert = p2i; + convert = p2i; else if (strcmp(mode, "F") == 0) - convert = p2f; + convert = p2f; else if (strcmp(mode, "RGB") == 0) - convert = p2rgb; + convert = p2rgb; else if (strcmp(mode, "RGBA") == 0) - convert = (alpha) ? pa2rgba : p2rgba; + convert = (alpha) ? pa2rgba : p2rgba; else if (strcmp(mode, "RGBX") == 0) - convert = p2rgba; + convert = p2rgba; else if (strcmp(mode, "CMYK") == 0) - convert = p2cmyk; + convert = p2cmyk; else if (strcmp(mode, "YCbCr") == 0) - convert = p2ycbcr; + convert = p2ycbcr; else - return (Imaging) ImagingError_ValueError("conversion not supported"); + return (Imaging) ImagingError_ValueError("conversion not supported"); imOut = ImagingNew2(mode, imOut, imIn); if (!imOut) @@ -783,13 +859,16 @@ frompalette(Imaging imOut, Imaging imIn, const char *mode) ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) - (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], - imIn->xsize, imIn->palette->palette); + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize, imIn->palette->palette); ImagingSectionLeave(&cookie); return imOut; } +#if defined(_MSC_VER) && (_MSC_VER == 1600) +#pragma optimize("", off) +#endif static Imaging topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) { @@ -799,23 +878,23 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) /* Map L or RGB/RGBX/RGBA to palette image */ if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) - return (Imaging) ImagingError_ValueError("conversion not supported"); + return (Imaging) ImagingError_ValueError("conversion not supported"); if (palette == NULL) { /* FIXME: make user configurable */ if (imIn->bands == 1) - palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */ + palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */ else - palette = ImagingPaletteNewBrowser(); /* Standard colour cube */ + palette = ImagingPaletteNewBrowser(); /* Standard colour cube */ } if (!palette) - return (Imaging) ImagingError_ValueError("no palette"); + return (Imaging) ImagingError_ValueError("no palette"); imOut = ImagingNew2("P", imOut, imIn); if (!imOut) { if (palette != inpalette) - ImagingPaletteDelete(palette); + ImagingPaletteDelete(palette); return NULL; } @@ -823,24 +902,24 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) imOut->palette = ImagingPaletteDuplicate(palette); if (imIn->bands == 1) { - /* greyscale image */ + /* greyscale image */ - /* Greyscale palette: copy data as is */ + /* Greyscale palette: copy data as is */ ImagingSectionEnter(&cookie); - for (y = 0; y < imIn->ysize; y++) - memcpy(imOut->image[y], imIn->image[y], imIn->linesize); + for (y = 0; y < imIn->ysize; y++) + memcpy(imOut->image[y], imIn->image[y], imIn->linesize); ImagingSectionLeave(&cookie); } else { - /* colour image */ + /* colour image */ - /* Create mapping cache */ - if (ImagingPaletteCachePrepare(palette) < 0) { - ImagingDelete(imOut); - if (palette != inpalette) - ImagingPaletteDelete(palette); - return NULL; - } + /* Create mapping cache */ + if (ImagingPaletteCachePrepare(palette) < 0) { + ImagingDelete(imOut); + if (palette != inpalette) + ImagingPaletteDelete(palette); + return NULL; + } if (dither) { /* floyd-steinberg dither */ @@ -929,8 +1008,8 @@ topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) ImagingSectionLeave(&cookie); } - if (inpalette != palette) - ImagingPaletteCacheDelete(palette); + if (inpalette != palette) + ImagingPaletteCacheDelete(palette); } if (inpalette != palette) @@ -948,7 +1027,7 @@ tobilevel(Imaging imOut, Imaging imIn, int dither) /* Map L or RGB to dithered 1 image */ if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) - return (Imaging) ImagingError_ValueError("conversion not supported"); + return (Imaging) ImagingError_ValueError("conversion not supported"); imOut = ImagingNew2("1", imOut, imIn); if (!imOut) @@ -1022,7 +1101,9 @@ tobilevel(Imaging imOut, Imaging imIn, int dither) return imOut; } - +#if defined(_MSC_VER) && (_MSC_VER == 1600) +#pragma optimize("", on) +#endif static Imaging convert(Imaging imOut, Imaging imIn, const char *mode, @@ -1033,29 +1114,29 @@ convert(Imaging imOut, Imaging imIn, const char *mode, int y; if (!imIn) - return (Imaging) ImagingError_ModeError(); + return (Imaging) ImagingError_ModeError(); if (!mode) { - /* Map palette image to full depth */ - if (!imIn->palette) - return (Imaging) ImagingError_ModeError(); - mode = imIn->palette->mode; + /* Map palette image to full depth */ + if (!imIn->palette) + return (Imaging) ImagingError_ModeError(); + mode = imIn->palette->mode; } else - /* Same mode? */ - if (!strcmp(imIn->mode, mode)) - return ImagingCopy2(imOut, imIn); + /* Same mode? */ + if (!strcmp(imIn->mode, mode)) + return ImagingCopy2(imOut, imIn); /* test for special conversions */ if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) - return frompalette(imOut, imIn, mode); - + return frompalette(imOut, imIn, mode); + if (strcmp(mode, "P") == 0) - return topalette(imOut, imIn, palette, dither); + return topalette(imOut, imIn, palette, dither); if (dither && strcmp(mode, "1") == 0) - return tobilevel(imOut, imIn, dither); + return tobilevel(imOut, imIn, dither); /* standard conversion machinery */ @@ -1063,15 +1144,15 @@ convert(Imaging imOut, Imaging imIn, const char *mode, convert = NULL; for (y = 0; converters[y].from; y++) - if (!strcmp(imIn->mode, converters[y].from) && - !strcmp(mode, converters[y].to)) { - convert = converters[y].convert; - break; - } + if (!strcmp(imIn->mode, converters[y].from) && + !strcmp(mode, converters[y].to)) { + convert = converters[y].convert; + break; + } if (!convert) #ifdef notdef - return (Imaging) ImagingError_ValueError("conversion not supported"); + return (Imaging) ImagingError_ValueError("conversion not supported"); #else { static char buf[256]; @@ -1087,8 +1168,8 @@ convert(Imaging imOut, Imaging imIn, const char *mode, ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) - (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], - imIn->xsize); + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize); ImagingSectionLeave(&cookie); return imOut; @@ -1107,6 +1188,60 @@ ImagingConvert2(Imaging imOut, Imaging imIn) return convert(imOut, imIn, imOut->mode, NULL, 0); } + +Imaging +ImagingConvertTransparent(Imaging imIn, const char *mode, + int r, int g, int b) +{ + ImagingSectionCookie cookie; + ImagingShuffler convert; + Imaging imOut = NULL; + int y; + + if (!imIn){ + return (Imaging) ImagingError_ModeError(); + } + + if (!((strcmp(imIn->mode, "RGB") == 0 || + strcmp(imIn->mode, "L") == 0) + && strcmp(mode, "RGBA") == 0)) +#ifdef notdef + { + return (Imaging) ImagingError_ValueError("conversion not supported"); + } +#else + { + static char buf[256]; + /* FIXME: may overflow if mode is too large */ + sprintf(buf, "conversion from %s to %s not supported in convert_transparent", imIn->mode, mode); + return (Imaging) ImagingError_ValueError(buf); + } +#endif + + if (strcmp(imIn->mode, "RGB") == 0) { + convert = rgb2rgba; + } else { + convert = l2rgb; + g = b = r; + } + + imOut = ImagingNew2(mode, imOut, imIn); + if (!imOut){ + return NULL; + } + + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize); + rgbT2rgba((UINT8*) imOut->image[y], imIn->xsize, r, g, b); + } + ImagingSectionLeave(&cookie); + + return imOut; + +} + Imaging ImagingConvertInPlace(Imaging imIn, const char* mode) { @@ -1121,10 +1256,10 @@ ImagingConvertInPlace(Imaging imIn, const char* mode) convert = bit2l; else return ImagingError_ModeError(); - + ImagingSectionEnter(&cookie); for (y = 0; y < imIn->ysize; y++) - (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y], + (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y], imIn->xsize); ImagingSectionLeave(&cookie); diff --git a/libImaging/ConvertYCbCr.c b/libImaging/ConvertYCbCr.c index 40cd01780..6ce549111 100644 --- a/libImaging/ConvertYCbCr.c +++ b/libImaging/ConvertYCbCr.c @@ -352,7 +352,7 @@ ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels) y = (Y_R[r] + Y_G[g] + Y_B[b]) >> SCALE; cb = ((Cb_R[r] + Cb_G[g] + Cb_B[b]) >> SCALE) + 128; cr = ((Cr_R[r] + Cr_G[g] + Cr_B[b]) >> SCALE) + 128; - + out[0] = (UINT8) y; out[1] = (UINT8) cb; out[2] = (UINT8) cr; diff --git a/libImaging/Copy.c b/libImaging/Copy.c index 4a1fc2489..b5b0b0aea 100644 --- a/libImaging/Copy.c +++ b/libImaging/Copy.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/Crop.c b/libImaging/Crop.c index 5acacd4c4..0db67e8e9 100644 --- a/libImaging/Crop.c +++ b/libImaging/Crop.c @@ -26,7 +26,7 @@ ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) int xsize, ysize; int dx0, dy0, dx1, dy1; INT32 zero = 0; - + if (!imIn) return (Imaging) ImagingError_ModeError(); diff --git a/libImaging/Dib.c b/libImaging/Dib.c index 36dc24ef3..6db8c076e 100644 --- a/libImaging/Dib.c +++ b/libImaging/Dib.c @@ -126,7 +126,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) /* Bind a palette to it as well (only required for 8-bit DIBs) */ if (dib->pixelsize == 1) { for (i = 0; i < 256; i++) { - palette[i].rgbRed = + palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = i; palette[i].rgbReserved = 0; @@ -181,7 +181,7 @@ ImagingNewDIB(const char *mode, int xsize, int ysize) i++; } for (r = 1; r < 22-1; r++) { - /* Black and white are already provided by the cube. */ + /* Black and white are already provided by the cube. */ pal->palPalEntry[i].peRed = pal->palPalEntry[i].peGreen = pal->palPalEntry[i].peBlue = r * 255 / (22-1); diff --git a/libImaging/Draw.c b/libImaging/Draw.c index bdf17e18a..f13ba4df0 100644 --- a/libImaging/Draw.c +++ b/libImaging/Draw.c @@ -436,7 +436,7 @@ polygon8(Imaging im, int n, Edge *e, int ink, int eofill) for (;ymin <= ymax; ymin++) { y = ymin+0.5F; - for (i = j = 0; i < n; i++) + for (i = j = 0; i < n; i++) if (y >= e[i].ymin && y <= e[i].ymax) { if (e[i].d == 0) hline8(im, e[i].xmin, ymin, e[i].xmax, ink); @@ -590,7 +590,7 @@ add_edge(Edge *e, int x0, int y0, int x1, int y1) e->ymin = y0, e->ymax = y1; else e->ymin = y1, e->ymax = y0; - + if (y0 == y1) { e->d = 0; e->dx = 0.0; @@ -777,7 +777,7 @@ ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_, draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink); } - + return 0; } @@ -861,7 +861,7 @@ ellipse(Imaging im, int x0, int y0, int x1, int y1, } free(e); - + } else { for (i = start; i <= end; i++) { @@ -1017,7 +1017,7 @@ ImagingOutlineLine(ImagingOutline outline, float x1, float y1) outline->x = x1; outline->y = y1; - + return 0; } @@ -1061,13 +1061,13 @@ ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, outline->x = xo; outline->y = yo; - + return 0; } int ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy, - float x3, float y3) + float x3, float y3) { /* add bezier curve based on three control points (as in the Flash file format) */ @@ -1095,13 +1095,13 @@ ImagingOutlineTransform(ImagingOutline outline, double a[6]) int i, n; int x0, y0, x1, y1; int X0, Y0, X1, Y1; - + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; eIn = outline->edges; n = outline->count; - + /* FIXME: ugly! */ outline->edges = NULL; outline->count = outline->size = 0; @@ -1113,12 +1113,12 @@ ImagingOutlineTransform(ImagingOutline outline, double a[6]) ImagingError_MemoryError(); return -1; } - + for (i = 0; i < n; i++) { - + x0 = eIn->x0; y0 = eIn->y0; - + /* FIXME: ouch! */ if (eIn->x0 == eIn->xmin) x1 = eIn->xmax; diff --git a/libImaging/Effects.c b/libImaging/Effects.c index 1b964ac40..db6e72989 100644 --- a/libImaging/Effects.c +++ b/libImaging/Effects.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/EpsEncode.c b/libImaging/EpsEncode.c index 704d5a656..45fab0a6e 100644 --- a/libImaging/EpsEncode.c +++ b/libImaging/EpsEncode.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library. * $Id$ * @@ -76,5 +76,5 @@ ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } return ptr - buf; - + } diff --git a/libImaging/Except.c b/libImaging/Except.c index 635435d57..515d85d1f 100644 --- a/libImaging/Except.c +++ b/libImaging/Except.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/File.c b/libImaging/File.c index 6f454d1ca..ac9ec3be1 100644 --- a/libImaging/File.c +++ b/libImaging/File.c @@ -57,7 +57,7 @@ ImagingOpenPPM(const char* infile) x = y = max = 0; - while (i < 3) { + while (i < 3) { /* Ignore optional comment fields */ while (c == '\n') { diff --git a/libImaging/Fill.c b/libImaging/Fill.c index ce00e6c15..1e8dbf9d8 100644 --- a/libImaging/Fill.c +++ b/libImaging/Fill.c @@ -24,6 +24,7 @@ Imaging ImagingFill(Imaging im, const void* colour) { int x, y; + ImagingSectionCookie cookie; if (im->type == IMAGING_TYPE_SPECIAL) { /* use generic API */ @@ -40,6 +41,7 @@ ImagingFill(Imaging im, const void* colour) } } else { INT32 c = 0L; + ImagingSectionEnter(&cookie); memcpy(&c, colour, im->pixelsize); if (im->image32 && c != 0L) { for (y = 0; y < im->ysize; y++) @@ -50,6 +52,7 @@ ImagingFill(Imaging im, const void* colour) for (y = 0; y < im->ysize; y++) memset(im->image[y], cc, im->linesize); } + ImagingSectionLeave(&cookie); } return im; diff --git a/libImaging/Filter.c b/libImaging/Filter.c index 45c9bf060..9079fbf88 100644 --- a/libImaging/Filter.c +++ b/libImaging/Filter.c @@ -31,7 +31,7 @@ ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) { Imaging imOut; int x, y; - + if (xmargin < 0 && ymargin < 0) return (Imaging) ImagingError_ValueError("bad kernel size"); diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c index 41f535b56..0f59ee0d5 100644 --- a/libImaging/Geometry.c +++ b/libImaging/Geometry.c @@ -583,7 +583,7 @@ getfilter(Imaging im, int filterid) Imaging ImagingTransform( - Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, ImagingTransformMap transform, void* transform_data, ImagingTransformFilter filter, void* filter_data, int fill) diff --git a/libImaging/GetBBox.c b/libImaging/GetBBox.c index d1722fe14..3cfa42c48 100644 --- a/libImaging/GetBBox.c +++ b/libImaging/GetBBox.c @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * @@ -224,7 +224,7 @@ getcolors32(Imaging im, int maxcolors, int* size) 33554432,9, 67108864,71, 134217728,39, 268435456,9, 536870912,5, 1073741824,83, 0 }; - + code_size = code_poly = code_mask = 0; for (i = 0; SIZES[i]; i += 2) { diff --git a/libImaging/GifDecode.c b/libImaging/GifDecode.c index 6fb05b564..1b9206b29 100644 --- a/libImaging/GifDecode.c +++ b/libImaging/GifDecode.c @@ -75,7 +75,7 @@ ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) state->errcode = IMAGING_CODEC_CONFIG; return -1; } - + /* Clear code */ context->clear = 1 << context->bits; diff --git a/libImaging/GifEncode.c b/libImaging/GifEncode.c index f4d07598f..4ada55496 100644 --- a/libImaging/GifEncode.c +++ b/libImaging/GifEncode.c @@ -44,7 +44,7 @@ emit(GIFENCODERSTATE *context, int byte) /* no room in the current block (or no current block); allocate a new one */ - + /* add current block to end of flush queue */ if (context->block) { block = context->flush; diff --git a/libImaging/Histo.c b/libImaging/Histo.c index 513c84475..0bfc8dfe9 100644 --- a/libImaging/Histo.c +++ b/libImaging/Histo.c @@ -41,7 +41,7 @@ ImagingHistogramNew(Imaging im) /* Create histogram descriptor */ h = calloc(1, sizeof(struct ImagingHistogramInstance)); - strcpy(h->mode, im->mode); + strncpy(h->mode, im->mode, IMAGING_MODE_LENGTH); h->bands = im->bands; h->histogram = calloc(im->pixelsize, 256 * sizeof(long)); diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h index 2effd3870..23c660488 100644 --- a/libImaging/ImDib.h +++ b/libImaging/ImDib.h @@ -14,6 +14,10 @@ #if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__) /* already defined in basetsd.h */ +#undef INT8 +#undef UINT8 +#undef INT16 +#undef UINT16 #undef INT32 #undef INT64 #undef UINT32 diff --git a/libImaging/ImPlatform.h b/libImaging/ImPlatform.h index 4ffd6fdef..7fc5cbdca 100644 --- a/libImaging/ImPlatform.h +++ b/libImaging/ImPlatform.h @@ -1,4 +1,4 @@ -/* +/* * The Python Imaging Library * $Id$ * diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index 4609376ad..b45dcbe23 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -1,7 +1,7 @@ /* * The Python Imaging Library * $Id$ - * + * * declarations for the imaging core library * * Copyright (c) 1997-2005 by Secret Labs AB @@ -40,6 +40,7 @@ extern "C" { * RGBA 4 R, G, B, A * CMYK 4 C, M, Y, K * YCbCr 4 Y, Cb, Cr, - + * Lab 4 L, a, b, - * * experimental modes (incomplete): * LA 4 L, -, -, A @@ -72,10 +73,12 @@ typedef struct ImagingPaletteInstance* ImagingPalette; #define IMAGING_TYPE_FLOAT32 2 #define IMAGING_TYPE_SPECIAL 3 /* check mode for details */ +#define IMAGING_MODE_LENGTH 6+1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ + struct ImagingMemoryInstance { /* Format */ - char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ + char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */ int type; /* Data type (IMAGING_TYPE_*) */ int depth; /* Depth (ignored in this version) */ int bands; /* Number of bands (1, 2, 3, or 4) */ @@ -127,7 +130,7 @@ struct ImagingAccessInstance { struct ImagingHistogramInstance { /* Format */ - char mode[4+1]; /* Band names (of corresponding source image) */ + char mode[IMAGING_MODE_LENGTH]; /* Band names (of corresponding source image) */ int bands; /* Number of bands (1, 3, or 4) */ /* Data */ @@ -139,7 +142,7 @@ struct ImagingHistogramInstance { struct ImagingPaletteInstance { /* Format */ - char mode[4+1]; /* Band names */ + char mode[IMAGING_MODE_LENGTH]; /* Band names */ /* Data */ UINT8 palette[1024];/* Palette data (same format as image data) */ @@ -239,11 +242,13 @@ typedef int (*ImagingTransformFilter)(void* out, Imaging im, /* Image Manipulation Methods */ /* -------------------------- */ +extern Imaging ImagingAlphaComposite(Imaging imIn1, Imaging imIn2); extern Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha); extern Imaging ImagingCopy(Imaging im); extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither); extern Imaging ImagingConvertInPlace(Imaging im, const char* mode); extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]); +extern Imaging ImagingConvertTransparent(Imaging im, const char *mode, int r, int g, int b); extern Imaging ImagingCrop(Imaging im, int x0, int y0, int x1, int y1); extern Imaging ImagingExpand(Imaging im, int x, int y, int mode); extern Imaging ImagingFill(Imaging im, const void* ink); @@ -288,16 +293,16 @@ extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter); extern Imaging ImagingTransformPerspective( - Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill); extern Imaging ImagingTransformAffine( - Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[6], int filter, int fill); extern Imaging ImagingTransformQuad( - Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, double a[8], int filter, int fill); extern Imaging ImagingTransform( - Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, ImagingTransformMap transform, void* transform_data, ImagingTransformFilter filter, void* filter_data, int fill); @@ -370,7 +375,7 @@ extern int ImagingOutlineLine(ImagingOutline outline, float x, float y); extern int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, float x2, float y2, float x3, float y3); extern int ImagingOutlineTransform(ImagingOutline outline, double a[6]); - + extern int ImagingOutlineClose(ImagingOutline outline); /* Special effects */ @@ -414,11 +419,19 @@ extern int ImagingHexDecode(Imaging im, ImagingCodecState state, #ifdef HAVE_LIBJPEG extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); +extern int ImagingJpegDecodeCleanup(ImagingCodecState state); + extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); #endif extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); +#ifdef HAVE_LIBTIFF +extern int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#endif #ifdef HAVE_LIBMPEG extern int ImagingMpegDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes); diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h index d39165f3c..0b8c5cf9a 100644 --- a/libImaging/Jpeg.h +++ b/libImaging/Jpeg.h @@ -88,6 +88,9 @@ typedef struct { /* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */ int subsampling; + /* Custom quantization tables () */ + unsigned int **qtables; + /* Extra data (to be injected after header) */ char* extra; int extra_size; @@ -101,4 +104,7 @@ typedef struct { int extra_offset; + int rawExifLen; /* EXIF data length */ + char* rawExif; /* EXIF buffer pointer */ + } JPEGENCODERSTATE; diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c index 11b2767ba..6ebdb8f93 100644 --- a/libImaging/JpegDecode.c +++ b/libImaging/JpegDecode.c @@ -15,8 +15,8 @@ * 2000-10-12 fl Suppress warnings * 2000-12-04 fl Suppress errors beyond end of image data * - * Copyright (c) 1998-2000 Secret Labs AB - * Copyright (c) 1996-2000 Fredrik Lundh + * Copyright (c) 1998-2000 Secret Labs AB + * Copyright (c) 1996-2000 Fredrik Lundh * * See the README file for details on usage and redistribution. */ @@ -24,11 +24,11 @@ #include "Imaging.h" -#ifdef HAVE_LIBJPEG +#ifdef HAVE_LIBJPEG -#undef HAVE_PROTOTYPES -#undef HAVE_STDLIB_H -#undef HAVE_STDDEF_H +#undef HAVE_PROTOTYPES +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H #undef UINT8 #undef UINT16 #undef UINT32 @@ -39,7 +39,7 @@ /* -------------------------------------------------------------------- */ -/* Suspending input handler */ +/* Suspending input handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -61,16 +61,16 @@ skip_input_data(j_decompress_ptr cinfo, long num_bytes) JPEGSOURCE* source = (JPEGSOURCE*) cinfo->src; if (num_bytes > (long) source->pub.bytes_in_buffer) { - /* We need to skip more data than we have in the buffer. - This will force the JPEG library to suspend decoding. */ - source->skip = num_bytes - source->pub.bytes_in_buffer; - source->pub.next_input_byte += source->pub.bytes_in_buffer; - source->pub.bytes_in_buffer = 0; + /* We need to skip more data than we have in the buffer. + This will force the JPEG library to suspend decoding. */ + source->skip = num_bytes - source->pub.bytes_in_buffer; + source->pub.next_input_byte += source->pub.bytes_in_buffer; + source->pub.bytes_in_buffer = 0; } else { - /* Skip portion of the buffer */ - source->pub.bytes_in_buffer -= num_bytes; - source->pub.next_input_byte += num_bytes; - source->skip = 0; + /* Skip portion of the buffer */ + source->pub.bytes_in_buffer -= num_bytes; + source->pub.next_input_byte += num_bytes; + source->skip = 0; } } @@ -93,7 +93,7 @@ jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source) /* -------------------------------------------------------------------- */ -/* Error handler */ +/* Error handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -111,7 +111,7 @@ output(j_common_ptr cinfo) } /* -------------------------------------------------------------------- */ -/* Decoder */ +/* Decoder */ /* -------------------------------------------------------------------- */ int @@ -121,23 +121,23 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) int ok; if (setjmp(context->error.setjmp_buffer)) { - /* JPEG error handler */ - jpeg_destroy_decompress(&context->cinfo); - state->errcode = IMAGING_CODEC_BROKEN; - return -1; + /* JPEG error handler */ + jpeg_destroy_decompress(&context->cinfo); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; } if (!state->state) { - /* Setup decompression context */ - context->cinfo.err = jpeg_std_error(&context->error.pub); - context->error.pub.error_exit = error; - context->error.pub.output_message = output; - jpeg_create_decompress(&context->cinfo); - jpeg_buffer_src(&context->cinfo, &context->source); + /* Setup decompression context */ + context->cinfo.err = jpeg_std_error(&context->error.pub); + context->error.pub.error_exit = error; + context->error.pub.output_message = output; + jpeg_create_decompress(&context->cinfo); + jpeg_buffer_src(&context->cinfo, &context->source); - /* Ready to decode */ - state->state = 1; + /* Ready to decode */ + state->state = 1; } @@ -146,122 +146,137 @@ ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) context->source.pub.bytes_in_buffer = bytes; if (context->source.skip > 0) { - skip_input_data(&context->cinfo, context->source.skip); - if (context->source.skip > 0) - return context->source.pub.next_input_byte - buf; + skip_input_data(&context->cinfo, context->source.skip); + if (context->source.skip > 0) + return context->source.pub.next_input_byte - buf; } switch (state->state) { case 1: - /* Read JPEG header, until we find an image body. */ - do { - - /* Note that we cannot return unless we have decoded - as much data as possible. */ - ok = jpeg_read_header(&context->cinfo, FALSE); + /* Read JPEG header, until we find an image body. */ + do { - } while (ok == JPEG_HEADER_TABLES_ONLY); + /* Note that we cannot return unless we have decoded + as much data as possible. */ + ok = jpeg_read_header(&context->cinfo, FALSE); - if (ok == JPEG_SUSPENDED) - break; + } while (ok == JPEG_HEADER_TABLES_ONLY); - /* Decoder settings */ + if (ok == JPEG_SUSPENDED) + break; - /* jpegmode indicates whats in the file; if not set, we'll - trust the decoder */ - if (strcmp(context->jpegmode, "L") == 0) - context->cinfo.jpeg_color_space = JCS_GRAYSCALE; - else if (strcmp(context->jpegmode, "RGB") == 0) - context->cinfo.jpeg_color_space = JCS_RGB; - else if (strcmp(context->jpegmode, "CMYK") == 0) - context->cinfo.jpeg_color_space = JCS_CMYK; - else if (strcmp(context->jpegmode, "YCbCr") == 0) - context->cinfo.jpeg_color_space = JCS_YCbCr; - else if (strcmp(context->jpegmode, "YCbCrK") == 0) { - context->cinfo.jpeg_color_space = JCS_YCCK; - } + /* Decoder settings */ - /* rawmode indicates what we want from the decoder. if not - set, conversions are disabled */ - if (strcmp(context->rawmode, "L") == 0) - context->cinfo.out_color_space = JCS_GRAYSCALE; - else if (strcmp(context->rawmode, "RGB") == 0) - context->cinfo.out_color_space = JCS_RGB; - else if (strcmp(context->rawmode, "CMYK") == 0 || + /* jpegmode indicates whats in the file; if not set, we'll + trust the decoder */ + if (strcmp(context->jpegmode, "L") == 0) + context->cinfo.jpeg_color_space = JCS_GRAYSCALE; + else if (strcmp(context->jpegmode, "RGB") == 0) + context->cinfo.jpeg_color_space = JCS_RGB; + else if (strcmp(context->jpegmode, "CMYK") == 0) + context->cinfo.jpeg_color_space = JCS_CMYK; + else if (strcmp(context->jpegmode, "YCbCr") == 0) + context->cinfo.jpeg_color_space = JCS_YCbCr; + else if (strcmp(context->jpegmode, "YCbCrK") == 0) { + context->cinfo.jpeg_color_space = JCS_YCCK; + } + + /* rawmode indicates what we want from the decoder. if not + set, conversions are disabled */ + if (strcmp(context->rawmode, "L") == 0) + context->cinfo.out_color_space = JCS_GRAYSCALE; + else if (strcmp(context->rawmode, "RGB") == 0) + context->cinfo.out_color_space = JCS_RGB; + else if (strcmp(context->rawmode, "CMYK") == 0 || strcmp(context->rawmode, "CMYK;I") == 0) - context->cinfo.out_color_space = JCS_CMYK; - else if (strcmp(context->rawmode, "YCbCr") == 0) - context->cinfo.out_color_space = JCS_YCbCr; - else if (strcmp(context->rawmode, "YCbCrK") == 0) - context->cinfo.out_color_space = JCS_YCCK; - else { - /* Disable decoder conversions */ - context->cinfo.jpeg_color_space = JCS_UNKNOWN; - context->cinfo.out_color_space = JCS_UNKNOWN; - } + context->cinfo.out_color_space = JCS_CMYK; + else if (strcmp(context->rawmode, "YCbCr") == 0) + context->cinfo.out_color_space = JCS_YCbCr; + else if (strcmp(context->rawmode, "YCbCrK") == 0) + context->cinfo.out_color_space = JCS_YCCK; + else { + /* Disable decoder conversions */ + context->cinfo.jpeg_color_space = JCS_UNKNOWN; + context->cinfo.out_color_space = JCS_UNKNOWN; + } - if (context->scale > 1) { - context->cinfo.scale_num = 1; - context->cinfo.scale_denom = context->scale; - } - if (context->draft) { - context->cinfo.do_fancy_upsampling = FALSE; - context->cinfo.dct_method = JDCT_FASTEST; - } + if (context->scale > 1) { + context->cinfo.scale_num = 1; + context->cinfo.scale_denom = context->scale; + } + if (context->draft) { + context->cinfo.do_fancy_upsampling = FALSE; + context->cinfo.dct_method = JDCT_FASTEST; + } state->state++; - /* fall through */ + /* fall through */ case 2: /* Set things up for decompression (this processes the entire file if necessary to return data line by line) */ - if (!jpeg_start_decompress(&context->cinfo)) + if (!jpeg_start_decompress(&context->cinfo)) break; - - state->state++; - /* fall through */ + + state->state++; + /* fall through */ case 3: - /* Decompress a single line of data */ - ok = 1; - while (state->y < state->ysize) { - ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); - if (ok != 1) - break; - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, state->buffer, - state->xsize); - state->y++; - } - if (ok != 1) - break; - state->state++; - /* fall through */ + /* Decompress a single line of data */ + ok = 1; + while (state->y < state->ysize) { + ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); + if (ok != 1) + break; + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + state->y++; + } + if (ok != 1) + break; + state->state++; + /* fall through */ case 4: - /* Finish decompression */ - if (!jpeg_finish_decompress(&context->cinfo)) { + /* Finish decompression */ + if (!jpeg_finish_decompress(&context->cinfo)) { /* FIXME: add strictness mode test */ if (state->y < state->ysize) break; } - /* Clean up */ - jpeg_destroy_decompress(&context->cinfo); - /* if (jerr.pub.num_warnings) return BROKEN; */ - return -1; + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + /* if (jerr.pub.num_warnings) return BROKEN; */ + return -1; } /* Return number of bytes consumed */ - return context->source.pub.next_input_byte - buf; + return context->source.pub.next_input_byte - buf; } +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + +int ImagingJpegDecodeCleanup(ImagingCodecState state){ + /* called to fee the decompression engine when the decode terminates + due to a corrupt or truncated image + */ + JPEGSTATE* context = (JPEGSTATE*) state->context; + + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + return -1; +} + #endif diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c index 1e2191a69..711b201e0 100644 --- a/libImaging/JpegEncode.c +++ b/libImaging/JpegEncode.c @@ -22,11 +22,11 @@ #include "Imaging.h" -#ifdef HAVE_LIBJPEG +#ifdef HAVE_LIBJPEG -#undef HAVE_PROTOTYPES -#undef HAVE_STDLIB_H -#undef HAVE_STDDEF_H +#undef HAVE_PROTOTYPES +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H #undef UINT8 #undef UINT16 #undef UINT32 @@ -36,7 +36,7 @@ #include "Jpeg.h" /* -------------------------------------------------------------------- */ -/* Suspending output handler */ +/* Suspending output handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) @@ -64,16 +64,16 @@ jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination) /* -------------------------------------------------------------------- */ -/* Error handler */ +/* Error handler */ /* -------------------------------------------------------------------- */ METHODDEF(void) error(j_common_ptr cinfo) { - JPEGERROR* error; - error = (JPEGERROR*) cinfo->err; - (*cinfo->err->output_message) (cinfo); - longjmp(error->setjmp_buffer, 1); + JPEGERROR* error; + error = (JPEGERROR*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(error->setjmp_buffer, 1); } @@ -143,48 +143,62 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) /* Compressor configuration */ jpeg_set_defaults(&context->cinfo); - if (context->quality > 0) + + /* Use custom quantization tables */ + if (context->qtables) { + int i; + int quality = 100; + if (context->quality > 0) { + quality = context->quality; + } + for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) { + // TODO: Should add support for none baseline + jpeg_add_quant_table(&context->cinfo, i, context->qtables[i], + quality, TRUE); + } + } else if (context->quality > 0) { jpeg_set_quality(&context->cinfo, context->quality, 1); - + } + /* Set subsampling options */ switch (context->subsampling) - { - case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ + { + case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ { - context->cinfo.comp_info[0].h_samp_factor = 1; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 1; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ + case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 1; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ + case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ { - context->cinfo.comp_info[0].h_samp_factor = 2; - context->cinfo.comp_info[0].v_samp_factor = 2; - context->cinfo.comp_info[1].h_samp_factor = 1; - context->cinfo.comp_info[1].v_samp_factor = 1; - context->cinfo.comp_info[2].h_samp_factor = 1; - context->cinfo.comp_info[2].v_samp_factor = 1; - break; + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 2; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; } - default: + default: { - /* Use the lib's default */ - break; + /* Use the lib's default */ + break; } - } + } if (context->progressive) jpeg_simple_progression(&context->cinfo); context->cinfo.smoothing_factor = context->smooth; @@ -215,6 +229,19 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) /* fall through */ case 2: + // check for exif len + 'APP1' header bytes + if (context->rawExifLen + 5 > context->destination.pub.free_in_buffer){ + break; + } + //add exif header + if (context->rawExifLen > 0){ + jpeg_write_marker(&context->cinfo, JPEG_APP0+1, + (unsigned char*)context->rawExif, context->rawExifLen); + } + + state->state++; + /* fall through */ + case 3: if (context->extra) { /* copy extra buffer to output buffer */ @@ -231,9 +258,12 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) else break; } else - state->state++; + state->state++; - case 3: + case 4: + if (1024 > context->destination.pub.free_in_buffer){ + break; + } ok = 1; while (state->y < state->ysize) { @@ -251,7 +281,7 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) state->state++; /* fall through */ - case 4: + case 5: /* Finish compression */ if (context->destination.pub.free_in_buffer < 100) diff --git a/libImaging/ModeFilter.c b/libImaging/ModeFilter.c index ad11d87d4..b1fc7e8e6 100644 --- a/libImaging/ModeFilter.c +++ b/libImaging/ModeFilter.c @@ -69,7 +69,7 @@ ImagingModeFilter(Imaging im, int size) out[x] = IMAGING_PIXEL_L(im, x, y); } - + } ImagingCopyInfo(imOut, im); diff --git a/libImaging/Pack.c b/libImaging/Pack.c index 478de7499..1cc1f3a94 100644 --- a/libImaging/Pack.c +++ b/libImaging/Pack.c @@ -361,6 +361,27 @@ packI16B(UINT8* out, const UINT8* in_, int pixels) } } +static void +packI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16B; + out += 2; tmp += 2; + } + +} +static void +packI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C16L; + out += 2; tmp += 2; + } +} + + static void packI32S(UINT8* out, const UINT8* in, int pixels) { @@ -372,6 +393,19 @@ packI32S(UINT8* out, const UINT8* in, int pixels) } } +void +ImagingPackLAB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LAB triplets */ + for (i = 0; i < pixels; i++) { + out[0] = in[0]; + out[1] = in[1] ^ 128; /* signed in outside world */ + out[2] = in[2] ^ 128; + out += 3; in += 4; + } +} + static void copy1(UINT8* out, const UINT8* in, int pixels) { @@ -526,6 +560,12 @@ static struct { {"YCbCr", "Cb", 8, band1}, {"YCbCr", "Cr", 8, band2}, + /* LAB Color */ + {"LAB", "LAB", 24, ImagingPackLAB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, + /* integer */ {"I", "I", 32, copy4}, {"I", "I;16B", 16, packI16B}, @@ -541,6 +581,9 @@ static struct { {"I;16", "I;16", 16, copy2}, {"I;16B", "I;16B", 16, copy2}, {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, packI16N_I16}, + {"I;16B", "I;16N", 16, packI16N_I16B}, {"BGR;15", "BGR;15", 16, copy2}, {"BGR;16", "BGR;16", 16, copy2}, {"BGR;24", "BGR;24", 24, copy3}, diff --git a/libImaging/Palette.c b/libImaging/Palette.c index bd4f4f1d6..9b0dd57c4 100644 --- a/libImaging/Palette.c +++ b/libImaging/Palette.c @@ -37,7 +37,7 @@ ImagingPaletteNew(const char* mode) if (!palette) return (ImagingPalette) ImagingError_MemoryError(); - strcpy(palette->mode, mode); + strncpy(palette->mode, mode, IMAGING_MODE_LENGTH); /* Initialize to ramp */ for (i = 0; i < 256; i++) { diff --git a/libImaging/Paste.c b/libImaging/Paste.c index b89dd6404..d27b10fdb 100644 --- a/libImaging/Paste.c +++ b/libImaging/Paste.c @@ -7,7 +7,7 @@ * history: * 96-03-27 fl Created * 96-07-16 fl Support "1", "L" and "RGBA" masks - * 96-08-16 fl Merged with opaque paste + * 96-08-16 fl Merged with opaque paste * 97-01-17 fl Faster blending, added support for RGBa images * 97-08-27 fl Faster masking for 32-bit images * 98-02-02 fl Fixed MULDIV255 macro for gcc @@ -209,7 +209,7 @@ paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask, } } } - + int ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, int dx0, int dy0, int dx1, int dy1) @@ -310,7 +310,7 @@ fill(Imaging imOut, const void* ink_, int dx, int dy, xsize *= pixelsize; for (y = 0; y < ysize; y++) memset(imOut->image[y+dy]+dx, ink8, xsize); - + } else { for (y = 0; y < ysize; y++) { diff --git a/libImaging/PcdDecode.c b/libImaging/PcdDecode.c index b6898e301..fb6adc688 100644 --- a/libImaging/PcdDecode.c +++ b/libImaging/PcdDecode.c @@ -37,7 +37,7 @@ ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) for (;;) { - /* We need data for two full lines before we can do anything */ + /* We need data for two full lines before we can do anything */ if (bytes < chunk) return ptr - buf; diff --git a/libImaging/PcxDecode.c b/libImaging/PcxDecode.c index ab82b23ad..04c86cb35 100644 --- a/libImaging/PcxDecode.c +++ b/libImaging/PcxDecode.c @@ -36,7 +36,7 @@ ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) return ptr - buf; n = ptr[0] & 0x3F; - + while (n > 0) { if (state->x >= state->bytes) { state->errcode = IMAGING_CODEC_OVERRUN; diff --git a/libImaging/Point.c b/libImaging/Point.c index 2593dca57..53d797e58 100644 --- a/libImaging/Point.c +++ b/libImaging/Point.c @@ -207,8 +207,8 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) Imaging imOut; int x, y; - if (!imIn || (strcmp(imIn->mode, "I") != 0 && - strcmp(imIn->mode, "I;16") != 0 && + if (!imIn || (strcmp(imIn->mode, "I") != 0 && + strcmp(imIn->mode, "I;16") != 0 && strcmp(imIn->mode, "F") != 0)) return (Imaging) ImagingError_ModeError(); diff --git a/libImaging/Quant.c b/libImaging/Quant.c index 1b76a6a0f..5b8a8d994 100644 --- a/libImaging/Quant.c +++ b/libImaging/Quant.c @@ -11,7 +11,7 @@ * 2005-02-07 fl Limit number of colors to 256 * * Written by Toby J Sargeant . - * + * * Copyright (c) 1998 by Toby J Sargeant * Copyright (c) 1998-2004 by Secret Labs AB. All rights reserved. * @@ -25,16 +25,15 @@ #include #include -#include "Quant.h" - -#include "QuantDefines.h" +#include "QuantTypes.h" +#include "QuantOctree.h" #include "QuantHash.h" #include "QuantHeap.h" #define NO_OUTPUT typedef struct { - unsigned long scale; + uint32_t scale; } PixelHashData; typedef struct _PixelList { @@ -49,7 +48,7 @@ typedef struct _BoxNode { PixelList *head[3],*tail[3]; int axis; int volume; - unsigned long pixelCount; + uint32_t pixelCount; } BoxNode; #define _SQR(x) ((x)*(x)) @@ -75,104 +74,92 @@ typedef struct _BoxNode { ((q)->c.g=(p)->c.g>>(s)), \ ((q)->c.b=(p)->c.b>>(s)) -static unsigned long -unshifted_pixel_hash(const HashTable h, const void *p) +static uint32_t +unshifted_pixel_hash(const HashTable *h, const Pixel pixel) { - Pixel *pixel=(Pixel *)&p; - unsigned long hash=PIXEL_HASH(pixel->c.r, - pixel->c.g, - pixel->c.b); - return hash; + return PIXEL_HASH(pixel.c.r, pixel.c.g, pixel.c.b); } static int -unshifted_pixel_cmp(const HashTable h, const void *a, const void *b) +unshifted_pixel_cmp(const HashTable *h, const Pixel pixel1, const Pixel pixel2) { - Pixel *pixel1=(Pixel *)&a; - Pixel *pixel2=(Pixel *)&b; - if (pixel1->c.r==pixel2->c.r) { - if (pixel1->c.g==pixel2->c.g) { - if (pixel1->c.b==pixel2->c.b) { + if (pixel1.c.r==pixel2.c.r) { + if (pixel1.c.g==pixel2.c.g) { + if (pixel1.c.b==pixel2.c.b) { return 0; } else { - return (int)(pixel1->c.b)-(int)(pixel2->c.b); + return (int)(pixel1.c.b)-(int)(pixel2.c.b); } } else { - return (int)(pixel1->c.g)-(int)(pixel2->c.g); + return (int)(pixel1.c.g)-(int)(pixel2.c.g); } } else { - return (int)(pixel1->c.r)-(int)(pixel2->c.r); + return (int)(pixel1.c.r)-(int)(pixel2.c.r); } } -static unsigned long -pixel_hash(const HashTable h,const void *p) +static uint32_t +pixel_hash(const HashTable *h,const Pixel pixel) { PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); - Pixel *pixel=(Pixel *)&p; - unsigned long hash=PIXEL_HASH(pixel->c.r>>d->scale, - pixel->c.g>>d->scale, - pixel->c.b>>d->scale); - return hash; + return PIXEL_HASH(pixel.c.r>>d->scale, pixel.c.g>>d->scale, pixel.c.b>>d->scale); } static int -pixel_cmp(const HashTable h,const void *a,const void *b) +pixel_cmp(const HashTable *h,const Pixel pixel1, const Pixel pixel2) { PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); - Pixel *pixel1=(Pixel *)&a; - Pixel *pixel2=(Pixel *)&b; - unsigned long A,B; - A=PIXEL_HASH(pixel1->c.r>>d->scale, - pixel1->c.g>>d->scale, - pixel1->c.b>>d->scale); - B=PIXEL_HASH(pixel2->c.r>>d->scale, - pixel2->c.g>>d->scale, - pixel2->c.b>>d->scale); + uint32_t A,B; + A=PIXEL_HASH(pixel1.c.r>>d->scale, pixel1.c.g>>d->scale, pixel1.c.b>>d->scale); + B=PIXEL_HASH(pixel2.c.r>>d->scale, pixel2.c.g>>d->scale, pixel2.c.b>>d->scale); return (A==B)?0:((Ascale=0; +#ifndef NO_OUTPUT timer=timer3=clock(); +#endif for (i=0;iscale++; #ifndef NO_OUTPUT printf ("rehashing - new scale: %d\n",(int)d->scale); -#endif timer2=clock(); - hashtable_rehash_compute(hash,rehash_collide); - timer2=clock()-timer2; -#ifndef NO_OUTPUT - printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC); #endif + hashtable_rehash_compute(hash,rehash_collide); +#ifndef NO_OUTPUT + timer2=clock()-timer2; + printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC); timer+=timer2; +#endif } } #ifndef NO_OUTPUT @@ -200,7 +187,7 @@ create_pixel_hash(Pixel *pixelData,unsigned long nPixels) } static void -destroy_pixel_hash(HashTable hash) +destroy_pixel_hash(HashTable *hash) { PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash); if (d) free(d); @@ -236,21 +223,19 @@ compute_box_volume(BoxNode *b) } static void -hash_to_list(HashTable h, const void *key, const void *val, void *u) +hash_to_list(const HashTable *h, const Pixel pixel, const uint32_t count, void *u) { PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); PixelList **pl=(PixelList **)u; PixelList *p; - Pixel *pixel=(Pixel *)&key; int i; Pixel q; - int count=*(int *)&val; - PIXEL_SCALE(pixel,&q,d->scale); + PIXEL_SCALE(&pixel,&q,d->scale); p=malloc(sizeof(PixelList)); if (!p) return; - + p->flag=0; p->p=q; p->count=count; @@ -326,7 +311,7 @@ test_sorted(PixelList *pl[3]) #endif static int -box_heap_cmp(const Heap h, const void *A, const void *B) +box_heap_cmp(const Heap *h, const void *A, const void *B) { BoxNode *a=(BoxNode *)A; BoxNode *b=(BoxNode *)B; @@ -340,11 +325,11 @@ splitlists(PixelList *h[3], PixelList *t[3], PixelList *nh[2][3], PixelList *nt[2][3], - unsigned long nCount[2], + uint32_t nCount[2], int axis, - unsigned long pixelCount) + uint32_t pixelCount) { - unsigned long left; + uint32_t left; PixelList *l,*r,*c,*n; int i; @@ -475,7 +460,7 @@ split(BoxNode *node) int i; PixelList *heads[2][3]; PixelList *tails[2][3]; - unsigned long newCounts[2]; + uint32_t newCounts[2]; BoxNode *left,*right; rh=node->head[0]->p.c.r; @@ -499,7 +484,7 @@ split(BoxNode *node) #ifdef TEST_SPLIT printf ("along axis %d\n",axis+1); #endif - + #ifdef TEST_SPLIT { PixelList *_prevTest,*_nextTest; @@ -617,13 +602,13 @@ split(BoxNode *node) static BoxNode * median_cut(PixelList *hl[3], - unsigned long imPixelCount, + uint32_t imPixelCount, int nPixels) { PixelList *tl[3]; int i; BoxNode *root; - Heap h; + Heap* h; BoxNode *thisNode; h=ImagingQuantHeapNew(box_heap_cmp); @@ -700,7 +685,7 @@ checkContained(BoxNode *n,Pixel *pp) #endif static int -annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box) +annotate_hash_table(BoxNode *n,HashTable *h,uint32_t *box) { PixelList *p; PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); @@ -716,7 +701,7 @@ annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box) } for (p=n->head[0];p;p=p->next[0]) { PIXEL_UNSCALE(&(p->p),&q,d->scale); - if (!hashtable_insert(h,(void *)q.v,(void *)*box)) { + if (!hashtable_insert(h,q,*box)) { #ifndef NO_OUTPUT printf ("hashtable insert failed\n"); #endif @@ -730,20 +715,20 @@ annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box) static int _sort_ulong_ptr_keys(const void *a, const void *b) { - unsigned long A=**(unsigned long **)a; - unsigned long B=**(unsigned long **)b; + uint32_t A=**(uint32_t **)a; + uint32_t B=**(uint32_t **)b; return (A==B)?0:((A=nPaletteEntries) { @@ -1090,35 +1075,35 @@ compute_palette_from_quantized_pixels( static int k_means(Pixel *pixelData, - unsigned long nPixels, + uint32_t nPixels, Pixel *paletteData, - unsigned long nPaletteEntries, - unsigned long *qp, + uint32_t nPaletteEntries, + uint32_t *qp, int threshold) { - unsigned long *avg[3]; - unsigned long *count; - unsigned long i; - unsigned long *avgDist; - unsigned long **avgDistSortKey; + uint32_t *avg[3]; + uint32_t *count; + uint32_t i; + uint32_t *avgDist; + uint32_t **avgDistSortKey; int changes; int built=0; - - if (!(count=malloc(sizeof(unsigned long)*nPaletteEntries))) { + + if (!(count=malloc(sizeof(uint32_t)*nPaletteEntries))) { return 0; } for(i=0;i<3;i++) { avg[i]=NULL; } for(i=0;i<3;i++) { - if (!(avg[i]=malloc(sizeof(unsigned long)*nPaletteEntries))) { + if (!(avg[i]=malloc(sizeof(uint32_t)*nPaletteEntries))) { goto error_1; } } - avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries); + avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries); if (!avgDist) { goto error_1; } - avgDistSortKey=malloc(sizeof(unsigned long *)*nPaletteEntries*nPaletteEntries); + avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries); if (!avgDistSortKey) { goto error_2; } #ifndef NO_OUTPUT @@ -1171,26 +1156,26 @@ error_1: int quantize(Pixel *pixelData, - unsigned long nPixels, - unsigned long nQuantPixels, + uint32_t nPixels, + uint32_t nQuantPixels, Pixel **palette, - unsigned long *paletteLength, - unsigned long **quantizedPixels, + uint32_t *paletteLength, + uint32_t **quantizedPixels, int kmeans) { PixelList *hl[3]; - HashTable h; + HashTable *h; BoxNode *root; - unsigned long i; - unsigned long *qp; - unsigned long nPaletteEntries; - - unsigned long *avgDist; - unsigned long **avgDistSortKey; + uint32_t i; + uint32_t *qp; + uint32_t nPaletteEntries; + + uint32_t *avgDist; + uint32_t **avgDistSortKey; Pixel *p; - + #ifndef NO_OUTPUT - unsigned long timer,timer2; + uint32_t timer,timer2; #endif #ifndef NO_OUTPUT @@ -1265,13 +1250,13 @@ quantize(Pixel *pixelData, free_box_tree(root); root=NULL; - qp=malloc(sizeof(unsigned long)*nPixels); + qp=malloc(sizeof(uint32_t)*nPixels); if (!qp) { goto error_4; } - avgDist=malloc(sizeof(unsigned long)*nPaletteEntries*nPaletteEntries); + avgDist=malloc(sizeof(uint32_t)*nPaletteEntries*nPaletteEntries); if (!avgDist) { goto error_5; } - avgDistSortKey=malloc(sizeof(unsigned long *)*nPaletteEntries*nPaletteEntries); + avgDistSortKey=malloc(sizeof(uint32_t *)*nPaletteEntries*nPaletteEntries); if (!avgDistSortKey) { goto error_6; } if (!build_distance_tables(avgDist,avgDistSortKey,p,nPaletteEntries)) { @@ -1285,12 +1270,12 @@ quantize(Pixel *pixelData, #ifdef TEST_NEAREST_NEIGHBOUR #include { - unsigned long bestmatch,bestdist,dist; - HashTable h2; + uint32_t bestmatch,bestdist,dist; + HashTable *h2; printf ("nearest neighbour search (full search)..."); fflush(stdout); timer=clock(); h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); for (i=0;inew),pixel); + uint32_t oldDist=*dist; + uint32_t newDist; + newDist=_DISTSQR(&(data->new),&pixel); if (data->secondPixel || newDistdata->furthestDistance) { data->furthestDistance=oldDist; - data->furthest.v=pixel->v; + data->furthest.v=pixel.v; } } int quantize2(Pixel *pixelData, - unsigned long nPixels, - unsigned long nQuantPixels, + uint32_t nPixels, + uint32_t nQuantPixels, Pixel **palette, - unsigned long *paletteLength, - unsigned long **quantizedPixels, + uint32_t *paletteLength, + uint32_t **quantizedPixels, int kmeans) { - HashTable h; - unsigned long i; - unsigned long mean[3]; + HashTable *h; + uint32_t i; + uint32_t mean[3]; Pixel *p; DistanceData data; - unsigned long *qp; - unsigned long *avgDist; - unsigned long **avgDistSortKey; + uint32_t *qp; + uint32_t *avgDist; + uint32_t **avgDistSortKey; p=malloc(sizeof(Pixel)*nQuantPixels); if (!p) return 0; mean[0]=mean[1]=mean[2]=0; h=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); for (i=0;imode, "L") != 0 && strcmp(im->mode, "P") != 0 && - strcmp(im->mode, "RGB")) + strcmp(im->mode, "RGB") != 0 && strcmp(im->mode, "RGBA") !=0) return ImagingError_ModeError(); + /* only octree supports RGBA */ + if (!strcmp(im->mode, "RGBA") && mode != 2) + return ImagingError_ModeError(); + p = malloc(sizeof(Pixel) * im->xsize * im->ysize); if (!p) return ImagingError_MemoryError(); @@ -1529,7 +1519,7 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) p[i].c.b = pp[v*4+2]; } - } else if (!strcmp(im->mode, "RGB")) { + } else if (!strcmp(im->mode, "RGB") || !strcmp(im->mode, "RGBA")) { /* true colour */ for (i = y = 0; y < im->ysize; y++) @@ -1541,6 +1531,8 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) return (Imaging) ImagingError_ValueError("internal error"); } + ImagingSectionEnter(&cookie); + switch (mode) { case 0: /* median cut */ @@ -1566,16 +1558,31 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) kmeans ); break; + case 2: + if (!strcmp(im->mode, "RGBA")) { + withAlpha = 1; + } + result = quantize_octree( + p, + im->xsize*im->ysize, + colors, + &palette, + &paletteLength, + &newData, + withAlpha + ); + break; default: result = 0; break; } free(p); + ImagingSectionLeave(&cookie); if (result) { - imOut = ImagingNew("P", im->xsize, im->ysize); + ImagingSectionEnter(&cookie); for (i = y = 0; y < im->ysize; y++) for (x=0; x < im->xsize; x++) @@ -1589,7 +1596,11 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) *pp++ = palette[i].c.r; *pp++ = palette[i].c.g; *pp++ = palette[i].c.b; - *pp++ = 255; + if (withAlpha) { + *pp++ = palette[i].c.a; + } else { + *pp++ = 255; + } } for (; i < 256; i++) { *pp++ = 0; @@ -1598,7 +1609,12 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) *pp++ = 255; } + if (withAlpha) { + strcpy(imOut->palette->mode, "RGBA"); + } + free(palette); + ImagingSectionLeave(&cookie); return imOut; diff --git a/libImaging/Quant.h b/libImaging/Quant.h deleted file mode 100644 index 0de485a1a..000000000 --- a/libImaging/Quant.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * The Python Imaging Library - * $Id$ - * - * image quantizer - * - * Written by Toby J Sargeant . - * - * See the README file for information on usage and redistribution. - */ - -#ifndef __QUANT_H__ -#define __QUANT_H__ - -typedef union { - struct { - unsigned char r,g,b,a; - } c; - struct { - unsigned char v[4]; - } a; - unsigned long v; -} Pixel; - -int quantize(Pixel *, - unsigned long, - unsigned long, - Pixel **, - unsigned long *, - unsigned long **, - int); - -int quantize2(Pixel *, - unsigned long, - unsigned long, - Pixel **, - unsigned long *, - unsigned long **, - int); -#endif diff --git a/libImaging/QuantDefines.h b/libImaging/QuantDefines.h deleted file mode 100644 index 5b080104e..000000000 --- a/libImaging/QuantDefines.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * The Python Imaging Library - * $Id$ - * - * image quantizer - * - * Written by Toby J Sargeant . - * - * See the README file for information on usage and redistribution. - */ - -#ifndef __DEFINES_H__ -#define __DEFINES_H__ - -#if 0 - -void *newMalloc(size_t,const char *,const char *,int); -void newFree(void *,const char *,const char *,int); -void print_malloc_stats(); -#define malloc(x) newMalloc(x,__FILE__,__FUNCTION__,__LINE__) -#define free(x) newFree(x,__FILE__,__FUNCTION__,__LINE__) - -#endif - -#endif diff --git a/libImaging/QuantHash.c b/libImaging/QuantHash.c index e6438402d..a1f99ed3a 100644 --- a/libImaging/QuantHash.c +++ b/libImaging/QuantHash.c @@ -22,35 +22,35 @@ #include #include "QuantHash.h" -#include "QuantDefines.h" -typedef struct _IntHashNode { - struct _IntHashNode *next; - void *key,*value; -} IntHashNode; +typedef struct _HashNode { + struct _HashNode *next; + HashKey_t key; + HashVal_t value; +} HashNode; -typedef struct _IntHashTable { - IntHashNode **table; - unsigned long length; - unsigned long count; +struct _HashTable { + HashNode **table; + uint32_t length; + uint32_t count; HashFunc hashFunc; HashCmpFunc cmpFunc; - DestroyFunc keyDestroyFunc; - DestroyFunc valDestroyFunc; + KeyDestroyFunc keyDestroyFunc; + ValDestroyFunc valDestroyFunc; void *userData; -} IntHashTable; +}; #define MIN_LENGTH 11 #define RESIZE_FACTOR 3 -static int _hashtable_insert_node(IntHashTable *,IntHashNode *,int,int,CollisionFunc); +static int _hashtable_insert_node(HashTable *,HashNode *,int,int,CollisionFunc); #if 0 -static int _hashtable_test(IntHashTable *); +static int _hashtable_test(HashTable *); #endif -HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) { - IntHashTable *h; - h=malloc(sizeof(IntHashTable)); +HashTable *hashtable_new(HashFunc hf,HashCmpFunc cf) { + HashTable *h; + h=malloc(sizeof(HashTable)); if (!h) { return NULL; } h->hashFunc=hf; h->cmpFunc=cf; @@ -59,25 +59,24 @@ HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) { h->length=MIN_LENGTH; h->count=0; h->userData=NULL; - h->table=malloc(sizeof(IntHashNode *)*h->length); + h->table=malloc(sizeof(HashNode *)*h->length); if (!h->table) { free(h); return NULL; } - memset (h->table,0,sizeof(IntHashNode *)*h->length); - return (HashTable)h; + memset (h->table,0,sizeof(HashNode *)*h->length); + return h; } -static void _hashtable_destroy(HashTable H,const void *key,const void *val,void *u) { - IntHashTable *h=(IntHashTable *)H; - if (h->keyDestroyFunc&&key) { - h->keyDestroyFunc((HashTable)h,(void *)key); +static void _hashtable_destroy(const HashTable *h,const HashKey_t key,const HashVal_t val,void *u) { + if (h->keyDestroyFunc) { + h->keyDestroyFunc(h,key); } - if (h->valDestroyFunc&&val) { - h->valDestroyFunc((HashTable)h,(void *)val); + if (h->valDestroyFunc) { + h->valDestroyFunc(h,val); } } -static unsigned long _findPrime(unsigned long start,int dir) { +static uint32_t _findPrime(uint32_t start,int dir) { static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0}; - unsigned long t; + uint32_t t; while (start>1) { if (!unit[start&0x0f]) { start+=dir; @@ -94,22 +93,20 @@ static unsigned long _findPrime(unsigned long start,int dir) { return start; } -static void _hashtable_rehash(IntHashTable *h, - CollisionFunc cf, - unsigned long newSize) { - IntHashNode **oldTable=h->table; - unsigned long i; - IntHashNode *n,*nn; - unsigned long oldSize; +static void _hashtable_rehash(HashTable *h,CollisionFunc cf,uint32_t newSize) { + HashNode **oldTable=h->table; + uint32_t i; + HashNode *n,*nn; + uint32_t oldSize; oldSize=h->length; - h->table=malloc(sizeof(IntHashNode *)*newSize); + h->table=malloc(sizeof(HashNode *)*newSize); if (!h->table) { h->table=oldTable; return; } h->length=newSize; h->count=0; - memset (h->table,0,sizeof(IntHashNode *)*h->length); + memset (h->table,0,sizeof(HashNode *)*h->length); for (i=0;inext; @@ -119,9 +116,9 @@ static void _hashtable_rehash(IntHashTable *h, free(oldTable); } -static void _hashtable_resize(IntHashTable *h) { - unsigned long newSize; - unsigned long oldSize; +static void _hashtable_resize(HashTable *h) { + uint32_t newSize; + uint32_t oldSize; oldSize=h->length; newSize=oldSize; if (h->count*RESIZE_FACTORlength) { @@ -136,13 +133,13 @@ static void _hashtable_resize(IntHashTable *h) { } #if 0 -static int _hashtable_test(IntHashTable *h) { - unsigned long i; +static int _hashtable_test(HashTable *h) { + uint32_t i; int j; - IntHashNode *n; + HashNode *n; for (i=0;ilength;i++) { for (n=h->table[i];n&&n->next;n=n->next) { - j=h->cmpFunc((HashTable)h,n->key,n->next->key); + j=h->cmpFunc(h,n->key,n->next->key); printf ("%c",j?(j<0?'-':'+'):'='); } printf ("\n"); @@ -151,26 +148,26 @@ static int _hashtable_test(IntHashTable *h) { } #endif -static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,int update,CollisionFunc cf) { - unsigned long hash=h->hashFunc((HashTable)h,node->key)%h->length; - IntHashNode **n,*nv; +static int _hashtable_insert_node(HashTable *h,HashNode *node,int resize,int update,CollisionFunc cf) { + uint32_t hash=h->hashFunc(h,node->key)%h->length; + HashNode **n,*nv; int i; for (n=&(h->table[hash]);*n;n=&((*n)->next)) { nv=*n; - i=h->cmpFunc((HashTable)h,nv->key,node->key); + i=h->cmpFunc(h,nv->key,node->key); if (!i) { if (cf) { nv->key=node->key; - cf((HashTable)h,&(nv->key),&(nv->value),node->key,node->value); + cf(h,&(nv->key),&(nv->value),node->key,node->value); free(node); return 1; } else { if (h->valDestroyFunc) { - h->valDestroyFunc((HashTable)h,nv->value); + h->valDestroyFunc(h,nv->value); } if (h->keyDestroyFunc) { - h->keyDestroyFunc((HashTable)h,nv->key); + h->keyDestroyFunc(h,nv->key); } nv->key=node->key; nv->value=node->value; @@ -192,17 +189,17 @@ static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,i } } -static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int update) { - IntHashNode **n,*nv; - IntHashNode *t; +static int _hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val,int resize,int update) { + HashNode **n,*nv; + HashNode *t; int i; - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - + uint32_t hash=h->hashFunc(h,key)%h->length; + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { nv=*n; - i=h->cmpFunc((HashTable)h,nv->key,key); + i=h->cmpFunc(h,nv->key,key); if (!i) { - if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,nv->value); } + if (h->valDestroyFunc) { h->valDestroyFunc(h,nv->value); } nv->value=val; return 1; } else if (i>0) { @@ -210,7 +207,7 @@ static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int } } if (!update) { - t=malloc(sizeof(IntHashNode)); + t=malloc(sizeof(HashNode)); if (!t) return 0; t->next=*n; *n=t; @@ -224,15 +221,15 @@ static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int } } -static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,void *newVal,int resize) { - IntHashNode **n,*nv; - IntHashNode *t; +static int _hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *retVal,HashVal_t newVal,int resize) { + HashNode **n,*nv; + HashNode *t; int i; - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - + uint32_t hash=h->hashFunc(h,key)%h->length; + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { nv=*n; - i=h->cmpFunc((HashTable)h,nv->key,key); + i=h->cmpFunc(h,nv->key,key); if (!i) { *retVal=nv->value; return 1; @@ -240,7 +237,7 @@ static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,v break; } } - t=malloc(sizeof(IntHashNode)); + t=malloc(sizeof(HashNode)); if (!t) return 0; t->next=*n; *n=t; @@ -252,26 +249,25 @@ static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,v return 1; } -int hashtable_insert_or_update_computed(HashTable H, - void *key, +int hashtable_insert_or_update_computed(HashTable *h, + HashKey_t key, ComputeFunc newFunc, ComputeFunc existsFunc) { - IntHashTable *h=(IntHashTable *)H; - IntHashNode **n,*nv; - IntHashNode *t; + HashNode **n,*nv; + HashNode *t; int i; - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - + uint32_t hash=h->hashFunc(h,key)%h->length; + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { nv=*n; - i=h->cmpFunc((HashTable)h,nv->key,key); + i=h->cmpFunc(h,nv->key,key); if (!i) { - void *old=nv->value; + HashVal_t old=nv->value; if (existsFunc) { - existsFunc(H,nv->key,&(nv->value)); + existsFunc(h,nv->key,&(nv->value)); if (nv->value!=old) { if (h->valDestroyFunc) { - h->valDestroyFunc((HashTable)h,old); + h->valDestroyFunc(h,old); } } } else { @@ -282,13 +278,13 @@ int hashtable_insert_or_update_computed(HashTable H, break; } } - t=malloc(sizeof(IntHashNode)); + t=malloc(sizeof(HashNode)); if (!t) return 0; t->key=key; t->next=*n; *n=t; if (newFunc) { - newFunc(H,t->key,&(t->value)); + newFunc(h,t->key,&(t->value)); } else { free(t); return 0; @@ -298,52 +294,47 @@ int hashtable_insert_or_update_computed(HashTable H, return 1; } -int hashtable_update(HashTable H,void *key,void *val) { - IntHashTable *h=(IntHashTable *)H; +int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val) { return _hashtable_insert(h,key,val,1,0); } -int hashtable_insert(HashTable H,void *key,void *val) { - IntHashTable *h=(IntHashTable *)H; +int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val) { return _hashtable_insert(h,key,val,1,0); } -void hashtable_foreach_update(HashTable H,IteratorUpdateFunc i,void *u) { - IntHashTable *h=(IntHashTable *)H; - IntHashNode *n; - unsigned long x; +void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u) { + HashNode *n; + uint32_t x; if (h->table) { for (x=0;xlength;x++) { for (n=h->table[x];n;n=n->next) { - i((HashTable)h,n->key,(void **)&(n->value),u); + i(h,n->key,&(n->value),u); } } } } -void hashtable_foreach(HashTable H,IteratorFunc i,void *u) { - IntHashTable *h=(IntHashTable *)H; - IntHashNode *n; - unsigned long x; +void hashtable_foreach(HashTable *h,IteratorFunc i,void *u) { + HashNode *n; + uint32_t x; if (h->table) { for (x=0;xlength;x++) { for (n=h->table[x];n;n=n->next) { - i((HashTable)h,n->key,n->value,u); + i(h,n->key,n->value,u); } } } } -void hashtable_free(HashTable H) { - IntHashTable *h=(IntHashTable *)H; - IntHashNode *n,*nn; - unsigned long i; +void hashtable_free(HashTable *h) { + HashNode *n,*nn; + uint32_t i; if (h->table) { if (h->keyDestroyFunc || h->keyDestroyFunc) { - hashtable_foreach(H,_hashtable_destroy,NULL); + hashtable_foreach(h,_hashtable_destroy,NULL); } for (i=0;ilength;i++) { for (n=h->table[i];n;n=nn) { @@ -356,31 +347,29 @@ void hashtable_free(HashTable H) { free(h); } -DestroyFunc hashtable_set_value_destroy_func(HashTable H,DestroyFunc d) { - IntHashTable *h=(IntHashTable *)H; - DestroyFunc r=h->valDestroyFunc; +ValDestroyFunc hashtable_set_value_destroy_func(HashTable *h,ValDestroyFunc d) { + ValDestroyFunc r=h->valDestroyFunc; h->valDestroyFunc=d; return r; } -DestroyFunc hashtable_set_key_destroy_func(HashTable H,DestroyFunc d) { - IntHashTable *h=(IntHashTable *)H; - DestroyFunc r=h->keyDestroyFunc; +KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *h,KeyDestroyFunc d) { + KeyDestroyFunc r=h->keyDestroyFunc; h->keyDestroyFunc=d; return r; } -static int _hashtable_remove(IntHashTable *h, - const void *key, - void **keyRet, - void **valRet, +static int _hashtable_remove(HashTable *h, + const HashKey_t key, + HashKey_t *keyRet, + HashVal_t *valRet, int resize) { - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - IntHashNode *n,*p; + uint32_t hash=h->hashFunc(h,key)%h->length; + HashNode *n,*p; int i; - + for (p=NULL,n=h->table[hash];n;p=n,n=n->next) { - i=h->cmpFunc((HashTable)h,n->key,key); + i=h->cmpFunc(h,n->key,key); if (!i) { if (p) p=n->next; else h->table[hash]=n->next; *keyRet=n->key; @@ -395,17 +384,17 @@ static int _hashtable_remove(IntHashTable *h, return 0; } -static int _hashtable_delete(IntHashTable *h,const void *key,int resize) { - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - IntHashNode *n,*p; +static int _hashtable_delete(HashTable *h,const HashKey_t key,int resize) { + uint32_t hash=h->hashFunc(h,key)%h->length; + HashNode *n,*p; int i; - + for (p=NULL,n=h->table[hash];n;p=n,n=n->next) { - i=h->cmpFunc((HashTable)h,n->key,key); + i=h->cmpFunc(h,n->key,key); if (!i) { if (p) p=n->next; else h->table[hash]=n->next; - if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,n->value); } - if (h->keyDestroyFunc) { h->keyDestroyFunc((HashTable)h,n->key); } + if (h->valDestroyFunc) { h->valDestroyFunc(h,n->value); } + if (h->keyDestroyFunc) { h->keyDestroyFunc(h,n->key); } free(n); h->count++; return 1; @@ -416,39 +405,33 @@ static int _hashtable_delete(IntHashTable *h,const void *key,int resize) { return 0; } -int hashtable_remove(HashTable H,const void *key,void **keyRet,void **valRet) { - IntHashTable *h=(IntHashTable *)H; +int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet) { return _hashtable_remove(h,key,keyRet,valRet,1); } -int hashtable_delete(HashTable H,const void *key) { - IntHashTable *h=(IntHashTable *)H; +int hashtable_delete(HashTable *h,const HashKey_t key) { return _hashtable_delete(h,key,1); } -void hashtable_rehash_compute(HashTable H,CollisionFunc cf) { - IntHashTable *h=(IntHashTable *)H; +void hashtable_rehash_compute(HashTable *h,CollisionFunc cf) { _hashtable_rehash(h,cf,h->length); } -void hashtable_rehash(HashTable H) { - IntHashTable *h=(IntHashTable *)H; +void hashtable_rehash(HashTable *h) { _hashtable_rehash(h,NULL,h->length); } -int hashtable_lookup_or_insert(HashTable H,void *key,void **valp,void *val) { - IntHashTable *h=(IntHashTable *)H; +int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val) { return _hashtable_lookup_or_insert(h,key,valp,val,1); } -int hashtable_lookup(const HashTable H,const void *key,void **valp) { - IntHashTable *h=(IntHashTable *)H; - unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; - IntHashNode *n; +int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp) { + uint32_t hash=h->hashFunc(h,key)%h->length; + HashNode *n; int i; - + for (n=h->table[hash];n;n=n->next) { - i=h->cmpFunc((HashTable)h,n->key,key); + i=h->cmpFunc(h,n->key,key); if (!i) { *valp=n->value; return 1; @@ -459,18 +442,15 @@ int hashtable_lookup(const HashTable H,const void *key,void **valp) { return 0; } -unsigned long hashtable_get_count(const HashTable H) { - IntHashTable *h=(IntHashTable *)H; +uint32_t hashtable_get_count(const HashTable *h) { return h->count; } -void *hashtable_get_user_data(const HashTable H) { - IntHashTable *h=(IntHashTable *)H; +void *hashtable_get_user_data(const HashTable *h) { return h->userData; } -void *hashtable_set_user_data(HashTable H,void *data) { - IntHashTable *h=(IntHashTable *)H; +void *hashtable_set_user_data(HashTable *h,void *data) { void *r=h->userData; h->userData=data; return r; diff --git a/libImaging/QuantHash.h b/libImaging/QuantHash.h index b98b56eaa..028b4af89 100644 --- a/libImaging/QuantHash.h +++ b/libImaging/QuantHash.h @@ -9,28 +9,41 @@ * See the README file for information on usage and redistribution. */ -#ifndef __HASH_H__ -#define __HASH_H__ +#ifndef __QUANTHASH_H__ +#define __QUANTHASH_H__ #include "QuantTypes.h" -HashTable hashtable_new(HashFunc,HashCmpFunc); -void hashtable_free(HashTable); -void hashtable_foreach(HashTable,IteratorFunc,void *); -void hashtable_foreach_update(HashTable,IteratorUpdateFunc,void *); -int hashtable_insert(HashTable,void *,void *); -int hashtable_update(HashTable,void *,void *); -int hashtable_lookup(const HashTable,const void *,void **); -int hashtable_lookup_or_insert(HashTable,void *,void **,void *); -int hashtable_insert_or_update_computed(HashTable,void *,ComputeFunc,ComputeFunc); -int hashtable_delete(HashTable,const void *); -int hashtable_remove(HashTable,const void *,void **,void **); -void *hashtable_set_user_data(HashTable,void *); -void *hashtable_get_user_data(const HashTable); -DestroyFunc hashtable_set_key_destroy_func(HashTable,DestroyFunc); -DestroyFunc hashtable_set_value_destroy_func(HashTable,DestroyFunc); -unsigned long hashtable_get_count(const HashTable); -void hashtable_rehash(HashTable); -void hashtable_rehash_compute(HashTable,CollisionFunc); +typedef struct _HashTable HashTable; +typedef Pixel HashKey_t; +typedef uint32_t HashVal_t; -#endif +typedef uint32_t (*HashFunc)(const HashTable *,const HashKey_t); +typedef int (*HashCmpFunc)(const HashTable *,const HashKey_t,const HashKey_t); +typedef void (*IteratorFunc)(const HashTable *,const HashKey_t,const HashVal_t,void *); +typedef void (*IteratorUpdateFunc)(const HashTable *,const HashKey_t,HashVal_t *,void *); +typedef void (*KeyDestroyFunc)(const HashTable *,HashKey_t); +typedef void (*ValDestroyFunc)(const HashTable *,HashVal_t); +typedef void (*ComputeFunc)(const HashTable *,const HashKey_t,HashVal_t *); +typedef void (*CollisionFunc)(const HashTable *,HashKey_t *,HashVal_t *,HashKey_t,HashVal_t); + +HashTable * hashtable_new(HashFunc hf,HashCmpFunc cf); +void hashtable_free(HashTable *h); +void hashtable_foreach(HashTable *h,IteratorFunc i,void *u); +void hashtable_foreach_update(HashTable *h,IteratorUpdateFunc i,void *u); +int hashtable_insert(HashTable *h,HashKey_t key,HashVal_t val); +int hashtable_update(HashTable *h,HashKey_t key,HashVal_t val); +int hashtable_lookup(const HashTable *h,const HashKey_t key,HashVal_t *valp); +int hashtable_lookup_or_insert(HashTable *h,HashKey_t key,HashVal_t *valp,HashVal_t val); +int hashtable_insert_or_update_computed(HashTable *h,HashKey_t key,ComputeFunc newFunc,ComputeFunc existsFunc); +int hashtable_delete(HashTable *h,const HashKey_t key); +int hashtable_remove(HashTable *h,const HashKey_t key,HashKey_t *keyRet,HashVal_t *valRet); +void *hashtable_set_user_data(HashTable *h,void *data); +void *hashtable_get_user_data(const HashTable *h); +KeyDestroyFunc hashtable_set_key_destroy_func(HashTable *,KeyDestroyFunc d); +ValDestroyFunc hashtable_set_value_destroy_func(HashTable *,ValDestroyFunc d); +uint32_t hashtable_get_count(const HashTable *h); +void hashtable_rehash(HashTable *h); +void hashtable_rehash_compute(HashTable *h,CollisionFunc cf); + +#endif // __QUANTHASH_H__ diff --git a/libImaging/QuantHeap.c b/libImaging/QuantHeap.c index 9332a5cd7..9006903b7 100644 --- a/libImaging/QuantHeap.c +++ b/libImaging/QuantHeap.c @@ -21,31 +21,29 @@ #include #include -#include "QuantHash.h" -#include "QuantDefines.h" +#include "QuantHeap.h" -typedef struct { +struct _Heap { void **heap; int heapsize; int heapcount; HeapCmpFunc cf; -} IntHeap; +}; #define INITIAL_SIZE 256 -#define DEBUG +// #define DEBUG #ifdef DEBUG -static int _heap_test(Heap); +static int _heap_test(Heap *); #endif -void ImagingQuantHeapFree(Heap H) { - IntHeap *h=(IntHeap *)H; +void ImagingQuantHeapFree(Heap *h) { free(h->heap); free(h); } -static int _heap_grow(IntHeap *h,int newsize) { +static int _heap_grow(Heap *h,int newsize) { void *newheap; if (!newsize) newsize=h->heapsize<<1; if (newsizeheapsize) return 0; @@ -59,15 +57,14 @@ static int _heap_grow(IntHeap *h,int newsize) { } #ifdef DEBUG -static int _heap_test(Heap H) { - IntHeap *h=(IntHeap *)H; +static int _heap_test(Heap *h) { int k; for (k=1;k*2<=h->heapcount;k++) { - if (h->cf(H,h->heap[k],h->heap[k*2])<0) { + if (h->cf(h,h->heap[k],h->heap[k*2])<0) { printf ("heap is bad\n"); return 0; } - if (k*2+1<=h->heapcount && h->cf(H,h->heap[k],h->heap[k*2+1])<0) { + if (k*2+1<=h->heapcount && h->cf(h,h->heap[k],h->heap[k*2+1])<0) { printf ("heap is bad\n"); return 0; } @@ -76,8 +73,7 @@ static int _heap_test(Heap H) { } #endif -int ImagingQuantHeapRemove(Heap H,void **r) { - IntHeap *h=(IntHeap *)H; +int ImagingQuantHeapRemove(Heap* h,void **r) { int k,l; void *v; @@ -89,31 +85,30 @@ int ImagingQuantHeapRemove(Heap H,void **r) { for (k=1;k*2<=h->heapcount;k=l) { l=k*2; if (lheapcount) { - if (h->cf(H,h->heap[l],h->heap[l+1])<0) { + if (h->cf(h,h->heap[l],h->heap[l+1])<0) { l++; } } - if (h->cf(H,v,h->heap[l])>0) { + if (h->cf(h,v,h->heap[l])>0) { break; } h->heap[k]=h->heap[l]; } h->heap[k]=v; #ifdef DEBUG - if (!_heap_test(H)) { printf ("oops - heap_remove messed up the heap\n"); exit(1); } + if (!_heap_test(h)) { printf ("oops - heap_remove messed up the heap\n"); exit(1); } #endif return 1; } -int ImagingQuantHeapAdd(Heap H,void *val) { - IntHeap *h=(IntHeap *)H; +int ImagingQuantHeapAdd(Heap *h,void *val) { int k; if (h->heapcount==h->heapsize-1) { _heap_grow(h,0); } k=++h->heapcount; while (k!=1) { - if (h->cf(H,val,h->heap[k/2])<=0) { + if (h->cf(h,val,h->heap[k/2])<=0) { break; } h->heap[k]=h->heap[k/2]; @@ -121,13 +116,12 @@ int ImagingQuantHeapAdd(Heap H,void *val) { } h->heap[k]=val; #ifdef DEBUG - if (!_heap_test(H)) { printf ("oops - heap_add messed up the heap\n"); exit(1); } + if (!_heap_test(h)) { printf ("oops - heap_add messed up the heap\n"); exit(1); } #endif return 1; } -int ImagingQuantHeapTop(Heap H,void **r) { - IntHeap *h=(IntHeap *)H; +int ImagingQuantHeapTop(Heap *h,void **r) { if (!h->heapcount) { return 0; } @@ -136,15 +130,14 @@ int ImagingQuantHeapTop(Heap H,void **r) { } Heap *ImagingQuantHeapNew(HeapCmpFunc cf) { - IntHeap *h; - - h=malloc(sizeof(IntHeap)); + Heap *h; + + h=malloc(sizeof(Heap)); if (!h) return NULL; h->heapsize=INITIAL_SIZE; h->heap=malloc(sizeof(void *)*h->heapsize); if (!h->heap) { free(h); return NULL; } h->heapcount=0; h->cf=cf; - return (Heap)h; + return h; } - diff --git a/libImaging/QuantHeap.h b/libImaging/QuantHeap.h index 5a213c42a..77bf0d9d5 100644 --- a/libImaging/QuantHeap.h +++ b/libImaging/QuantHeap.h @@ -9,15 +9,19 @@ * See the README file for information on usage and redistribution. */ -#ifndef __HEAP_H__ -#define __HEAP_H__ +#ifndef __QUANTHEAP_H__ +#define __QUANTHEAP_H__ #include "QuantTypes.h" -void ImagingQuantHeapFree(Heap); -int ImagingQuantHeapRemove(Heap,void **); -int ImagingQuantHeapAdd(Heap,void *); -int ImagingQuantHeapTop(Heap,void **); +typedef struct _Heap Heap; + +typedef int (*HeapCmpFunc)(const Heap *,const void *,const void *); + +void ImagingQuantHeapFree(Heap *); +int ImagingQuantHeapRemove(Heap *,void **); +int ImagingQuantHeapAdd(Heap *,void *); +int ImagingQuantHeapTop(Heap *,void **); Heap *ImagingQuantHeapNew(HeapCmpFunc); -#endif +#endif // __QUANTHEAP_H__ diff --git a/libImaging/QuantOctree.c b/libImaging/QuantOctree.c new file mode 100644 index 000000000..b233ccd2e --- /dev/null +++ b/libImaging/QuantOctree.c @@ -0,0 +1,454 @@ +/* Copyright (c) 2010 Oliver Tonnhofer , Omniscale +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +*/ + +/* +// This file implements a variation of the octree color quantization algorithm. +*/ + +#include +#include +#include + +#include "QuantOctree.h" + +typedef struct _ColorBucket{ + /* contains palette index when used for look up cube */ + uint32_t count; + uint64_t r; + uint64_t g; + uint64_t b; + uint64_t a; +} *ColorBucket; + +typedef struct _ColorCube{ + unsigned int rBits, gBits, bBits, aBits; + unsigned int rWidth, gWidth, bWidth, aWidth; + unsigned int rOffset, gOffset, bOffset, aOffset; + + long size; + ColorBucket buckets; +} *ColorCube; + +#define MAX(a, b) (a)>(b) ? (a) : (b) + +static ColorCube +new_color_cube(int r, int g, int b, int a) { + ColorCube cube; + + cube = malloc(sizeof(struct _ColorCube)); + if (!cube) return NULL; + + cube->rBits = MAX(r, 0); + cube->gBits = MAX(g, 0); + cube->bBits = MAX(b, 0); + cube->aBits = MAX(a, 0); + + /* the width of the cube for each dimension */ + cube->rWidth = 1<rBits; + cube->gWidth = 1<gBits; + cube->bWidth = 1<bBits; + cube->aWidth = 1<aBits; + + /* the offsets of each color */ + + cube->rOffset = cube->gBits + cube->bBits + cube->aBits; + cube->gOffset = cube->bBits + cube->aBits; + cube->bOffset = cube->aBits; + cube->aOffset = 0; + + /* the number of color buckets */ + cube->size = cube->rWidth * cube->gWidth * cube->bWidth * cube->aWidth; + cube->buckets = calloc(cube->size, sizeof(struct _ColorBucket)); + + if (!cube->buckets) { + free(cube); + return NULL; + } + return cube; +} + +static void +free_color_cube(ColorCube cube) { + if (cube != NULL) { + free(cube->buckets); + free(cube); + } +} + +static long +color_bucket_offset_pos(const ColorCube cube, + unsigned int r, unsigned int g, unsigned int b, unsigned int a) +{ + return r<rOffset | g<gOffset | b<bOffset | a<aOffset; +} + +static long +color_bucket_offset(const ColorCube cube, const Pixel *p) { + unsigned int r = p->c.r>>(8-cube->rBits); + unsigned int g = p->c.g>>(8-cube->gBits); + unsigned int b = p->c.b>>(8-cube->bBits); + unsigned int a = p->c.a>>(8-cube->aBits); + return color_bucket_offset_pos(cube, r, g, b, a); +} + +static ColorBucket +color_bucket_from_cube(const ColorCube cube, const Pixel *p) { + unsigned int offset = color_bucket_offset(cube, p); + return &cube->buckets[offset]; +} + +static void +add_color_to_color_cube(const ColorCube cube, const Pixel *p) { + ColorBucket bucket = color_bucket_from_cube(cube, p); + bucket->count += 1; + bucket->r += p->c.r; + bucket->g += p->c.g; + bucket->b += p->c.b; + bucket->a += p->c.a; +} + +static long +count_used_color_buckets(const ColorCube cube) { + long usedBuckets = 0; + long i; + for (i=0; i < cube->size; i++) { + if (cube->buckets[i].count > 0) { + usedBuckets += 1; + } + } + return usedBuckets; +} + +static void +avg_color_from_color_bucket(const ColorBucket bucket, Pixel *dst) { + float count = bucket->count; + dst->c.r = (int)(bucket->r / count); + dst->c.g = (int)(bucket->g / count); + dst->c.b = (int)(bucket->b / count); + dst->c.a = (int)(bucket->a / count); +} + +static int +compare_bucket_count(const ColorBucket a, const ColorBucket b) { + return b->count - a->count; +} + +static ColorBucket +create_sorted_color_palette(const ColorCube cube) { + ColorBucket buckets; + buckets = malloc(sizeof(struct _ColorBucket)*cube->size); + if (!buckets) return NULL; + memcpy(buckets, cube->buckets, sizeof(struct _ColorBucket)*cube->size); + + qsort(buckets, cube->size, sizeof(struct _ColorBucket), + (int (*)(void const *, void const *))&compare_bucket_count); + + return buckets; +} + +void add_bucket_values(ColorBucket src, ColorBucket dst) { + dst->count += src->count; + dst->r += src->r; + dst->g += src->g; + dst->b += src->b; + dst->a += src->a; +} + +/* expand or shrink a given cube to level */ +static ColorCube copy_color_cube(const ColorCube cube, + int rBits, int gBits, int bBits, int aBits) +{ + unsigned int r, g, b, a; + long src_pos, dst_pos; + unsigned int src_reduce[4] = {0}, dst_reduce[4] = {0}; + unsigned int width[4]; + ColorCube result; + + result = new_color_cube(rBits, gBits, bBits, aBits); + if (!result) return NULL; + + if (cube->rBits > rBits) { + dst_reduce[0] = cube->rBits - result->rBits; + width[0] = cube->rWidth; + } else { + src_reduce[0] = result->rBits - cube->rBits; + width[0] = result->rWidth; + } + if (cube->gBits > gBits) { + dst_reduce[1] = cube->gBits - result->gBits; + width[1] = cube->gWidth; + } else { + src_reduce[1] = result->gBits - cube->gBits; + width[1] = result->gWidth; + } + if (cube->bBits > bBits) { + dst_reduce[2] = cube->bBits - result->bBits; + width[2] = cube->bWidth; + } else { + src_reduce[2] = result->bBits - cube->bBits; + width[2] = result->bWidth; + } + if (cube->aBits > aBits) { + dst_reduce[3] = cube->aBits - result->aBits; + width[3] = cube->aWidth; + } else { + src_reduce[3] = result->aBits - cube->aBits; + width[3] = result->aWidth; + } + + for (r=0; r>src_reduce[0], + g>>src_reduce[1], + b>>src_reduce[2], + a>>src_reduce[3]); + dst_pos = color_bucket_offset_pos(result, + r>>dst_reduce[0], + g>>dst_reduce[1], + b>>dst_reduce[2], + a>>dst_reduce[3]); + add_bucket_values( + &cube->buckets[src_pos], + &result->buckets[dst_pos] + ); + } + } + } + } + return result; +} + +void +subtract_color_buckets(ColorCube cube, ColorBucket buckets, long nBuckets) { + ColorBucket minuend, subtrahend; + long i; + Pixel p; + for (i=0; icount -= subtrahend->count; + minuend->r -= subtrahend->r; + minuend->g -= subtrahend->g; + minuend->b -= subtrahend->b; + minuend->a -= subtrahend->a; + } +} + +static void +set_lookup_value(const ColorCube cube, const Pixel *p, long value) { + ColorBucket bucket = color_bucket_from_cube(cube, p); + bucket->count = value; +} + +uint64_t +lookup_color(const ColorCube cube, const Pixel *p) { + ColorBucket bucket = color_bucket_from_cube(cube, p); + return bucket->count; +} + +void add_lookup_buckets(ColorCube cube, ColorBucket palette, long nColors, long offset) { + long i; + Pixel p; + for (i=offset; i 64). + + For a quantization to 256 colors all 64 coarse colors will be used + plus the 192 most used color buckets from the fine color cube. + The average of all colors within one bucket is used as the actual + color for that bucket. + + For images with alpha the cubes gets a forth dimension, + 8x16x8x8 and 4x4x4x4. + */ + + /* create fine cube */ + fineCube = new_color_cube(cubeBits[0], cubeBits[1], + cubeBits[2], cubeBits[3]); + if (!fineCube) goto error; + for (i=0; i nQuantPixels) + nCoarseColors = nQuantPixels; + + /* how many space do we have in our palette for fine colors? */ + nFineColors = nQuantPixels - nCoarseColors; + + /* create fine color palette */ + paletteBucketsFine = create_sorted_color_palette(fineCube); + if (!paletteBucketsFine) goto error; + + /* remove the used fine colors from the coarse cube */ + subtract_color_buckets(coarseCube, paletteBucketsFine, nFineColors); + + /* did the substraction cleared one or more coarse bucket? */ + while (nCoarseColors > count_used_color_buckets(coarseCube)) { + /* then we can use the free buckets for fine colors */ + nAlreadySubtracted = nFineColors; + nCoarseColors = count_used_color_buckets(coarseCube); + nFineColors = nQuantPixels - nCoarseColors; + subtract_color_buckets(coarseCube, &paletteBucketsFine[nAlreadySubtracted], + nFineColors-nAlreadySubtracted); + } + + /* create our palette buckets with fine and coarse combined */ + paletteBucketsCoarse = create_sorted_color_palette(coarseCube); + if (!paletteBucketsCoarse) goto error; + paletteBuckets = combined_palette(paletteBucketsCoarse, nCoarseColors, + paletteBucketsFine, nFineColors); + + free(paletteBucketsFine); + paletteBucketsFine = NULL; + free(paletteBucketsCoarse); + paletteBucketsCoarse = NULL; + + /* add all coarse colors to our coarse lookup cube. */ + coarseLookupCube = new_color_cube(cubeBits[4], cubeBits[5], + cubeBits[6], cubeBits[7]); + if (!coarseLookupCube) goto error; + add_lookup_buckets(coarseLookupCube, paletteBuckets, nCoarseColors, 0); + + /* expand coarse cube (64) to larger fine cube (4k). the value of each + coarse bucket is then present in the according 64 fine buckets. */ + lookupCube = copy_color_cube(coarseLookupCube, cubeBits[0], cubeBits[1], + cubeBits[2], cubeBits[3]); + if (!lookupCube) goto error; + + /* add fine colors to the lookup cube */ + add_lookup_buckets(lookupCube, paletteBuckets, nFineColors, nCoarseColors); + + /* create result pixles and map palatte indices */ + qp = malloc(sizeof(Pixel)*nPixels); + if (!qp) goto error; + map_image_pixels(pixelData, nPixels, lookupCube, qp); + + /* convert palette buckets to RGB pixel palette */ + *palette = create_palette_array(paletteBuckets, nQuantPixels); + if (!(*palette)) goto error; + + *quantizedPixels = qp; + *paletteLength = nQuantPixels; + + free_color_cube(coarseCube); + free_color_cube(fineCube); + free_color_cube(lookupCube); + free_color_cube(coarseLookupCube); + free(paletteBuckets); + return 1; + +error: + /* everything is initialized to NULL + so we are safe to call free */ + free(qp); + free_color_cube(lookupCube); + free_color_cube(coarseLookupCube); + free(paletteBucketsCoarse); + free(paletteBucketsFine); + free_color_cube(coarseCube); + free_color_cube(fineCube); + return 0; +} diff --git a/libImaging/QuantOctree.h b/libImaging/QuantOctree.h new file mode 100644 index 000000000..968644eda --- /dev/null +++ b/libImaging/QuantOctree.h @@ -0,0 +1,14 @@ +#ifndef __QUANT_OCTREE_H__ +#define __QUANT_OCTREE_H__ + +#include "QuantTypes.h" + +int quantize_octree(Pixel *, + uint32_t, + uint32_t, + Pixel **, + uint32_t *, + uint32_t **, + int); + +#endif diff --git a/libImaging/QuantTypes.h b/libImaging/QuantTypes.h index 308b51c13..411485498 100644 --- a/libImaging/QuantTypes.h +++ b/libImaging/QuantTypes.h @@ -12,17 +12,21 @@ #ifndef __TYPES_H__ #define __TYPES_H__ -typedef void *HashTable; -typedef void *Heap; +#ifdef _MSC_VER +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif -typedef unsigned long (*HashFunc)(const HashTable,const void *); -typedef int (*HashCmpFunc)(const HashTable,const void *,const void *); -typedef void (*IteratorFunc)(const HashTable,const void *,const void *,void *); -typedef void (*IteratorUpdateFunc)(const HashTable,const void *,void **,void *); -typedef void (*DestroyFunc)(const HashTable,void *); -typedef void (*ComputeFunc)(const HashTable,const void *,void **); -typedef void (*CollisionFunc)(const HashTable,void **,void **,void *,void *); - -typedef int (*HeapCmpFunc)(const Heap,const void *,const void *); +typedef union { + struct { + unsigned char r,g,b,a; + } c; + struct { + unsigned char v[4]; + } a; + uint32_t v; +} Pixel; #endif diff --git a/libImaging/RankFilter.c b/libImaging/RankFilter.c index 84090f751..7bc4d5b10 100644 --- a/libImaging/RankFilter.c +++ b/libImaging/RankFilter.c @@ -81,6 +81,7 @@ ImagingRankFilter(Imaging im, int size, int rank) size * sizeof(type));\ IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\ }\ + free(buf); \ } while (0) if (im->image8) @@ -94,7 +95,7 @@ ImagingRankFilter(Imaging im, int size, int rank) ImagingDelete(imOut); return (Imaging) ImagingError_ModeError(); } - + ImagingCopyInfo(imOut, im); return imOut; diff --git a/libImaging/RawDecode.c b/libImaging/RawDecode.c index 5aadb2b44..40c0cb79a 100644 --- a/libImaging/RawDecode.c +++ b/libImaging/RawDecode.c @@ -69,7 +69,7 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) return ptr - buf; /* Unpack data */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->shuffle((UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, ptr, state->xsize); ptr += state->bytes; diff --git a/libImaging/Storage.c b/libImaging/Storage.c index 76a66c2a5..d31db5cb2 100644 --- a/libImaging/Storage.c +++ b/libImaging/Storage.c @@ -28,7 +28,7 @@ * 2003-09-26 fl Added "LA" and "PA" modes (experimental) * 2005-10-02 fl Added image counter * - * Copyright (c) 1998-2005 by Secret Labs AB + * Copyright (c) 1998-2005 by Secret Labs AB * Copyright (c) 1995-2005 by Fredrik Lundh * * See the README file for information on usage and redistribution. @@ -36,6 +36,7 @@ #include "Imaging.h" +#include int ImagingNewCount = 0; @@ -53,7 +54,7 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im = (Imaging) calloc(1, size); if (!im) - return (Imaging) ImagingError_MemoryError(); + return (Imaging) ImagingError_MemoryError(); /* Setup image descriptor */ im->xsize = xsize; @@ -104,7 +105,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im->linesize = xsize * 4; im->type = IMAGING_TYPE_INT32; - } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 || strcmp(mode, "I;16B") == 0) { + } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \ + || strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) { /* EXPERIMENTAL */ /* 16-bit raw integer images */ im->bands = 1; @@ -177,9 +179,16 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, im->pixelsize = 4; im->linesize = xsize * 4; + } else if (strcmp(mode, "LAB") == 0) { + /* 24-bit color, luminance, + 2 color channels */ + /* L is uint8, a,b are int8 */ + im->bands = 3; + im->pixelsize = 4; + im->linesize = xsize * 4; + } else { free(im); - return (Imaging) ImagingError_ValueError("unrecognized mode"); + return (Imaging) ImagingError_ValueError("unrecognized mode"); } /* Setup image descriptor */ @@ -194,8 +203,8 @@ ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, ImagingSectionLeave(&cookie); if (!im->image) { - free(im); - return (Imaging) ImagingError_MemoryError(); + free(im); + return (Imaging) ImagingError_MemoryError(); } ImagingNewCount++; @@ -218,16 +227,16 @@ ImagingNewEpilogue(Imaging im) assume that it couldn't allocate the required amount of memory. */ if (!im->destroy) - return (Imaging) ImagingError_MemoryError(); + return (Imaging) ImagingError_MemoryError(); /* Initialize alias pointers to pixel data. */ switch (im->pixelsize) { case 1: case 2: case 3: - im->image8 = (UINT8 **) im->image; - break; + im->image8 = (UINT8 **) im->image; + break; case 4: - im->image32 = (INT32 **) im->image; - break; + im->image32 = (INT32 **) im->image; + break; } return im; @@ -237,16 +246,16 @@ void ImagingDelete(Imaging im) { if (!im) - return; + return; if (im->palette) - ImagingPaletteDelete(im->palette); + ImagingPaletteDelete(im->palette); if (im->destroy) - im->destroy(im); + im->destroy(im); if (im->image) - free(im->image); + free(im->image); free(im); } @@ -262,9 +271,9 @@ ImagingDestroyArray(Imaging im) int y; if (im->image) - for (y = 0; y < im->ysize; y++) - if (im->image[y]) - free(im->image[y]); + for (y = 0; y < im->ysize; y++) + if (im->image[y]) + free(im->image[y]); } Imaging @@ -278,24 +287,24 @@ ImagingNewArray(const char *mode, int xsize, int ysize) im = ImagingNewPrologue(mode, xsize, ysize); if (!im) - return NULL; + return NULL; ImagingSectionEnter(&cookie); /* Allocate image as an array of lines */ for (y = 0; y < im->ysize; y++) { - p = (char *) malloc(im->linesize); - if (!p) { - ImagingDestroyArray(im); - break; - } + p = (char *) malloc(im->linesize); + if (!p) { + ImagingDestroyArray(im); + break; + } im->image[y] = p; } ImagingSectionLeave(&cookie); if (y == im->ysize) - im->destroy = ImagingDestroyArray; + im->destroy = ImagingDestroyArray; return ImagingNewEpilogue(im); } @@ -309,22 +318,22 @@ static void ImagingDestroyBlock(Imaging im) { if (im->block) - free(im->block); + free(im->block); } Imaging ImagingNewBlock(const char *mode, int xsize, int ysize) { Imaging im; - int y, i; - int bytes; + Py_ssize_t y, i; + Py_ssize_t bytes; im = ImagingNewPrologue(mode, xsize, ysize); if (!im) - return NULL; + return NULL; /* Use a single block */ - bytes = im->ysize * im->linesize; + bytes = (Py_ssize_t) im->ysize * im->linesize; if (bytes <= 0) /* some platforms return NULL for malloc(0); this fix prevents MemoryError on zero-sized images on such @@ -333,13 +342,14 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) im->block = (char *) malloc(bytes); if (im->block) { + memset(im->block, 0, bytes); - for (y = i = 0; y < im->ysize; y++) { - im->image[y] = im->block + i; - i += im->linesize; - } + for (y = i = 0; y < im->ysize; y++) { + im->image[y] = im->block + i; + i += im->linesize; + } - im->destroy = ImagingDestroyBlock; + im->destroy = ImagingDestroyBlock; } @@ -350,9 +360,9 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) * Create a new, internally allocated, image. */ #if defined(IMAGING_SMALL_MODEL) -#define THRESHOLD 16384L +#define THRESHOLD 16384L #else -#define THRESHOLD (2048*2048*4L) +#define THRESHOLD (2048*2048*4L) #endif Imaging @@ -369,7 +379,7 @@ ImagingNew(const char* mode, int xsize, int ysize) } else bytes = strlen(mode); /* close enough */ - if ((long) xsize * ysize * bytes <= THRESHOLD) { + if ((Py_ssize_t) xsize * ysize * bytes <= THRESHOLD) { im = ImagingNewBlock(mode, xsize, ysize); if (im) return im; @@ -408,6 +418,6 @@ ImagingCopyInfo(Imaging destination, Imaging source) if (source->palette) { if (destination->palette) ImagingPaletteDelete(destination->palette); - destination->palette = ImagingPaletteDuplicate(source->palette); + destination->palette = ImagingPaletteDuplicate(source->palette); } } diff --git a/libImaging/SunRleDecode.c b/libImaging/SunRleDecode.c index bf34d9382..6c240e400 100644 --- a/libImaging/SunRleDecode.c +++ b/libImaging/SunRleDecode.c @@ -62,7 +62,7 @@ ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) } memset(state->buffer + state->x, ptr[2], n); - + ptr += 3; bytes -= 3; diff --git a/libImaging/TiffDecode.c b/libImaging/TiffDecode.c new file mode 100644 index 000000000..787cd4506 --- /dev/null +++ b/libImaging/TiffDecode.c @@ -0,0 +1,422 @@ +/* + * The Python Imaging Library. + * $Id: //modules/pil/libImaging/TiffDecode.c#1 $ + * + * LibTiff-based Group3 and Group4 decoder + * + * + * started modding to use non-private tiff functions to port to libtiff 4.x + * eds 3/12/12 + * + */ + +#include "Imaging.h" + +#ifdef HAVE_LIBTIFF + +#ifndef uint +#define uint uint32 +#endif + +#include "TiffDecode.h" + +void dump_state(const TIFFSTATE *state){ + TRACE(("State: Location %u size %d eof %d data: %p \n", (uint)state->loc, + (int)state->size, (uint)state->eof, state->data)); +} + +/* + procs for TIFFOpenClient +*/ + +tsize_t _tiffReadProc(thandle_t hdata, tdata_t buf, tsize_t size) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + tsize_t to_read; + + TRACE(("_tiffReadProc: %d \n", (int)size)); + dump_state(state); + + to_read = min(size, min(state->size, (tsize_t)state->eof) - (tsize_t)state->loc); + TRACE(("to_read: %d\n", (int)to_read)); + + _TIFFmemcpy(buf, (UINT8 *)state->data + state->loc, to_read); + state->loc += (toff_t)to_read; + + TRACE( ("location: %u\n", (uint)state->loc)); + return to_read; +} + +tsize_t _tiffWriteProc(thandle_t hdata, tdata_t buf, tsize_t size) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + tsize_t to_write; + + TRACE(("_tiffWriteProc: %d \n", (int)size)); + dump_state(state); + + to_write = min(size, state->size - (tsize_t)state->loc); + if (state->flrealloc && size>to_write) { + tdata_t new; + tsize_t newsize=state->size; + while (newsize < (size + state->size)) { + newsize += 64*1024; + // newsize*=2; // UNDONE, by 64k chunks? + } + TRACE(("Reallocing in write to %d bytes\n", (int)newsize)); + new = realloc(state->data, newsize); + if (!new) { + // fail out + return 0; + } + state->data = new; + state->size = newsize; + to_write = size; + } + + TRACE(("to_write: %d\n", (int)to_write)); + + _TIFFmemcpy((UINT8 *)state->data + state->loc, buf, to_write); + state->loc += (toff_t)to_write; + state->eof = max(state->loc, state->eof); + + dump_state(state); + return to_write; +} + +toff_t _tiffSeekProc(thandle_t hdata, toff_t off, int whence) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + + TRACE(("_tiffSeekProc: off: %u whence: %d \n", (uint)off, whence)); + dump_state(state); + switch (whence) { + case 0: + state->loc = off; + break; + case 1: + state->loc += off; + break; + case 2: + state->loc = state->eof + off; + break; + } + dump_state(state); + return state->loc; +} + +int _tiffCloseProc(thandle_t hdata) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + + TRACE(("_tiffCloseProc \n")); + dump_state(state); + + return 0; +} + + +toff_t _tiffSizeProc(thandle_t hdata) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + + TRACE(("_tiffSizeProc \n")); + dump_state(state); + + return (toff_t)state->size; +} +int _tiffMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) { + TIFFSTATE *state = (TIFFSTATE *)hdata; + + TRACE(("_tiffMapProc input size: %u, data: %p\n", (uint)*psize, *pbase)); + dump_state(state); + + *pbase = state->data; + *psize = state->size; + TRACE(("_tiffMapProc returning size: %u, data: %p\n", (uint)*psize, *pbase)); + return (1); +} + +int _tiffNullMapProc(thandle_t hdata, tdata_t* pbase, toff_t* psize) { + (void) hdata; (void) pbase; (void) psize; + return (0); +} + +void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) { + TRACE(("_tiffUnMapProc\n")); + (void) hdata; (void) base; (void) size; +} + +int ImagingLibTiffInit(ImagingCodecState state, int fp) { + TIFFSTATE *clientstate = (TIFFSTATE *)state->context; + + TRACE(("initing libtiff\n")); + TRACE(("filepointer: %d \n", fp)); + TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, + state->x, state->y, state->ystep)); + TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, + state->xoff, state->yoff)); + TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); + TRACE(("State: context %p \n", state->context)); + + clientstate->loc = 0; + clientstate->size = 0; + clientstate->data = 0; + clientstate->fp = fp; + clientstate->eof = 0; + + return 1; +} + +int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) { + TIFFSTATE *clientstate = (TIFFSTATE *)state->context; + char *filename = "tempfile.tif"; + char *mode = "r"; + TIFF *tiff; + int size; + + + /* buffer is the encoded file, bytes is the length of the encoded file */ + /* it all ends up in state->buffer, which is a uint8* from Imaging.h */ + + TRACE(("in decoder: bytes %d\n", bytes)); + TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, + state->x, state->y, state->ystep)); + TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, + state->xoff, state->yoff)); + TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); + TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); + TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3])); + TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", + im->mode, im->type, im->bands, im->xsize, im->ysize)); + TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", + im->image8, im->image32, im->image, im->block)); + TRACE(("Image: pixelsize: %d, linesize %d \n", + im->pixelsize, im->linesize)); + + dump_state(clientstate); + clientstate->size = bytes; + clientstate->eof = clientstate->size; + clientstate->loc = 0; + clientstate->data = (tdata_t)buffer; + clientstate->flrealloc = 0; + + dump_state(clientstate); + + TIFFSetWarningHandler(NULL); + TIFFSetWarningHandlerExt(NULL); + + if (clientstate->fp) { + TRACE(("Opening using fd: %d\n",clientstate->fp)); + lseek(clientstate->fp,0,SEEK_SET); // Sometimes, I get it set to the end. + tiff = TIFFFdOpen(clientstate->fp, filename, mode); + } else { + TRACE(("Opening from string\n")); + tiff = TIFFClientOpen(filename, mode, + (thandle_t) clientstate, + _tiffReadProc, _tiffWriteProc, + _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, + _tiffMapProc, _tiffUnmapProc); + } + + if (!tiff){ + TRACE(("Error, didn't get the tiff\n")); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + size = TIFFScanlineSize(tiff); + TRACE(("ScanlineSize: %d \n", size)); + if (size > state->bytes) { + TRACE(("Error, scanline size > buffer size\n")); + state->errcode = IMAGING_CODEC_BROKEN; + TIFFClose(tiff); + return -1; + } + + // Have to do this row by row and shove stuff into the buffer that way, + // with shuffle. (or, just alloc a buffer myself, then figure out how to get it + // back in. Can't use read encoded stripe. + + // This thing pretty much requires that I have the whole image in one shot. + // Prehaps a stub version would work better??? + while(state->y < state->ysize){ + if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { + TRACE(("Decode Error, row %d\n", state->y)); + state->errcode = IMAGING_CODEC_BROKEN; + TIFFClose(tiff); + return -1; + } + /* TRACE(("Decoded row %d \n", state->y)); */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->buffer, + state->xsize); + + state->y++; + } + + TIFFClose(tiff); + TRACE(("Done Decoding, Returning \n")); + // Returning -1 here to force ImageFile.load to break, rather than + // even think about looping back around. + return -1; +} + +int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp) { + // Open the FD or the pointer as a tiff file, for writing. + // We may have to do some monkeying around to make this really work. + // If we have a fp, then we're good. + // If we have a memory string, we're probably going to have to malloc, then + // shuffle bytes into the writescanline process. + // Going to have to deal with the directory as well. + + TIFFSTATE *clientstate = (TIFFSTATE *)state->context; + int bufsize = 64*1024; + char *mode = "w"; + + TRACE(("initing libtiff\n")); + TRACE(("Filename %s, filepointer: %d \n", filename, fp)); + TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, + state->x, state->y, state->ystep)); + TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, + state->xoff, state->yoff)); + TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); + TRACE(("State: context %p \n", state->context)); + + clientstate->loc = 0; + clientstate->size = 0; + clientstate->eof =0; + clientstate->data = 0; + clientstate->flrealloc = 0; + clientstate->fp = fp; + + state->state = 0; + + if (fp) { + TRACE(("Opening using fd: %d for writing \n",clientstate->fp)); + clientstate->tiff = TIFFFdOpen(clientstate->fp, filename, mode); + } else { + // malloc a buffer to write the tif, we're going to need to realloc or something if we need bigger. + TRACE(("Opening a buffer for writing \n")); + clientstate->data = malloc(bufsize); + clientstate->size = bufsize; + clientstate->flrealloc=1; + + if (!clientstate->data) { + TRACE(("Error, couldn't allocate a buffer of size %d\n", bufsize)); + return 0; + } + + clientstate->tiff = TIFFClientOpen(filename, mode, + (thandle_t) clientstate, + _tiffReadProc, _tiffWriteProc, + _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, + _tiffNullMapProc, _tiffUnmapProc); /*force no mmap*/ + + } + + if (!clientstate->tiff) { + TRACE(("Error, couldn't open tiff file\n")); + return 0; + } + + return 1; + +} + +int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...){ + // after tif_dir.c->TIFFSetField. + TIFFSTATE *clientstate = (TIFFSTATE *)state->context; + va_list ap; + int status; + + va_start(ap, tag); + status = TIFFVSetField(clientstate->tiff, tag, ap); + va_end(ap); + return status; +} + + +int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) { + /* One shot encoder. Encode everything to the tiff in the clientstate. + If we're running off of a FD, then run once, we're good, everything + ends up in the file, we close and we're done. + + If we're going to memory, then we need to write the whole file into memory, then + parcel it back out to the pystring buffer bytes at a time. + + */ + + TIFFSTATE *clientstate = (TIFFSTATE *)state->context; + TIFF *tiff = clientstate->tiff; + + TRACE(("in encoder: bytes %d\n", bytes)); + TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state, + state->x, state->y, state->ystep)); + TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize, + state->xoff, state->yoff)); + TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes)); + TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); + TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3])); + TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n", + im->mode, im->type, im->bands, im->xsize, im->ysize)); + TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n", + im->image8, im->image32, im->image, im->block)); + TRACE(("Image: pixelsize: %d, linesize %d \n", + im->pixelsize, im->linesize)); + + dump_state(clientstate); + + if (state->state == 0) { + TRACE(("Encoding line bt line")); + while(state->y < state->ysize){ + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->xsize); + + if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y, 0) == -1) { + TRACE(("Encode Error, row %d\n", state->y)); + state->errcode = IMAGING_CODEC_BROKEN; + TIFFClose(tiff); + if (!clientstate->fp){ + free(clientstate->data); + } + return -1; + } + state->y++; + } + + if (state->y == state->ysize) { + state->state=1; + + TRACE(("Flushing \n")); + if (!TIFFFlush(tiff)) { + TRACE(("Error flushing the tiff")); + // likely reason is memory. + state->errcode = IMAGING_CODEC_MEMORY; + TIFFClose(tiff); + if (!clientstate->fp){ + free(clientstate->data); + } + return -1; + } + TRACE(("Closing \n")); + TIFFClose(tiff); + // reset the clientstate metadata to use it to read out the buffer. + clientstate->loc = 0; + clientstate->size = clientstate->eof; // redundant? + } + } + + if (state->state == 1 && !clientstate->fp) { + int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes); + TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3])); + if (clientstate->loc == clientstate->eof) { + TRACE(("Hit EOF, calling an end, freeing data")); + state->errcode = IMAGING_CODEC_END; + free(clientstate->data); + } + return read; + } + + state->errcode = IMAGING_CODEC_END; + return 0; +} +#endif diff --git a/libImaging/TiffDecode.h b/libImaging/TiffDecode.h new file mode 100644 index 000000000..90fe3c9d4 --- /dev/null +++ b/libImaging/TiffDecode.h @@ -0,0 +1,63 @@ +/* + * The Python Imaging Library. + * $Id: //modules/pil/libImaging/Tiff.h#1 $ + * + * declarations for the LibTiff-based Group3 and Group4 decoder + * + */ + +#ifndef _TIFFIO_ +#include +#endif +#ifndef _TIFF_ +#include +#endif + +#ifndef _UNISTD_H +#include +#endif + + +#ifndef min +#define min(x,y) (( x > y ) ? y : x ) +#define max(x,y) (( x < y ) ? y : x ) +#endif + +#ifndef _PIL_LIBTIFF_ +#define _PIL_LIBTIFF_ + +typedef struct { + tdata_t data; /* tdata_t == void* */ + toff_t loc; /* toff_t == uint32 */ + tsize_t size; /* tsize_t == int32 */ + int fp; + TIFF *tiff; /* Used in write */ + toff_t eof; + int flrealloc; /* may we realloc */ +} TIFFSTATE; + + + +extern int ImagingLibTiffInit(ImagingCodecState state, int fp); +extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp); +extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...); + + +#if defined(_MSC_VER) && (_MSC_VER == 1310) +/* VS2003/py2.4 can't use varargs. Skipping trace for now.*/ +#define TRACE(args) +#else + + +/* +#define VA_ARGS(...) __VA_ARGS__ +#define TRACE(args) fprintf(stderr, VA_ARGS args) +*/ + +#define TRACE(args) + +#endif /* _MSC_VER */ + + + +#endif diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c index 90a76feb3..70b11b1b0 100644 --- a/libImaging/Unpack.c +++ b/libImaging/Unpack.c @@ -33,17 +33,17 @@ #include "Imaging.h" -#define R 0 -#define G 1 -#define B 2 -#define X 3 +#define R 0 +#define G 1 +#define B 2 +#define X 3 -#define A 3 +#define A 3 -#define C 0 -#define M 1 -#define Y 2 -#define K 3 +#define C 0 +#define M 1 +#define Y 2 +#define K 3 #define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) @@ -110,18 +110,18 @@ unpack1(UINT8* out, const UINT8* in, int pixels) { /* bits (msb first, white is non-zero) */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; - case 1: *out++ = (byte & 128) ? 255 : 0; - } - pixels -= 8; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 1: *out++ = (byte & 128) ? 255 : 0; + } + pixels -= 8; } } @@ -130,18 +130,18 @@ unpack1I(UINT8* out, const UINT8* in, int pixels) { /* bits (msb first, white is zero) */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 7: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 6: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 5: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 4: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 3: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 2: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; - case 1: *out++ = (byte & 128) ? 0 : 255; - } - pixels -= 8; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 7: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 6: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 5: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 4: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 3: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 2: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 1: *out++ = (byte & 128) ? 0 : 255; + } + pixels -= 8; } } @@ -150,18 +150,18 @@ unpack1R(UINT8* out, const UINT8* in, int pixels) { /* bits (lsb first, white is non-zero) */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 7: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 6: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 5: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 4: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 3: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 2: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; - case 1: *out++ = (byte & 1) ? 255 : 0; - } - pixels -= 8; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 7: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 6: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 5: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 4: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 3: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 2: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 1: *out++ = (byte & 1) ? 255 : 0; + } + pixels -= 8; } } @@ -170,18 +170,18 @@ unpack1IR(UINT8* out, const UINT8* in, int pixels) { /* bits (lsb first, white is zero) */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 7: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 6: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 5: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 4: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 3: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 2: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; - case 1: *out++ = (byte & 1) ? 0 : 255; - } - pixels -= 8; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 7: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 6: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 5: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 4: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 3: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 2: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 1: *out++ = (byte & 1) ? 0 : 255; + } + pixels -= 8; } } @@ -193,14 +193,14 @@ unpackL2(UINT8* out, const UINT8* in, int pixels) { /* nibbles */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; - case 1: *out++ = ((byte >> 6) & 3) * 255 / 3; - } - pixels -= 4; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 1: *out++ = ((byte >> 6) & 3) * 255 / 3; + } + pixels -= 4; } } @@ -209,12 +209,12 @@ unpackL4(UINT8* out, const UINT8* in, int pixels) { /* nibbles */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4; - case 1: *out++ = ((byte >> 4) & 15) * 255 / 15; - } - pixels -= 2; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4; + case 1: *out++ = ((byte >> 4) & 15) * 255 / 15; + } + pixels -= 2; } } @@ -224,9 +224,9 @@ unpackLA(UINT8* out, const UINT8* in, int pixels) int i; /* LA, pixel interleaved */ for (i = 0; i < pixels; i++) { - out[R] = out[G] = out[B] = in[0]; - out[A] = in[1]; - in += 2; out += 4; + out[R] = out[G] = out[B] = in[0]; + out[A] = in[1]; + in += 2; out += 4; } } @@ -236,9 +236,9 @@ unpackLAL(UINT8* out, const UINT8* in, int pixels) int i; /* LA, line interleaved */ for (i = 0; i < pixels; i++) { - out[R] = out[G] = out[B] = in[i]; - out[A] = in[i+pixels]; - out += 4; + out[R] = out[G] = out[B] = in[i]; + out[A] = in[i+pixels]; + out += 4; } } @@ -248,7 +248,7 @@ unpackLI(UINT8* out, const UINT8* in, int pixels) /* negative */ int i; for (i = 0; i < pixels; i++) - out[i] = ~in[i]; + out[i] = ~in[i]; } static void @@ -257,7 +257,7 @@ unpackLR(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, bit reversed */ for (i = 0; i < pixels; i++) { - out[i] = BITFLIP[in[i]]; + out[i] = BITFLIP[in[i]]; } } @@ -267,8 +267,8 @@ unpackL16(UINT8* out, const UINT8* in, int pixels) /* int16 (upper byte, little endian) */ int i; for (i = 0; i < pixels; i++) { - out[i] = in[1]; - in += 2; + out[i] = in[1]; + in += 2; } } @@ -278,8 +278,8 @@ unpackL16B(UINT8* out, const UINT8* in, int pixels) int i; /* int16 (upper byte, big endian) */ for (i = 0; i < pixels; i++) { - out[i] = in[0]; - in += 2; + out[i] = in[0]; + in += 2; } } @@ -291,18 +291,18 @@ unpackP1(UINT8* out, const UINT8* in, int pixels) { /* bits */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte >> 7) & 1; byte <<= 1; - case 7: *out++ = (byte >> 7) & 1; byte <<= 1; - case 6: *out++ = (byte >> 7) & 1; byte <<= 1; - case 5: *out++ = (byte >> 7) & 1; byte <<= 1; - case 4: *out++ = (byte >> 7) & 1; byte <<= 1; - case 3: *out++ = (byte >> 7) & 1; byte <<= 1; - case 2: *out++ = (byte >> 7) & 1; byte <<= 1; - case 1: *out++ = (byte >> 7) & 1; - } - pixels -= 8; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 7) & 1; byte <<= 1; + case 7: *out++ = (byte >> 7) & 1; byte <<= 1; + case 6: *out++ = (byte >> 7) & 1; byte <<= 1; + case 5: *out++ = (byte >> 7) & 1; byte <<= 1; + case 4: *out++ = (byte >> 7) & 1; byte <<= 1; + case 3: *out++ = (byte >> 7) & 1; byte <<= 1; + case 2: *out++ = (byte >> 7) & 1; byte <<= 1; + case 1: *out++ = (byte >> 7) & 1; + } + pixels -= 8; } } @@ -311,14 +311,14 @@ unpackP2(UINT8* out, const UINT8* in, int pixels) { /* bit pairs */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte >> 6) & 3; byte <<= 2; - case 3: *out++ = (byte >> 6) & 3; byte <<= 2; - case 2: *out++ = (byte >> 6) & 3; byte <<= 2; - case 1: *out++ = (byte >> 6) & 3; - } - pixels -= 4; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 6) & 3; byte <<= 2; + case 3: *out++ = (byte >> 6) & 3; byte <<= 2; + case 2: *out++ = (byte >> 6) & 3; byte <<= 2; + case 1: *out++ = (byte >> 6) & 3; + } + pixels -= 4; } } @@ -327,12 +327,12 @@ unpackP4(UINT8* out, const UINT8* in, int pixels) { /* nibbles */ while (pixels > 0) { - UINT8 byte = *in++; - switch (pixels) { - default: *out++ = (byte >> 4) & 15; byte <<= 4; - case 1: *out++ = (byte >> 4) & 15; - } - pixels -= 2; + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 4) & 15; byte <<= 4; + case 1: *out++ = (byte >> 4) & 15; + } + pixels -= 2; } } @@ -344,11 +344,11 @@ unpackP2L(UINT8* out, const UINT8* in, int pixels) m = 128; s = (pixels+7)/8; for (i = j = 0; i < pixels; i++) { - out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0); - if ((m >>= 1) == 0) { - m = 128; - j++; - } + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } } } @@ -360,12 +360,12 @@ unpackP4L(UINT8* out, const UINT8* in, int pixels) m = 128; s = (pixels+7)/8; for (i = j = 0; i < pixels; i++) { - out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) + - ((in[j + 2*s] & m) ? 4 : 0) + ((in[j + 3*s] & m) ? 8 : 0); - if ((m >>= 1) == 0) { - m = 128; - j++; - } + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) + + ((in[j + 2*s] & m) ? 4 : 0) + ((in[j + 3*s] & m) ? 8 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } } } @@ -377,11 +377,11 @@ ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels) int i; /* RGB triplets */ for (i = 0; i < pixels; i++) { - out[R] = in[0]; - out[G] = in[1]; - out[B] = in[2]; - out[A] = 255; - out += 4; in += 3; + out[R] = in[0]; + out[G] = in[1]; + out[B] = in[2]; + out[A] = 255; + out += 4; in += 3; } } @@ -391,11 +391,11 @@ unpackRGB16B(UINT8* out, const UINT8* in, int pixels) int i; /* 16-bit RGB triplets, big-endian order */ for (i = 0; i < pixels; i++) { - out[R] = in[0]; - out[G] = in[2]; - out[B] = in[4]; - out[A] = 255; - out += 4; in += 6; + out[R] = in[0]; + out[G] = in[2]; + out[B] = in[4]; + out[A] = 255; + out += 4; in += 6; } } @@ -405,11 +405,11 @@ unpackRGBL(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, line interleaved */ for (i = 0; i < pixels; i++) { - out[R] = in[i]; - out[G] = in[i+pixels]; - out[B] = in[i+pixels+pixels]; - out[A] = 255; - out += 4; + out[R] = in[i]; + out[G] = in[i+pixels]; + out[B] = in[i+pixels+pixels]; + out[A] = 255; + out += 4; } } @@ -419,11 +419,11 @@ unpackRGBR(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, bit reversed */ for (i = 0; i < pixels; i++) { - out[R] = BITFLIP[in[0]]; - out[G] = BITFLIP[in[1]]; - out[B] = BITFLIP[in[2]]; - out[A] = 255; - out += 4; in += 3; + out[R] = BITFLIP[in[0]]; + out[G] = BITFLIP[in[1]]; + out[B] = BITFLIP[in[2]]; + out[A] = 255; + out += 4; in += 3; } } @@ -433,11 +433,41 @@ ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, reversed bytes */ for (i = 0; i < pixels; i++) { - out[R] = in[2]; - out[G] = in[1]; - out[B] = in[0]; - out[A] = 255; - out += 4; in += 3; + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = 255; + out += 4; in += 3; + } +} + +void +ImagingUnpackRGB15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[B] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; } } @@ -447,12 +477,42 @@ ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) int i, pixel; /* RGB, reversed bytes, 5 bits per pixel */ for (i = 0; i < pixels; i++) { - pixel = in[0] + (in[1] << 8); - out[B] = (pixel & 31) * 255 / 31; - out[G] = ((pixel>>5) & 31) * 255 / 31; - out[R] = ((pixel>>10) & 31) * 255 / 31; - out[A] = 255; - out += 4; in += 2; + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[R] = ((pixel>>10) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackBGRA15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, reversed bytes, 5/5/5/1 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[R] = ((pixel>>10) & 31) * 255 / 31; + out[A] = (pixel>>15) * 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGB16(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 5/6/5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 63) * 255 / 63; + out[B] = ((pixel>>11) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; } } @@ -462,12 +522,42 @@ ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) int i, pixel; /* RGB, reversed bytes, 5/6/5 bits per pixel */ for (i = 0; i < pixels; i++) { - pixel = in[0] + (in[1] << 8); - out[B] = (pixel & 31) * 255 / 31; - out[G] = ((pixel>>5) & 63) * 255 / 63; - out[R] = ((pixel>>11) & 31) * 255 / 31; - out[A] = 255; - out += 4; in += 2; + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 63) * 255 / 63; + out[R] = ((pixel>>11) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGB4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackRGBA4B(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGBA, 4 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[R] = (pixel & 15) * 17; + out[G] = ((pixel>>4) & 15) * 17; + out[B] = ((pixel>>8) & 15) * 17; + out[A] = ((pixel>>12) & 15) * 17; + out += 4; in += 2; } } @@ -477,11 +567,11 @@ ImagingUnpackBGRX(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, reversed bytes with padding */ for (i = 0; i < pixels; i++) { - out[R] = in[2]; - out[G] = in[1]; - out[B] = in[0]; - out[A] = 255; - out += 4; in += 4; + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = 255; + out += 4; in += 4; } } @@ -491,11 +581,11 @@ ImagingUnpackXRGB(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, leading pad */ for (i = 0; i < pixels; i++) { - out[R] = in[1]; - out[G] = in[2]; - out[B] = in[3]; - out[A] = 255; - out += 4; in += 4; + out[R] = in[1]; + out[G] = in[2]; + out[B] = in[3]; + out[A] = 255; + out += 4; in += 4; } } @@ -505,11 +595,11 @@ ImagingUnpackXBGR(UINT8* out, const UINT8* in, int pixels) int i; /* RGB, reversed bytes, leading pad */ for (i = 0; i < pixels; i++) { - out[R] = in[3]; - out[G] = in[2]; - out[B] = in[1]; - out[A] = 255; - out += 4; in += 4; + out[R] = in[3]; + out[G] = in[2]; + out[B] = in[1]; + out[A] = 255; + out += 4; in += 4; } } @@ -521,9 +611,9 @@ unpackRGBALA(UINT8* out, const UINT8* in, int pixels) int i; /* greyscale with alpha */ for (i = 0; i < pixels; i++) { - out[R] = out[G] = out[B] = in[0]; - out[A] = in[1]; - out += 4; in += 2; + out[R] = out[G] = out[B] = in[0]; + out[A] = in[1]; + out += 4; in += 2; } } @@ -533,9 +623,9 @@ unpackRGBALA16B(UINT8* out, const UINT8* in, int pixels) int i; /* 16-bit greyscale with alpha, big-endian */ for (i = 0; i < pixels; i++) { - out[R] = out[G] = out[B] = in[0]; - out[A] = in[2]; - out += 4; in += 4; + out[R] = out[G] = out[B] = in[0]; + out[A] = in[2]; + out += 4; in += 4; } } @@ -545,7 +635,7 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels) int i; /* premultiplied RGBA */ for (i = 0; i < pixels; i++) { - int a = in[3]; + int a = in[3]; if (!a) out[R] = out[G] = out[B] = out[A] = 0; else { @@ -554,7 +644,7 @@ unpackRGBa(UINT8* out, const UINT8* in, int pixels) out[B] = CLIP(in[2] * 255 / a); out[A] = a; } - out += 4; in += 4; + out += 4; in += 4; } } @@ -564,11 +654,11 @@ unpackRGBAI(UINT8* out, const UINT8* in, int pixels) int i; /* RGBA, inverted RGB bytes (FlashPix) */ for (i = 0; i < pixels; i++) { - out[R] = ~in[0]; - out[G] = ~in[1]; - out[B] = ~in[2]; - out[A] = in[3]; - out += 4; in += 4; + out[R] = ~in[0]; + out[G] = ~in[1]; + out[B] = ~in[2]; + out[A] = in[3]; + out += 4; in += 4; } } @@ -579,11 +669,11 @@ unpackRGBAL(UINT8* out, const UINT8* in, int pixels) /* RGBA, line interleaved */ for (i = 0; i < pixels; i++) { - out[R] = in[i]; - out[G] = in[i+pixels]; - out[B] = in[i+pixels+pixels]; - out[A] = in[i+pixels+pixels+pixels]; - out += 4; + out[R] = in[i]; + out[G] = in[i+pixels]; + out[B] = in[i+pixels+pixels]; + out[A] = in[i+pixels+pixels+pixels]; + out += 4; } } @@ -593,11 +683,11 @@ unpackRGBA16B(UINT8* out, const UINT8* in, int pixels) int i; /* 16-bit RGBA, big-endian order */ for (i = 0; i < pixels; i++) { - out[R] = in[0]; - out[G] = in[2]; - out[B] = in[4]; - out[A] = in[6]; - out += 4; in += 8; + out[R] = in[0]; + out[G] = in[2]; + out[B] = in[4]; + out[A] = in[6]; + out += 4; in += 8; } } @@ -607,11 +697,11 @@ unpackARGB(UINT8* out, const UINT8* in, int pixels) int i; /* RGBA, leading pad */ for (i = 0; i < pixels; i++) { - out[R] = in[1]; - out[G] = in[2]; - out[B] = in[3]; - out[A] = in[0]; - out += 4; in += 4; + out[R] = in[1]; + out[G] = in[2]; + out[B] = in[3]; + out[A] = in[0]; + out += 4; in += 4; } } @@ -621,11 +711,11 @@ unpackABGR(UINT8* out, const UINT8* in, int pixels) int i; /* RGBA, reversed bytes */ for (i = 0; i < pixels; i++) { - out[R] = in[3]; - out[G] = in[2]; - out[B] = in[1]; - out[A] = in[0]; - out += 4; in += 4; + out[R] = in[3]; + out[G] = in[2]; + out[B] = in[1]; + out[A] = in[0]; + out += 4; in += 4; } } @@ -635,11 +725,11 @@ unpackBGRA(UINT8* out, const UINT8* in, int pixels) int i; /* RGBA, reversed bytes */ for (i = 0; i < pixels; i++) { - out[R] = in[2]; - out[G] = in[1]; - out[B] = in[0]; - out[A] = in[3]; - out += 4; in += 4; + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = in[3]; + out += 4; in += 4; } } @@ -652,14 +742,113 @@ unpackCMYKI(UINT8* out, const UINT8* in, int pixels) int i; /* CMYK, inverted bytes (Photoshop 2.5) */ for (i = 0; i < pixels; i++) { - out[C] = ~in[0]; - out[M] = ~in[1]; - out[Y] = ~in[2]; - out[K] = ~in[3]; - out += 4; in += 4; + out[C] = ~in[0]; + out[M] = ~in[1]; + out[Y] = ~in[2]; + out[K] = ~in[3]; + out += 4; in += 4; } } +/* Unpack to "LAB" image */ +/* There are two representations of LAB images for whatever precision: + L: Uint (in PS, it's 0-100) + A: Int (in ps, -128 .. 128, or elsewhere 0..255, with 128 as middle. + Channels in PS display a 0 value as middle grey, + LCMS appears to use 128 as the 0 value for these channels) + B: Int (as above) + + Since we don't have any signed ints, we're going with the shifted versions + internally, and we'll unshift for saving and whatnot. +*/ +void +ImagingUnpackLAB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LAB triplets */ + for (i = 0; i < pixels; i++) { + out[0] = in[0]; + out[1] = in[1] ^ 128; /* signed in outside world */ + out[2] = in[2] ^ 128; + out[3] = 255; + out += 4; in += 3; + } +} + +static void +unpackI16N_I16B(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16B; + in += 2; tmp += 2; + } + +} +static void +unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){ + int i; + UINT8* tmp = (UINT8*) out; + for (i = 0; i < pixels; i++) { + C16L; + in += 2; tmp += 2; + } +} + +static void +unpackI12_I16(UINT8* out, const UINT8* in, int pixels){ + /* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs. + + According to the TIFF spec: + + FillOrder = 2 should be used only when BitsPerSample = 1 and + the data is either uncompressed or compressed using CCITT 1D + or 2D compression, to avoid potentially ambigous situations. + + Yeah. I thought so. We'll see how well people read the spec. + We've got several fillorder=2 modes in TiffImagePlugin.py + + There's no spec I can find. It appears that the in storage + layout is: 00 80 00 ... -> (128 , 0 ...). The samples are + stored in a single big bitian 12bit block, but need to be + pulled out to little endian format to be stored in a 2 byte + int. + */ + + int i; + UINT16 pixel; +#ifdef WORDS_BIGENDIAN + UINT8* tmp = (UINT8 *)&pixel; +#endif + UINT16* out16 = (UINT16 *)out; + for (i = 0; i < pixels-1; i+=2) { + pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4); +#ifdef WORDS_BIGENDIAN + out[0] = tmp[1]; out[1] = tmp[0]; +#else + out16[0] = pixel; +#endif + + pixel = (((UINT16) (in[1] & 0x0F)) << 8) + in[2]; +#ifdef WORDS_BIGENDIAN + out[2] = tmp[1]; out[3] = tmp[0]; +#else + out16[1] = pixel; +#endif + + in += 3; out16 += 2; out+=4; + } + if (i == pixels-1) { + pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4); +#ifdef WORDS_BIGENDIAN + out[0] = tmp[1]; out[1] = tmp[0]; +#else + out16[0] = pixel; +#endif + } +} + + static void copy1(UINT8* out, const UINT8* in, int pixels) { @@ -674,6 +863,13 @@ copy2(UINT8* out, const UINT8* in, int pixels) memcpy(out, in, pixels*2); } +static void +copy3(UINT8* out, const UINT8* in, int pixels) +{ + /* LAB triples, 24bit */ + memcpy(out, in, 3 * pixels); +} + static void copy4(UINT8* out, const UINT8* in, int pixels) { @@ -753,8 +949,8 @@ band0(UINT8* out, const UINT8* in, int pixels) int i; /* band 0 only */ for (i = 0; i < pixels; i++) { - out[0] = in[i]; - out += 4; + out[0] = in[i]; + out += 4; } } @@ -764,8 +960,8 @@ band1(UINT8* out, const UINT8* in, int pixels) int i; /* band 1 only */ for (i = 0; i < pixels; i++) { - out[1] = in[i]; - out += 4; + out[1] = in[i]; + out += 4; } } @@ -775,8 +971,8 @@ band2(UINT8* out, const UINT8* in, int pixels) int i; /* band 2 only */ for (i = 0; i < pixels; i++) { - out[2] = in[i]; - out += 4; + out[2] = in[i]; + out += 4; } } @@ -786,8 +982,8 @@ band3(UINT8* out, const UINT8* in, int pixels) /* band 3 only */ int i; for (i = 0; i < pixels; i++) { - out[3] = in[i]; - out += 4; + out[3] = in[i]; + out += 4; } } @@ -797,8 +993,8 @@ band0I(UINT8* out, const UINT8* in, int pixels) int i; /* band 0 only */ for (i = 0; i < pixels; i++) { - out[0] = ~in[i]; - out += 4; + out[0] = ~in[i]; + out += 4; } } @@ -808,8 +1004,8 @@ band1I(UINT8* out, const UINT8* in, int pixels) int i; /* band 1 only */ for (i = 0; i < pixels; i++) { - out[1] = ~in[i]; - out += 4; + out[1] = ~in[i]; + out += 4; } } @@ -819,8 +1015,8 @@ band2I(UINT8* out, const UINT8* in, int pixels) int i; /* band 2 only */ for (i = 0; i < pixels; i++) { - out[2] = ~in[i]; - out += 4; + out[2] = ~in[i]; + out += 4; } } @@ -830,8 +1026,8 @@ band3I(UINT8* out, const UINT8* in, int pixels) /* band 3 only */ int i; for (i = 0; i < pixels; i++) { - out[3] = ~in[i]; - out += 4; + out[3] = ~in[i]; + out += 4; } } @@ -852,157 +1048,176 @@ static struct { interleave, "S" signed, "F" floating point */ /* bilevel */ - {"1", "1", 1, unpack1}, - {"1", "1;I", 1, unpack1I}, - {"1", "1;R", 1, unpack1R}, - {"1", "1;IR", 1, unpack1IR}, + {"1", "1", 1, unpack1}, + {"1", "1;I", 1, unpack1I}, + {"1", "1;R", 1, unpack1R}, + {"1", "1;IR", 1, unpack1IR}, /* greyscale */ - {"L", "L;2", 2, unpackL2}, - {"L", "L;4", 4, unpackL4}, - {"L", "L", 8, copy1}, - {"L", "L;I", 8, unpackLI}, - {"L", "L;R", 8, unpackLR}, - {"L", "L;16", 16, unpackL16}, - {"L", "L;16B", 16, unpackL16B}, + {"L", "L;2", 2, unpackL2}, + {"L", "L;4", 4, unpackL4}, + {"L", "L", 8, copy1}, + {"L", "L;I", 8, unpackLI}, + {"L", "L;R", 8, unpackLR}, + {"L", "L;16", 16, unpackL16}, + {"L", "L;16B", 16, unpackL16B}, /* greyscale w. alpha */ - {"LA", "LA", 16, unpackLA}, - {"LA", "LA;L", 16, unpackLAL}, + {"LA", "LA", 16, unpackLA}, + {"LA", "LA;L", 16, unpackLAL}, /* palette */ - {"P", "P;1", 1, unpackP1}, - {"P", "P;2", 2, unpackP2}, - {"P", "P;2L", 2, unpackP2L}, - {"P", "P;4", 4, unpackP4}, - {"P", "P;4L", 4, unpackP4L}, - {"P", "P", 8, copy1}, - {"P", "P;R", 8, unpackLR}, + {"P", "P;1", 1, unpackP1}, + {"P", "P;2", 2, unpackP2}, + {"P", "P;2L", 2, unpackP2L}, + {"P", "P;4", 4, unpackP4}, + {"P", "P;4L", 4, unpackP4L}, + {"P", "P", 8, copy1}, + {"P", "P;R", 8, unpackLR}, /* palette w. alpha */ - {"PA", "PA", 16, unpackLA}, - {"PA", "PA;L", 16, unpackLAL}, + {"PA", "PA", 16, unpackLA}, + {"PA", "PA;L", 16, unpackLAL}, /* true colour */ - {"RGB", "RGB", 24, ImagingUnpackRGB}, - {"RGB", "RGB;L", 24, unpackRGBL}, - {"RGB", "RGB;R", 24, unpackRGBR}, - {"RGB", "RGB;16B", 48, unpackRGB16B}, - {"RGB", "BGR", 24, ImagingUnpackBGR}, - {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, - {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, - {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ - {"RGB", "RGBX", 32, copy4}, - {"RGB", "RGBX;L", 32, unpackRGBAL}, - {"RGB", "BGRX", 32, ImagingUnpackBGRX}, - {"RGB", "XRGB", 24, ImagingUnpackXRGB}, - {"RGB", "XBGR", 32, ImagingUnpackXBGR}, - {"RGB", "YCC;P", 24, ImagingUnpackYCC}, - {"RGB", "R", 8, band0}, - {"RGB", "G", 8, band1}, - {"RGB", "B", 8, band2}, + {"RGB", "RGB", 24, ImagingUnpackRGB}, + {"RGB", "RGB;L", 24, unpackRGBL}, + {"RGB", "RGB;R", 24, unpackRGBR}, + {"RGB", "RGB;16B", 48, unpackRGB16B}, + {"RGB", "BGR", 24, ImagingUnpackBGR}, + {"RGB", "RGB;15", 16, ImagingUnpackRGB15}, + {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGB", "RGB;16", 16, ImagingUnpackRGB16}, + {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGB", "RGB;4B", 16, ImagingUnpackRGB4B}, + {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ + {"RGB", "RGBX", 32, copy4}, + {"RGB", "RGBX;L", 32, unpackRGBAL}, + {"RGB", "BGRX", 32, ImagingUnpackBGRX}, + {"RGB", "XRGB", 24, ImagingUnpackXRGB}, + {"RGB", "XBGR", 32, ImagingUnpackXBGR}, + {"RGB", "YCC;P", 24, ImagingUnpackYCC}, + {"RGB", "R", 8, band0}, + {"RGB", "G", 8, band1}, + {"RGB", "B", 8, band2}, /* true colour w. alpha */ - {"RGBA", "LA", 16, unpackRGBALA}, - {"RGBA", "LA;16B", 32, unpackRGBALA16B}, - {"RGBA", "RGBA", 32, copy4}, - {"RGBA", "RGBa", 32, unpackRGBa}, - {"RGBA", "RGBA;I", 32, unpackRGBAI}, - {"RGBA", "RGBA;L", 32, unpackRGBAL}, - {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, - {"RGBA", "BGRA", 32, unpackBGRA}, - {"RGBA", "ARGB", 32, unpackARGB}, - {"RGBA", "ABGR", 32, unpackABGR}, - {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA}, - {"RGBA", "R", 8, band0}, - {"RGBA", "G", 8, band1}, - {"RGBA", "B", 8, band2}, - {"RGBA", "A", 8, band3}, + {"RGBA", "LA", 16, unpackRGBALA}, + {"RGBA", "LA;16B", 32, unpackRGBALA16B}, + {"RGBA", "RGBA", 32, copy4}, + {"RGBA", "RGBa", 32, unpackRGBa}, + {"RGBA", "RGBA;I", 32, unpackRGBAI}, + {"RGBA", "RGBA;L", 32, unpackRGBAL}, + {"RGBA", "RGBA;15", 16, ImagingUnpackRGBA15}, + {"RGBA", "BGRA;15", 16, ImagingUnpackBGRA15}, + {"RGBA", "RGBA;4B", 16, ImagingUnpackRGBA4B}, + {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, + {"RGBA", "BGRA", 32, unpackBGRA}, + {"RGBA", "ARGB", 32, unpackARGB}, + {"RGBA", "ABGR", 32, unpackABGR}, + {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA}, + {"RGBA", "R", 8, band0}, + {"RGBA", "G", 8, band1}, + {"RGBA", "B", 8, band2}, + {"RGBA", "A", 8, band3}, /* true colour w. padding */ - {"RGBX", "RGB", 24, ImagingUnpackRGB}, - {"RGBX", "RGB;L", 24, unpackRGBL}, - {"RGBX", "RGB;16B", 48, unpackRGB16B}, - {"RGBX", "BGR", 24, ImagingUnpackBGR}, - {"RGBX", "BGR;15", 16, ImagingUnpackBGR15}, - {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, - {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ - {"RGBX", "RGBX", 32, copy4}, - {"RGBX", "RGBX;L", 32, unpackRGBAL}, - {"RGBX", "BGRX", 32, ImagingUnpackBGRX}, - {"RGBX", "XRGB", 24, ImagingUnpackXRGB}, - {"RGBX", "XBGR", 32, ImagingUnpackXBGR}, - {"RGBX", "YCC;P", 24, ImagingUnpackYCC}, - {"RGBX", "R", 8, band0}, - {"RGBX", "G", 8, band1}, - {"RGBX", "B", 8, band2}, - {"RGBX", "X", 8, band3}, + {"RGBX", "RGB", 24, ImagingUnpackRGB}, + {"RGBX", "RGB;L", 24, unpackRGBL}, + {"RGBX", "RGB;16B", 48, unpackRGB16B}, + {"RGBX", "BGR", 24, ImagingUnpackBGR}, + {"RGBX", "RGB;15", 16, ImagingUnpackRGB15}, + {"RGBX", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGBX", "RGB;4B", 16, ImagingUnpackRGB4B}, + {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ + {"RGBX", "RGBX", 32, copy4}, + {"RGBX", "RGBX;L", 32, unpackRGBAL}, + {"RGBX", "BGRX", 32, ImagingUnpackBGRX}, + {"RGBX", "XRGB", 24, ImagingUnpackXRGB}, + {"RGBX", "XBGR", 32, ImagingUnpackXBGR}, + {"RGBX", "YCC;P", 24, ImagingUnpackYCC}, + {"RGBX", "R", 8, band0}, + {"RGBX", "G", 8, band1}, + {"RGBX", "B", 8, band2}, + {"RGBX", "X", 8, band3}, /* colour separation */ - {"CMYK", "CMYK", 32, copy4}, - {"CMYK", "CMYK;I", 32, unpackCMYKI}, - {"CMYK", "CMYK;L", 32, unpackRGBAL}, - {"CMYK", "C", 8, band0}, - {"CMYK", "M", 8, band1}, - {"CMYK", "Y", 8, band2}, - {"CMYK", "K", 8, band3}, - {"CMYK", "C;I", 8, band0I}, - {"CMYK", "M;I", 8, band1I}, - {"CMYK", "Y;I", 8, band2I}, - {"CMYK", "K;I", 8, band3I}, + {"CMYK", "CMYK", 32, copy4}, + {"CMYK", "CMYK;I", 32, unpackCMYKI}, + {"CMYK", "CMYK;L", 32, unpackRGBAL}, + {"CMYK", "C", 8, band0}, + {"CMYK", "M", 8, band1}, + {"CMYK", "Y", 8, band2}, + {"CMYK", "K", 8, band3}, + {"CMYK", "C;I", 8, band0I}, + {"CMYK", "M;I", 8, band1I}, + {"CMYK", "Y;I", 8, band2I}, + {"CMYK", "K;I", 8, band3I}, /* video (YCbCr) */ - {"YCbCr", "YCbCr", 24, ImagingUnpackRGB}, - {"YCbCr", "YCbCr;L", 24, unpackRGBL}, - {"YCbCr", "YCbCrX", 32, copy4}, - {"YCbCr", "YCbCrK", 32, copy4}, + {"YCbCr", "YCbCr", 24, ImagingUnpackRGB}, + {"YCbCr", "YCbCr;L", 24, unpackRGBL}, + {"YCbCr", "YCbCrX", 32, copy4}, + {"YCbCr", "YCbCrK", 32, copy4}, + + /* LAB Color */ + {"LAB", "LAB", 24, ImagingUnpackLAB}, + {"LAB", "L", 8, band0}, + {"LAB", "A", 8, band1}, + {"LAB", "B", 8, band2}, /* integer variations */ - {"I", "I", 32, copy4}, - {"I", "I;8", 8, unpackI8}, - {"I", "I;8S", 8, unpackI8S}, - {"I", "I;16", 16, unpackI16}, - {"I", "I;16S", 16, unpackI16S}, - {"I", "I;16B", 16, unpackI16B}, - {"I", "I;16BS", 16, unpackI16BS}, - {"I", "I;16N", 16, unpackI16N}, - {"I", "I;16NS", 16, unpackI16NS}, - {"I", "I;32", 32, unpackI32}, - {"I", "I;32S", 32, unpackI32S}, - {"I", "I;32B", 32, unpackI32B}, - {"I", "I;32BS", 32, unpackI32BS}, - {"I", "I;32N", 32, unpackI32N}, - {"I", "I;32NS", 32, unpackI32NS}, + {"I", "I", 32, copy4}, + {"I", "I;8", 8, unpackI8}, + {"I", "I;8S", 8, unpackI8S}, + {"I", "I;16", 16, unpackI16}, + {"I", "I;16S", 16, unpackI16S}, + {"I", "I;16B", 16, unpackI16B}, + {"I", "I;16BS", 16, unpackI16BS}, + {"I", "I;16N", 16, unpackI16N}, + {"I", "I;16NS", 16, unpackI16NS}, + {"I", "I;32", 32, unpackI32}, + {"I", "I;32S", 32, unpackI32S}, + {"I", "I;32B", 32, unpackI32B}, + {"I", "I;32BS", 32, unpackI32BS}, + {"I", "I;32N", 32, unpackI32N}, + {"I", "I;32NS", 32, unpackI32NS}, /* floating point variations */ - {"F", "F", 32, copy4}, - {"F", "F;8", 8, unpackF8}, - {"F", "F;8S", 8, unpackF8S}, - {"F", "F;16", 16, unpackF16}, - {"F", "F;16S", 16, unpackF16S}, - {"F", "F;16B", 16, unpackF16B}, - {"F", "F;16BS", 16, unpackF16BS}, - {"F", "F;16N", 16, unpackF16N}, - {"F", "F;16NS", 16, unpackF16NS}, - {"F", "F;32", 32, unpackF32}, - {"F", "F;32S", 32, unpackF32S}, - {"F", "F;32B", 32, unpackF32B}, - {"F", "F;32BS", 32, unpackF32BS}, - {"F", "F;32N", 32, unpackF32N}, - {"F", "F;32NS", 32, unpackF32NS}, - {"F", "F;32F", 32, unpackF32F}, - {"F", "F;32BF", 32, unpackF32BF}, - {"F", "F;32NF", 32, unpackF32NF}, + {"F", "F", 32, copy4}, + {"F", "F;8", 8, unpackF8}, + {"F", "F;8S", 8, unpackF8S}, + {"F", "F;16", 16, unpackF16}, + {"F", "F;16S", 16, unpackF16S}, + {"F", "F;16B", 16, unpackF16B}, + {"F", "F;16BS", 16, unpackF16BS}, + {"F", "F;16N", 16, unpackF16N}, + {"F", "F;16NS", 16, unpackF16NS}, + {"F", "F;32", 32, unpackF32}, + {"F", "F;32S", 32, unpackF32S}, + {"F", "F;32B", 32, unpackF32B}, + {"F", "F;32BS", 32, unpackF32BS}, + {"F", "F;32N", 32, unpackF32N}, + {"F", "F;32NS", 32, unpackF32NS}, + {"F", "F;32F", 32, unpackF32F}, + {"F", "F;32BF", 32, unpackF32BF}, + {"F", "F;32NF", 32, unpackF32NF}, #ifdef FLOAT64 - {"F", "F;64F", 64, unpackF64F}, - {"F", "F;64BF", 64, unpackF64BF}, - {"F", "F;64NF", 64, unpackF64NF}, + {"F", "F;64F", 64, unpackF64F}, + {"F", "F;64BF", 64, unpackF64BF}, + {"F", "F;64NF", 64, unpackF64NF}, #endif /* storage modes */ - {"I;16", "I;16", 16, copy2}, - {"I;16B", "I;16B", 16, copy2}, - {"I;16L", "I;16L", 16, copy2}, + {"I;16", "I;16", 16, copy2}, + {"I;16B", "I;16B", 16, copy2}, + {"I;16L", "I;16L", 16, copy2}, + + {"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian. + {"I;16B", "I;16N", 16, unpackI16N_I16B}, + + {"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits. {NULL} /* sentinel */ }; @@ -1015,12 +1230,12 @@ ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out) /* find a suitable pixel unpacker */ for (i = 0; unpackers[i].rawmode; i++) - if (strcmp(unpackers[i].mode, mode) == 0 && + if (strcmp(unpackers[i].mode, mode) == 0 && strcmp(unpackers[i].rawmode, rawmode) == 0) { - if (bits_out) - *bits_out = unpackers[i].bits; - return unpackers[i].unpack; - } + if (bits_out) + *bits_out = unpackers[i].bits; + return unpackers[i].unpack; + } /* FIXME: configure a general unpacker based on the type codes... */ diff --git a/libImaging/Zip.h b/libImaging/Zip.h index d961407e3..21a336f90 100644 --- a/libImaging/Zip.h +++ b/libImaging/Zip.h @@ -28,6 +28,11 @@ typedef struct { /* Optimize (max compression) SLOW!!! */ int optimize; + /* 0 no compression, 9 best compression, -1 default compression */ + int compress_level; + /* compression strategy Z_XXX */ + int compress_type; + /* Predefined dictionary (experimental) */ char* dictionary; int dictionary_size; @@ -49,9 +54,9 @@ typedef struct { UINT8* output; /* output data */ int prefix; /* size of filter prefix (0 for TIFF data) */ - + int interlaced; /* is the image interlaced? (PNG) */ - + int pass; /* current pass of the interlaced image (PNG) */ } ZIPSTATE; diff --git a/libImaging/ZipDecode.c b/libImaging/ZipDecode.c index 08cdc7189..707033a66 100644 --- a/libImaging/ZipDecode.c +++ b/libImaging/ZipDecode.c @@ -32,7 +32,7 @@ static const int ROW_INCREMENT[] = { 8, 8, 8, 4, 4, 2, 2 }; * for interlaced images */ static int get_row_len(ImagingCodecState state, int pass) { - int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass]; + int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass]; return ((row_len * state->bits) + 7) / 8; } @@ -202,7 +202,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) if (state->bits >= 8) { /* Stuff pixels in their correct location, one by one */ for (i = 0; i < row_len; i += ((state->bits + 7) / 8)) { - state->shuffle((UINT8*) im->image[state->y] + + state->shuffle((UINT8*) im->image[state->y] + col * im->pixelsize, state->buffer + context->prefix + i, 1); col += COL_INCREMENT[context->pass]; @@ -214,7 +214,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) for (i = 0; i < row_bits; i += state->bits) { UINT8 byte = *(state->buffer + context->prefix + (i / 8)); byte <<= (i % 8); - state->shuffle((UINT8*) im->image[state->y] + + state->shuffle((UINT8*) im->image[state->y] + col * im->pixelsize, &byte, 1); col += COL_INCREMENT[context->pass]; } @@ -235,7 +235,7 @@ ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) memset(state->buffer, 0, state->bytes+1); } } else { - state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->shuffle((UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->buffer + context->prefix, state->xsize); diff --git a/libImaging/ZipEncode.c b/libImaging/ZipEncode.c index 19b2b7787..a4f76ffb4 100644 --- a/libImaging/ZipEncode.c +++ b/libImaging/ZipEncode.c @@ -26,6 +26,7 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { ZIPSTATE* context = (ZIPSTATE*) state->context; int err; + int compress_level, compress_type; UINT8* ptr; int i, bpp, s, sum; ImagingSectionCookie cookie; @@ -73,17 +74,25 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) context->z_stream.next_in = 0; context->z_stream.avail_in = 0; + compress_level = (context->optimize) ? Z_BEST_COMPRESSION + : context->compress_level; + + if (context->compress_type == -1) { + compress_type = (context->mode == ZIP_PNG) ? Z_FILTERED + : Z_DEFAULT_STRATEGY; + } else { + compress_type = context->compress_type; + } + err = deflateInit2(&context->z_stream, /* compression level */ - (context->optimize) ? Z_BEST_COMPRESSION - : Z_DEFAULT_COMPRESSION, + compress_level, /* compression method */ Z_DEFLATED, /* compression memory resources */ 15, 9, /* compression strategy (image data are filtered)*/ - (context->mode == ZIP_PNG) ? Z_FILTERED - : Z_DEFAULT_STRATEGY); + compress_type); if (err < 0) { state->errcode = IMAGING_CODEC_CONFIG; return -1; @@ -147,7 +156,7 @@ ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) /* Stuff image data into the compressor */ state->shuffle(state->buffer+1, - (UINT8*) im->image[state->y + state->yoff] + + (UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize); diff --git a/map.c b/map.c index f15b8200f..95d5d1d35 100644 --- a/map.c +++ b/map.c @@ -20,24 +20,25 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" #ifdef WIN32 #define WIN32_LEAN_AND_MEAN +#undef INT8 +#undef UINT8 +#undef INT16 +#undef UINT16 #undef INT32 #undef INT64 #undef UINT32 #include "windows.h" #endif +#include "py3.h" + /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Standard mapper */ @@ -53,18 +54,19 @@ typedef struct { #endif } ImagingMapperObject; -staticforward PyTypeObject ImagingMapperType; +static PyTypeObject ImagingMapperType; ImagingMapperObject* PyImaging_MapperNew(const char* filename, int readonly) { ImagingMapperObject *mapper; - ImagingMapperType.ob_type = &PyType_Type; + if (PyType_Ready(&ImagingMapperType) < 0) + return NULL; mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType); if (mapper == NULL) - return NULL; + return NULL; mapper->base = NULL; mapper->size = mapper->offset = 0; @@ -92,7 +94,7 @@ PyImaging_MapperNew(const char* filename, int readonly) PAGE_READONLY, 0, 0, NULL); if (mapper->hMap == (HANDLE)-1) { - CloseHandle(mapper->hFile); + CloseHandle(mapper->hFile); PyErr_SetString(PyExc_IOError, "cannot map file"); PyObject_Del(mapper); return NULL; @@ -114,11 +116,11 @@ mapping_dealloc(ImagingMapperObject* mapper) { #ifdef WIN32 if (mapper->base != 0) - UnmapViewOfFile(mapper->base); + UnmapViewOfFile(mapper->base); if (mapper->hMap != (HANDLE)-1) - CloseHandle(mapper->hMap); + CloseHandle(mapper->hMap); if (mapper->hFile != (HANDLE)-1) - CloseHandle(mapper->hFile); + CloseHandle(mapper->hFile); mapper->base = 0; mapper->hMap = mapper->hFile = (HANDLE)-1; #endif @@ -128,14 +130,14 @@ mapping_dealloc(ImagingMapperObject* mapper) /* -------------------------------------------------------------------- */ /* standard file operations */ -static PyObject* +static PyObject* mapping_read(ImagingMapperObject* mapper, PyObject* args) { PyObject* buf; int size = -1; if (!PyArg_ParseTuple(args, "|i", &size)) - return NULL; + return NULL; /* check size */ if (size < 0 || mapper->offset + size > mapper->size) @@ -143,25 +145,25 @@ mapping_read(ImagingMapperObject* mapper, PyObject* args) if (size < 0) size = 0; - buf = PyString_FromStringAndSize(NULL, size); + buf = PyBytes_FromStringAndSize(NULL, size); if (!buf) - return NULL; + return NULL; if (size > 0) { - memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size); + memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size); mapper->offset += size; } return buf; } -static PyObject* +static PyObject* mapping_seek(ImagingMapperObject* mapper, PyObject* args) { int offset; int whence = 0; if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) - return NULL; + return NULL; switch (whence) { case 0: /* SEEK_SET */ @@ -193,7 +195,7 @@ ImagingDestroyMap(Imaging im) return; /* nothing to do! */ } -static PyObject* +static PyObject* mapping_readimage(ImagingMapperObject* mapper, PyObject* args) { int y, size; @@ -206,7 +208,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) int orientation; if (!PyArg_ParseTuple(args, "s(ii)ii", &mode, &xsize, &ysize, &stride, &orientation)) - return NULL; + return NULL; if (stride <= 0) { /* FIXME: maybe we should call ImagingNewPrologue instead */ @@ -242,7 +244,7 @@ mapping_readimage(ImagingMapperObject* mapper, PyObject* args) if (!ImagingNewEpilogue(im)) return NULL; - mapper->offset += size; + mapper->offset += size; return PyImagingNew(im); } @@ -256,34 +258,46 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -mapping_getattr(ImagingMapperObject* self, char* name) -{ - return Py_FindMethod(methods, (PyObject*) self, name); -} - -statichere PyTypeObject ImagingMapperType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "ImagingMapper", /*tp_name*/ - sizeof(ImagingMapperObject), /*tp_size*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)mapping_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)mapping_getattr, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_hash*/ +static PyTypeObject ImagingMapperType = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImagingMapper", /*tp_name*/ + sizeof(ImagingMapperObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)mapping_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; -PyObject* +PyObject* PyImaging_Mapper(PyObject* self, PyObject* args) { char* filename; if (!PyArg_ParseTuple(args, "s", &filename)) - return NULL; + return NULL; return (PyObject*) PyImaging_MapperNew(filename, 1); } @@ -294,36 +308,37 @@ PyImaging_Mapper(PyObject* self, PyObject* args) typedef struct ImagingBufferInstance { struct ImagingMemoryInstance im; PyObject* target; + Py_buffer view; } ImagingBufferInstance; static void mapping_destroy_buffer(Imaging im) { ImagingBufferInstance* buffer = (ImagingBufferInstance*) im; - + + PyBuffer_Release(&buffer->view); Py_XDECREF(buffer->target); } -PyObject* +PyObject* PyImaging_MapBuffer(PyObject* self, PyObject* args) { - int y, size; + Py_ssize_t y, size; Imaging im; - char* ptr; - int bytes; PyObject* target; + Py_buffer view; char* mode; char* codec; PyObject* bbox; - int offset; + Py_ssize_t offset; int xsize, ysize; int stride; int ystep; - if (!PyArg_ParseTuple(args, "O(ii)sOi(sii)", &target, &xsize, &ysize, + if (!PyArg_ParseTuple(args, "O(ii)sOn(sii)", &target, &xsize, &ysize, &codec, &bbox, &offset, &mode, &stride, &ystep)) - return NULL; + return NULL; if (!PyImaging_CheckBuffer(target)) { PyErr_SetString(PyExc_TypeError, "expected string or buffer"); @@ -339,15 +354,17 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) stride = xsize * 4; } - size = ysize * stride; + size = (Py_ssize_t) ysize * stride; /* check buffer size */ - bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); - if (bytes < 0) { + if (PyImaging_GetBuffer(target, &view) < 0) + return NULL; + + if (view.len < 0) { PyErr_SetString(PyExc_ValueError, "buffer has negative size"); return NULL; } - if (offset + size > bytes) { + if (offset + size > view.len) { PyErr_SetString(PyExc_ValueError, "buffer is not large enough"); return NULL; } @@ -361,15 +378,16 @@ PyImaging_MapBuffer(PyObject* self, PyObject* args) /* setup file pointers */ if (ystep > 0) for (y = 0; y < ysize; y++) - im->image[y] = ptr + offset + y * stride; + im->image[y] = (char*)view.buf + offset + y * stride; else for (y = 0; y < ysize; y++) - im->image[ysize-y-1] = ptr + offset + y * stride; + im->image[ysize-y-1] = (char*)view.buf + offset + y * stride; im->destroy = mapping_destroy_buffer; Py_INCREF(target); ((ImagingBufferInstance*) im)->target = target; + ((ImagingBufferInstance*) im)->view = view; if (!ImagingNewEpilogue(im)) return NULL; diff --git a/outline.c b/outline.c index 6bb2ebb54..25e63aeaf 100644 --- a/outline.c +++ b/outline.c @@ -19,11 +19,6 @@ #include "Python.h" -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - #include "Imaging.h" @@ -35,15 +30,18 @@ typedef struct { ImagingOutline outline; } OutlineObject; -staticforward PyTypeObject OutlineType; +static PyTypeObject OutlineType; -#define PyOutline_Check(op) ((op)->ob_type == &OutlineType) +#define PyOutline_Check(op) (Py_TYPE(op) == &OutlineType) static OutlineObject* _outline_new(void) { OutlineObject *self; + if (PyType_Ready(&OutlineType) < 0) + return NULL; + self = PyObject_New(OutlineObject, &OutlineType); if (self == NULL) return NULL; @@ -159,21 +157,36 @@ static struct PyMethodDef _outline_methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -_outline_getattr(OutlineObject* self, char* name) -{ - return Py_FindMethod(_outline_methods, (PyObject*) self, name); -} - -statichere PyTypeObject OutlineType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyTypeObject OutlineType = { + PyVarObject_HEAD_INIT(NULL, 0) "Outline", /*tp_name*/ sizeof(OutlineObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)_outline_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)_outline_getattr, /*tp_getattr*/ - 0 /*tp_setattr*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + _outline_methods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ }; diff --git a/path.c b/path.c index f083b971d..db4a68e24 100644 --- a/path.c +++ b/path.c @@ -30,23 +30,11 @@ #include -#if PY_VERSION_HEX < 0x01060000 -#define PyObject_New PyObject_NEW -#define PyObject_Del PyMem_DEL -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define Py_ssize_t int -#define lenfunc inquiry -#define ssizeargfunc intargfunc -#define ssizessizeargfunc intintargfunc -#define ssizeobjargproc intobjargproc -#define ssizessizeobjargproc intintobjargproc -#endif +#include "py3.h" /* compatibility wrappers (defined in _imaging.c) */ extern int PyImaging_CheckBuffer(PyObject* buffer); -extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); +extern int PyImaging_GetBuffer(PyObject* buffer, Py_buffer *view); /* -------------------------------------------------------------------- */ /* Class */ @@ -59,10 +47,10 @@ typedef struct { int index; /* temporary use, e.g. in decimate */ } PyPathObject; -staticforward PyTypeObject PyPathType; +static PyTypeObject PyPathType; static double* -alloc_array(int count) +alloc_array(Py_ssize_t count) { double* xy; if (count < 0) { @@ -89,6 +77,9 @@ path_new(Py_ssize_t count, double* xy, int duplicate) xy = p; } + if (PyType_Ready(&PyPathType) < 0) + return NULL; + path = PyObject_New(PyPathObject, &PyPathType); if (path == NULL) return NULL; @@ -110,7 +101,7 @@ path_dealloc(PyPathObject* path) /* Helpers */ /* -------------------------------------------------------------------- */ -#define PyPath_Check(op) ((op)->ob_type == &PyPathType) +#define PyPath_Check(op) (Py_TYPE(op) == &PyPathType) int PyPath_Flatten(PyObject* data, double **pxy) @@ -128,19 +119,23 @@ PyPath_Flatten(PyObject* data, double **pxy) *pxy = xy; return path->count; } - + if (PyImaging_CheckBuffer(data)) { /* Assume the buffer contains floats */ - float* ptr; - int n = PyImaging_ReadBuffer(data, (const void**) &ptr); - n /= 2 * sizeof(float); - xy = alloc_array(n); - if (!xy) - return -1; - for (i = 0; i < n+n; i++) - xy[i] = ptr[i]; - *pxy = xy; - return n; + Py_buffer buffer; + if (PyImaging_GetBuffer(data, &buffer) == 0) { + int n = buffer.len / (2 * sizeof(float)); + float *ptr = (float*) buffer.buf; + xy = alloc_array(n); + if (!xy) + return -1; + for (i = 0; i < n+n; i++) + xy[i] = ptr[i]; + *pxy = xy; + PyBuffer_Release(&buffer); + return n; + } + PyErr_Clear(); } if (!PySequence_Check(data)) { @@ -251,7 +246,7 @@ PyPath_Create(PyObject* self, PyObject* args) Py_ssize_t count; double *xy; - if (PyArg_ParseTuple(args, "i:Path", &count)) { + if (PyArg_ParseTuple(args, "n:Path", &count)) { /* number of vertices */ xy = alloc_array(count); @@ -361,6 +356,8 @@ path_getbbox(PyPathObject* self, PyObject* args) static PyObject* path_getitem(PyPathObject* self, int i) { + if (i < 0) + i = self->count + i; if (i < 0 || i >= self->count) { PyErr_SetString(PyExc_IndexError, "path index out of range"); return NULL; @@ -383,7 +380,7 @@ path_getslice(PyPathObject* self, Py_ssize_t ilow, Py_ssize_t ihigh) ihigh = ilow; else if (ihigh > self->count) ihigh = self->count; - + return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1); } @@ -539,22 +536,56 @@ static struct PyMethodDef methods[] = { {NULL, NULL} /* sentinel */ }; -static PyObject* -path_getattr(PyPathObject* self, char* name) +static PyObject* +path_getattr_id(PyPathObject* self, void* closure) { - PyObject* res; + return Py_BuildValue("n", (Py_ssize_t) self->xy); +} - res = Py_FindMethod(methods, (PyObject*) self, name); - if (res) - return res; +static struct PyGetSetDef getsetters[] = { + { "id", (getter) path_getattr_id }, + { NULL } +}; - PyErr_Clear(); +static PyObject* +path_subscript(PyPathObject* self, PyObject* item) { + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + return path_getitem(self, i); + } + if (PySlice_Check(item)) { + int len = 4; + Py_ssize_t start, stop, step, slicelength; - if (strcmp(name, "id") == 0) - return Py_BuildValue("l", (long) self->xy); +#if PY_VERSION_HEX >= 0x03020000 + if (PySlice_GetIndicesEx(item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#else + if (PySlice_GetIndicesEx((PySliceObject*)item, len, &start, &stop, &step, &slicelength) < 0) + return NULL; +#endif - PyErr_SetString(PyExc_AttributeError, name); - return NULL; + if (slicelength <= 0) { + double *xy = alloc_array(0); + return (PyObject*) path_new(0, xy, 0); + } + else if (step == 1) { + return path_getslice(self, start, stop); + } + else { + PyErr_SetString(PyExc_TypeError, "slice steps not supported"); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, + "Path indices must be integers, not %.200s", + Py_TYPE(&item)->tp_name); + return NULL; + } } static PySequenceMethods path_as_sequence = { @@ -567,21 +598,43 @@ static PySequenceMethods path_as_sequence = { (ssizessizeobjargproc)0, /*sq_ass_slice*/ }; -statichere PyTypeObject PyPathType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ +static PyMappingMethods path_as_mapping = { + (lenfunc)path_len, + (binaryfunc)path_subscript, + NULL +}; + +static PyTypeObject PyPathType = { + PyVarObject_HEAD_INIT(NULL, 0) "Path", /*tp_name*/ sizeof(PyPathObject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)path_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)path_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number */ &path_as_sequence, /*tp_as_sequence */ - 0, /*tp_as_mapping */ - 0, /*tp_hash*/ + &path_as_mapping, /*tp_as_mapping */ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + methods, /*tp_methods*/ + 0, /*tp_members*/ + getsetters, /*tp_getset*/ }; + diff --git a/py3.h b/py3.h new file mode 100644 index 000000000..8adfac081 --- /dev/null +++ b/py3.h @@ -0,0 +1,55 @@ +/* + Python3 definition file to consistently map the code to Python 2.6 or + Python 3. + + PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions + are mapped to PyLong. + + PyString, on the other hand, was split into PyBytes and PyUnicode. We map + both back onto PyString, so use PyBytes or PyUnicode where appropriate. The + only exception to this is _imagingft.c, where PyUnicode is left alone. +*/ + +#if PY_VERSION_HEX >= 0x03000000 +#define PY_ARG_BYTES_LENGTH "y#" + +/* Map PyInt -> PyLong */ +#define PyInt_AsLong PyLong_AsLong +#define PyInt_Check PyLong_Check +#define PyInt_FromLong PyLong_FromLong +#define PyInt_AS_LONG PyLong_AS_LONG +#define PyInt_FromSsize_t PyLong_FromSsize_t + +#else /* PY_VERSION_HEX < 0x03000000 */ +#define PY_ARG_BYTES_LENGTH "s#" + +#if !defined(KEEP_PY_UNICODE) +/* Map PyUnicode -> PyString */ +#undef PyUnicode_AsString +#undef PyUnicode_AS_STRING +#undef PyUnicode_Check +#undef PyUnicode_FromStringAndSize +#undef PyUnicode_FromString +#undef PyUnicode_FromFormat +#undef PyUnicode_DecodeFSDefault + +#define PyUnicode_AsString PyString_AsString +#define PyUnicode_AS_STRING PyString_AS_STRING +#define PyUnicode_Check PyString_Check +#define PyUnicode_FromStringAndSize PyString_FromStringAndSize +#define PyUnicode_FromString PyString_FromString +#define PyUnicode_FromFormat PyString_FromFormat +#define PyUnicode_DecodeFSDefault PyString_FromString +#endif + +/* Map PyBytes -> PyString */ +#define PyBytesObject PyStringObject +#define PyBytes_AsString PyString_AsString +#define PyBytes_AS_STRING PyString_AS_STRING +#define PyBytes_Check PyString_Check +#define PyBytes_AsStringAndSize PyString_AsStringAndSize +#define PyBytes_FromStringAndSize PyString_FromStringAndSize +#define PyBytes_FromString PyString_FromString +#define _PyBytes_Resize _PyString_Resize + +#endif /* PY_VERSION_HEX < 0x03000000 */ diff --git a/selftest.py b/selftest.py index 22bbe433b..1f905b9a7 100644 --- a/selftest.py +++ b/selftest.py @@ -1,27 +1,34 @@ # minimal sanity check +from __future__ import print_function + +import sys +import os + +if "--installed" in sys.argv: + sys_path_0 = sys.path[0] + del sys.path[0] + +from PIL import Image, ImageDraw, ImageFilter, ImageMath + +if "--installed" in sys.argv: + sys.path.insert(0, sys_path_0) ROOT = "." -import os, sys -sys.path.insert(0, ROOT) - -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFilter -from PIL import ImageMath - try: Image.core.ping -except ImportError, v: - print "***", v +except ImportError as v: + print("***", v) sys.exit() except AttributeError: pass + def _info(im): im.load() return im.format, im.mode, im.size + def testimage(): """ PIL lets you create in-memory images with various pixel types: @@ -49,19 +56,19 @@ def testimage(): ('PPM', 'RGB', (128, 128)) >>> try: ... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg"))) - ... except IOError, v: - ... print v + ... except IOError as v: + ... print(v) ('JPEG', 'RGB', (128, 128)) PIL doesn't actually load the image data until it's needed, or you call the "load" method: >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) - >>> print im.im # internal image attribute + >>> print(im.im) # internal image attribute None >>> a = im.load() - >>> type(im.im) - + >>> type(im.im) # doctest: +ELLIPSIS + <... '...ImagingCore'> You can apply many different operations on images. Most operations return a new image: @@ -89,17 +96,17 @@ def testimage(): 2 >>> len(im.histogram()) 768 - >>> _info(im.point(range(256)*3)) + >>> _info(im.point(list(range(256))*3)) (None, 'RGB', (128, 128)) >>> _info(im.resize((64, 64))) (None, 'RGB', (64, 64)) >>> _info(im.rotate(45)) (None, 'RGB', (128, 128)) - >>> map(_info, im.split()) + >>> [_info(ch) for ch in im.split()] [(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))] >>> len(im.convert("1").tobitmap()) 10456 - >>> len(im.tostring()) + >>> len(im.tobytes()) 49152 >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0))) (None, 'RGB', (512, 512)) @@ -157,17 +164,18 @@ def testimage(): def check_module(feature, module): try: - __import__("PIL." + module) + __import__(module) except ImportError: - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") + def check_codec(feature, codec): if codec + "_encoder" not in dir(Image.core): - print "***", feature, "support not installed" + print("***", feature, "support not installed") else: - print "---", feature, "support ok" + print("---", feature, "support ok") if __name__ == "__main__": @@ -175,28 +183,39 @@ if __name__ == "__main__": exit_status = 0 - print "-"*68 - print "PIL", Image.VERSION, "TEST SUMMARY " - print "-"*68 - print "Python modules loaded from", os.path.dirname(Image.__file__) - print "Binary modules loaded from", os.path.dirname(Image.core.__file__) - print "-"*68 - check_module("PIL CORE", "_imaging") - check_module("TKINTER", "_imagingtk") + print("-"*68) + print("Pillow", Image.PILLOW_VERSION, "TEST SUMMARY ") + print("-"*68) + print("Python modules loaded from", os.path.dirname(Image.__file__)) + print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) + print("-"*68) + check_module("PIL CORE", "PIL._imaging") + check_module("TKINTER", "PIL._imagingtk") check_codec("JPEG", "jpeg") check_codec("ZLIB (PNG/ZIP)", "zip") - check_module("FREETYPE2", "_imagingft") - check_module("LITTLECMS", "_imagingcms") - print "-"*68 + check_codec("LIBTIFF", "libtiff") + check_module("FREETYPE2", "PIL._imagingft") + check_module("LITTLECMS2", "PIL._imagingcms") + check_module("WEBP", "PIL._webp") + try: + from PIL import _webp + if _webp.WebPDecoderBuggyAlpha(): + print("***", "Transparent WEBP", "support not installed") + else: + print("---", "Transparent WEBP", "support ok") + except Exception: + pass + print("-"*68) # use doctest to make sure the test program behaves as documented! - import doctest, selftest - print "Running selftest:" + import doctest + import selftest + print("Running selftest:") status = doctest.testmod(selftest) if status[0]: - print "*** %s tests of %d failed." % status + print("*** %s tests of %d failed." % status) exit_status = 1 else: - print "--- %s tests passed." % status[1] + print("--- %s tests passed." % status[1]) sys.exit(exit_status) diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 861a9f554..000000000 --- a/setup.cfg +++ /dev/null @@ -1,5 +0,0 @@ -[egg_info] -tag_build = -tag_date = 0 -tag_svn_revision = 0 - diff --git a/setup.py b/setup.py index 5e1a0a7c1..fb2aeffb3 100644 --- a/setup.py +++ b/setup.py @@ -1,33 +1,91 @@ -#!/usr/bin/env python -# -# Setup script for PIL 1.1.5 and later (repackaged by Chris McDonough and -# Hanno Schlichting for setuptools compatibility) -# -# Usage: python setup.py install -# +# > pyroma . +# ------------------------------ +# Checking . +# Found Pillow +# ------------------------------ +# Final rating: 10/10 +# Your cheese is so fresh most people think it's a cream: Mascarpone +# ------------------------------ +from __future__ import print_function +import glob +import os +import platform as plat +import re +import struct +import sys -import glob, os, re, struct, string, sys +from distutils.command.build_ext import build_ext +from distutils import sysconfig +from setuptools import Extension, setup, find_packages -def libinclude(root): + +_IMAGING = ( + "decode", "encode", "map", "display", "outline", "path") + +_LIB_IMAGING = ( + "Access", "AlphaComposite", "Antialias", "Bands", "BitDecode", "Blend", + "Chops", "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw", + "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode", + "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", + "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix", + "ModeFilter", "MspDecode", "Negative", "Offset", "Pack", + "PackDecode", "Palette", "Paste", "Quant", "QuantOctree", "QuantHash", + "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", + "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", + "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", + "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode") + + +def _add_directory(path, dir, where=None): + if dir is None: + return + dir = os.path.realpath(dir) + if os.path.isdir(dir) and dir not in path: + if where is None: + path.append(dir) + else: + path.insert(where, dir) + + +def _find_include_file(self, include): + for directory in self.compiler.include_dirs: + if os.path.isfile(os.path.join(directory, include)): + return 1 + return 0 + + +def _find_library_file(self, library): + # Fix for 3.2.x <3.2.4, 3.3.0, shared lib extension is the python shared + # lib extension, not the system shared lib extension: e.g. .cpython-33.so + # vs .so. See Python bug http://bugs.python.org/16754 + if 'cpython' in self.compiler.shared_lib_extension: + existing = self.compiler.shared_lib_extension + self.compiler.shared_lib_extension = "." + existing.split('.')[-1] + ret = self.compiler.find_library_file( + self.compiler.library_dirs, library) + self.compiler.shared_lib_extension = existing + return ret + else: + return self.compiler.find_library_file( + self.compiler.library_dirs, library) + + +def _lib_include(root): # map root to (root/lib, root/include) return os.path.join(root, "lib"), os.path.join(root, "include") -# -------------------------------------------------------------------- -# Library pointers. -# -# Use None to look for the libraries in well-known library locations. -# Use a string to specify a single directory, for both the library and -# the include files. Use a tuple to specify separate directories: -# (libpath, includepath). Examples: -# -# JPEG_ROOT = "/home/libraries/jpeg-6b" -# TIFF_ROOT = "/opt/tiff/lib", "/opt/tiff/include" -# -# If you have "lib" and "include" directories under a common parent, -# you can use the "libinclude" helper: -# -# TIFF_ROOT = libinclude("/opt/tiff") +def _read(file): + return open(file, 'rb').read() + +try: + import _tkinter +except ImportError: + _tkinter = None + + +NAME = 'Pillow' +VERSION = '2.3.0' TCL_ROOT = None JPEG_ROOT = None ZLIB_ROOT = None @@ -35,79 +93,51 @@ TIFF_ROOT = None FREETYPE_ROOT = None LCMS_ROOT = None -# FIXME: add mechanism to explicitly *disable* the use of a library - -# -------------------------------------------------------------------- -# Identification - -NAME = "Pillow" -DESCRIPTION = "Python Imaging Library (fork)" -#AUTHOR = "Secret Labs AB (PythonWare)", "info@pythonware.com" -AUTHOR = "Alex Clark (fork author)", "aclark@aclark.net" -#HOMEPAGE = "http://www.pythonware.com/products/pil" -HOMEPAGE = "http://github.com/Pillow" -#DOWNLOAD_URL = "http://effbot.org/downloads/%s-%s.tar.gz" # name, version - -# -------------------------------------------------------------------- -# Core library - -IMAGING = [ - "decode", "encode", "map", "display", "outline", "path", - ] - -LIBIMAGING = [ - "Access", "Antialias", "Bands", "BitDecode", "Blend", "Chops", - "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw", - "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode", - "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", - "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix", - "ModeFilter", "MspDecode", "Negative", "Offset", "Pack", - "PackDecode", "Palette", "Paste", "Quant", "QuantHash", - "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", - "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", - "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", - "XbmEncode", "ZipDecode", "ZipEncode" - ] - -# -------------------------------------------------------------------- - -from distutils import sysconfig -from distutils.command.build_ext import build_ext -from setuptools import setup, find_packages, Extension - -try: - import _tkinter -except ImportError: - _tkinter = None - -def add_directory(path, dir, where=None): - if dir and os.path.isdir(dir) and dir not in path: - if where is None: - path.append(dir) - else: - path.insert(where, dir) - -def find_include_file(self, include): - for directory in self.compiler.include_dirs: - if os.path.isfile(os.path.join(directory, include)): - return 1 - return 0 - -def find_library_file(self, library): - return self.compiler.find_library_file(self.compiler.library_dirs, library) - -def find_version(filename): - for line in open(filename).readlines(): - m = re.search("VERSION\s*=\s*\"([^\"]+)\"", line) - if m: - return m.group(1) - return None - -#VERSION = find_version("PIL/Image.py") -VERSION = "1.0" class pil_build_ext(build_ext): + class feature: + zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = webpmux = None + required = [] + + def require(self, feat): + return feat in self.required + def want(self, feat): + return getattr(self, feat) is None + + def __iter__(self): + for x in dir(self): + if x[1] != '_': + yield x + + feature = feature() + + user_options = build_ext.user_options + [ + ('disable-%s' % x, None, 'Disable support for %s' % x) + for x in feature + ] + [ + ('enable-%s' % x, None, 'Enable support for %s' % x) + for x in feature + ] + + def initialize_options(self): + build_ext.initialize_options(self) + for x in self.feature: + setattr(self, 'disable_%s' % x, None) + setattr(self, 'enable_%s' % x, None) + + def finalize_options(self): + build_ext.finalize_options(self) + for x in self.feature: + if getattr(self, 'disable_%s' % x): + setattr(self.feature, x, False) + if getattr(self, 'enable_%s' % x): + raise ValueError( + 'Conflicting options: --enable-%s and --disable-%s' + % (x, x)) + if getattr(self, 'enable_%s' % x): + self.feature.required.append(x) + def build_extensions(self): global TCL_ROOT @@ -115,36 +145,134 @@ class pil_build_ext(build_ext): library_dirs = [] include_dirs = [] - add_directory(include_dirs, "libImaging") + _add_directory(include_dirs, "libImaging") + + # + # add configured kits + + for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT, + FREETYPE_ROOT, LCMS_ROOT): + if isinstance(root, type(())): + lib_root, include_root = root + else: + lib_root = include_root = root + _add_directory(library_dirs, lib_root) + _add_directory(include_dirs, include_root) + + # respect CFLAGS/LDFLAGS + for k in ('CFLAGS', 'LDFLAGS'): + if k in os.environ: + for match in re.finditer(r'-I([^\s]+)', os.environ[k]): + _add_directory(include_dirs, match.group(1)) + for match in re.finditer(r'-L([^\s]+)', os.environ[k]): + _add_directory(library_dirs, match.group(1)) + + # include, rpath, if set as environment variables: + for k in ('C_INCLUDE_PATH', 'CPATH', 'INCLUDE'): + if k in os.environ: + for d in os.environ[k].split(os.path.pathsep): + _add_directory(include_dirs, d) + + for k in ('LD_RUN_PATH', 'LIBRARY_PATH', 'LIB'): + if k in os.environ: + for d in os.environ[k].split(os.path.pathsep): + _add_directory(library_dirs, d) + + prefix = sysconfig.get_config_var("prefix") + if prefix: + _add_directory(library_dirs, os.path.join(prefix, "lib")) + _add_directory(include_dirs, os.path.join(prefix, "include")) # # add platform directories if sys.platform == "cygwin": # pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory - add_directory(library_dirs, os.path.join( - "/usr/lib", "python%s" % sys.version[:3], "config" - )) + _add_directory(library_dirs, os.path.join( + "/usr/lib", "python%s" % sys.version[:3], "config")) elif sys.platform == "darwin": # attempt to make sure we pick freetype2 over other versions - add_directory(include_dirs, "/sw/include/freetype2") - add_directory(include_dirs, "/sw/lib/freetype2/include") + _add_directory(include_dirs, "/sw/include/freetype2") + _add_directory(include_dirs, "/sw/lib/freetype2/include") # fink installation directories - add_directory(library_dirs, "/sw/lib") - add_directory(include_dirs, "/sw/include") + _add_directory(library_dirs, "/sw/lib") + _add_directory(include_dirs, "/sw/include") # darwin ports installation directories - add_directory(library_dirs, "/opt/local/lib") - add_directory(include_dirs, "/opt/local/include") + _add_directory(library_dirs, "/opt/local/lib") + _add_directory(include_dirs, "/opt/local/include") + + # if homebrew is installed, use its lib and include directories + import subprocess + try: + prefix = subprocess.check_output(['brew', '--prefix']) + if prefix: + prefix = prefix.strip() + _add_directory(library_dirs, os.path.join(prefix, 'lib')) + _add_directory(include_dirs, os.path.join(prefix, 'include')) + + # freetype2 is a key-only brew under opt/ + _add_directory(library_dirs, os.path.join(prefix, 'opt', 'freetype', 'lib')) + _add_directory(include_dirs, os.path.join(prefix, 'opt', 'freetype', 'include')) + except: + pass # homebrew not installed + + # freetype2 ships with X11 (after homebrew, so that homebrew freetype is preferred) + _add_directory(library_dirs, "/usr/X11/lib") + _add_directory(include_dirs, "/usr/X11/include") + + elif sys.platform.startswith("linux"): + for platform_ in (plat.processor(), plat.architecture()[0]): + + if not platform_: + continue + + if platform_ in ["x86_64", "64bit"]: + _add_directory(library_dirs, "/lib64") + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu") + break + elif platform_ in ["i386", "i686", "32bit"]: + _add_directory(library_dirs, "/usr/lib/i386-linux-gnu") + break + elif platform_ in ["aarch64"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/aarch64-linux-gnu") + break + elif platform_ in ["arm", "armv7l"]: + _add_directory(library_dirs, "/usr/lib/arm-linux-gnueabi") + break + elif platform_ in ["ppc64"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/ppc64-linux-gnu") + _add_directory(library_dirs, "/usr/lib/powerpc64-linux-gnu") + break + elif platform_ in ["ppc"]: + _add_directory(library_dirs, "/usr/lib/ppc-linux-gnu") + _add_directory(library_dirs, "/usr/lib/powerpc-linux-gnu") + break + elif platform_ in ["s390x"]: + _add_directory(library_dirs, "/usr/lib64") + _add_directory(library_dirs, "/usr/lib/s390x-linux-gnu") + break + elif platform_ in ["s390"]: + _add_directory(library_dirs, "/usr/lib/s390-linux-gnu") + break + else: + raise ValueError( + "Unable to identify Linux platform: `%s`" % platform_) + + # XXX Kludge. Above /\ we brute force support multiarch. Here we + # try Barry's more general approach. Afterward, something should + # work ;-) + self.add_multiarch_paths() + + elif sys.platform.startswith("netbsd"): + _add_directory(library_dirs, "/usr/pkg/lib") + _add_directory(include_dirs, "/usr/pkg/include") - add_directory(library_dirs, "/usr/local/lib") # FIXME: check /opt/stuff directories here? - prefix = sysconfig.get_config_var("prefix") - if prefix: - add_directory(library_dirs, os.path.join(prefix, "lib")) - add_directory(include_dirs, os.path.join(prefix, "include")) - # # locate tkinter libraries @@ -163,46 +291,32 @@ class pil_build_ext(build_ext): os.path.join("/py" + PYVERSION, "Tcl"), os.path.join("/python" + PYVERSION, "Tcl"), "/Tcl", "/Tcl" + TCLVERSION, "/Tcl" + TCL_VERSION, - os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"), - ] + os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"), ] for TCL_ROOT in roots: TCL_ROOT = os.path.abspath(TCL_ROOT) if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")): # FIXME: use distutils logging (?) - print "--- using Tcl/Tk libraries at", TCL_ROOT - print "--- using Tcl/Tk version", TCL_VERSION - TCL_ROOT = libinclude(TCL_ROOT) + print("--- using Tcl/Tk libraries at", TCL_ROOT) + print("--- using Tcl/Tk version", TCL_VERSION) + TCL_ROOT = _lib_include(TCL_ROOT) break else: TCL_ROOT = None - # - # add configured kits - - for root in (TCL_ROOT, JPEG_ROOT, TCL_ROOT, TIFF_ROOT, ZLIB_ROOT, - FREETYPE_ROOT, LCMS_ROOT): - if isinstance(root, type(())): - lib_root, include_root = root - else: - lib_root = include_root = root - add_directory(library_dirs, lib_root) - add_directory(include_dirs, include_root) - - # # add standard directories # look for tcl specific subdirectory (e.g debian) if _tkinter: tcl_dir = "/usr/include/tcl" + TCL_VERSION if os.path.isfile(os.path.join(tcl_dir, "tk.h")): - add_directory(include_dirs, tcl_dir) + _add_directory(include_dirs, tcl_dir) # standard locations - add_directory(library_dirs, "/usr/local/lib") - add_directory(include_dirs, "/usr/local/include") + _add_directory(library_dirs, "/usr/local/lib") + _add_directory(include_dirs, "/usr/local/include") - add_directory(library_dirs, "/usr/lib") - add_directory(include_dirs, "/usr/include") + _add_directory(library_dirs, "/usr/lib") + _add_directory(include_dirs, "/usr/include") # # insert new dirs *before* default libs, to avoid conflicts @@ -214,69 +328,98 @@ class pil_build_ext(build_ext): # # look for available libraries - class feature: - zlib = jpeg = tiff = freetype = tcl = tk = lcms = None - feature = feature() + feature = self.feature - if find_include_file(self, "zlib.h"): - if find_library_file(self, "z"): - feature.zlib = "z" - elif sys.platform == "win32" and find_library_file(self, "zlib"): - feature.zlib = "zlib" # alternative name + if feature.want('zlib'): + if _find_include_file(self, "zlib.h"): + if _find_library_file(self, "z"): + feature.zlib = "z" + elif sys.platform == "win32" and _find_library_file(self, "zlib"): + feature.zlib = "zlib" # alternative name - if find_include_file(self, "jpeglib.h"): - if find_library_file(self, "jpeg"): - feature.jpeg = "jpeg" - elif sys.platform == "win32" and find_library_file(self, "libjpeg"): - feature.jpeg = "libjpeg" # alternative name + if feature.want('jpeg'): + if _find_include_file(self, "jpeglib.h"): + if _find_library_file(self, "jpeg"): + feature.jpeg = "jpeg" + elif ( + sys.platform == "win32" and + _find_library_file(self, "libjpeg")): + feature.jpeg = "libjpeg" # alternative name - if find_library_file(self, "tiff"): - feature.tiff = "tiff" + if feature.want('tiff'): + if _find_library_file(self, "tiff"): + feature.tiff = "tiff" + if sys.platform == "win32" and _find_library_file(self, "libtiff"): + feature.tiff = "libtiff" + if sys.platform == "darwin" and _find_library_file(self, "libtiff"): + feature.tiff = "libtiff" - if find_library_file(self, "freetype"): - # look for freetype2 include files - freetype_version = 0 - for dir in self.compiler.include_dirs: - if os.path.isfile(os.path.join(dir, "ft2build.h")): - freetype_version = 21 + if feature.want('freetype'): + if _find_library_file(self, "freetype"): + # look for freetype2 include files + freetype_version = 0 + for dir in self.compiler.include_dirs: + if os.path.isfile(os.path.join(dir, "ft2build.h")): + freetype_version = 21 + dir = os.path.join(dir, "freetype2") + break dir = os.path.join(dir, "freetype2") - break - dir = os.path.join(dir, "freetype2") - if os.path.isfile(os.path.join(dir, "ft2build.h")): - freetype_version = 21 - break - if os.path.isdir(os.path.join(dir, "freetype")): - freetype_version = 20 - break - if freetype_version: - feature.freetype = "freetype" - feature.freetype_version = freetype_version - if dir: - add_directory(self.compiler.include_dirs, dir, 0) + if os.path.isfile(os.path.join(dir, "ft2build.h")): + freetype_version = 21 + break + if os.path.isdir(os.path.join(dir, "freetype")): + freetype_version = 20 + break + if freetype_version: + feature.freetype = "freetype" + feature.freetype_version = freetype_version + if dir: + _add_directory(self.compiler.include_dirs, dir, 0) - if find_include_file(self, "lcms.h"): - if find_library_file(self, "lcms"): - feature.lcms = "lcms" + if feature.want('lcms'): + if _find_include_file(self, "lcms2.h"): + if _find_library_file(self, "lcms2"): + feature.lcms = "lcms" - if _tkinter and find_include_file(self, "tk.h"): + if _tkinter and _find_include_file(self, "tk.h"): # the library names may vary somewhat (e.g. tcl84 or tcl8.4) version = TCL_VERSION[0] + TCL_VERSION[2] - if find_library_file(self, "tcl" + version): - feature.tcl = "tcl" + version - elif find_library_file(self, "tcl" + TCL_VERSION): - feature.tcl = "tcl" + TCL_VERSION - if find_library_file(self, "tk" + version): - feature.tk = "tk" + version - elif find_library_file(self, "tk" + TCL_VERSION): - feature.tk = "tk" + TCL_VERSION + if feature.want('tcl'): + if _find_library_file(self, "tcl" + version): + feature.tcl = "tcl" + version + elif _find_library_file(self, "tcl" + TCL_VERSION): + feature.tcl = "tcl" + TCL_VERSION + if feature.want('tk'): + if _find_library_file(self, "tk" + version): + feature.tk = "tk" + version + elif _find_library_file(self, "tk" + TCL_VERSION): + feature.tk = "tk" + TCL_VERSION + + if feature.want('webp'): + if (_find_include_file(self, "webp/encode.h") and + _find_include_file(self, "webp/decode.h")): + if _find_library_file(self, "webp"): # in googles precompiled zip it is call "libwebp" + feature.webp = "webp" + + if feature.want('webpmux'): + if (_find_include_file(self, "webp/mux.h") and + _find_include_file(self, "webp/demux.h")): + if _find_library_file(self, "webpmux") and _find_library_file(self, "webpdemux"): + feature.webpmux = "webpmux" + + for f in feature: + if not getattr(feature, f) and feature.require(f): + raise ValueError( + '--enable-%s requested but %s not found, aborting.' + % (f, f)) # # core library files = ["_imaging.c"] - for file in IMAGING: + for file in _IMAGING: files.append(file + ".c") - for file in LIBIMAGING: + for file in _LIB_IMAGING: files.append(os.path.join("libImaging", file + ".c")) libs = [] @@ -287,14 +430,16 @@ class pil_build_ext(build_ext): if feature.zlib: libs.append(feature.zlib) defs.append(("HAVE_LIBZ", None)) + if feature.tiff: + libs.append(feature.tiff) + defs.append(("HAVE_LIBTIFF", None)) if sys.platform == "win32": libs.extend(["kernel32", "user32", "gdi32"]) - if struct.unpack("h", "\0\1")[0] == 1: + if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) exts = [(Extension( - "_imaging", files, libraries=libs, define_macros=defs - ))] + "PIL._imaging", files, libraries=libs, define_macros=defs))] # # additional libraries @@ -304,54 +449,61 @@ class pil_build_ext(build_ext): if feature.freetype_version == 20: defs.append(("USE_FREETYPE_2_0", None)) exts.append(Extension( - "_imagingft", ["_imagingft.c"], libraries=["freetype"], - define_macros=defs - )) + "PIL._imagingft", ["_imagingft.c"], libraries=["freetype"], + define_macros=defs)) if os.path.isfile("_imagingtiff.c") and feature.tiff: exts.append(Extension( - "_imagingtiff", ["_imagingtiff.c"], libraries=["tiff"] - )) + "PIL._imagingtiff", ["_imagingtiff.c"], libraries=["tiff"])) if os.path.isfile("_imagingcms.c") and feature.lcms: extra = [] if sys.platform == "win32": extra.extend(["user32", "gdi32"]) exts.append(Extension( - "_imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra - )) + "PIL._imagingcms", ["_imagingcms.c"], libraries=["lcms2"] + extra)) + + if os.path.isfile("_webp.c") and feature.webp: + libs = ["webp"] + defs = [] + + if feature.webpmux: + defs.append(("HAVE_WEBPMUX", None)) + libs.append("webpmux") + libs.append("webpdemux") + + exts.append(Extension( + "PIL._webp", ["_webp.c"], libraries=libs, define_macros=defs)) if sys.platform == "darwin": # locate Tcl/Tk frameworks frameworks = [] framework_roots = [ "/Library/Frameworks", - "/System/Library/Frameworks" - ] + "/System/Library/Frameworks"] for root in framework_roots: - if (os.path.exists(os.path.join(root, "Tcl.framework")) and - os.path.exists(os.path.join(root, "Tk.framework"))): - print "--- using frameworks at", root + if ( + os.path.exists(os.path.join(root, "Tcl.framework")) and + os.path.exists(os.path.join(root, "Tk.framework"))): + print("--- using frameworks at %s" % root) frameworks = ["-framework", "Tcl", "-framework", "Tk"] dir = os.path.join(root, "Tcl.framework", "Headers") - add_directory(self.compiler.include_dirs, dir, 0) + _add_directory(self.compiler.include_dirs, dir, 0) dir = os.path.join(root, "Tk.framework", "Headers") - add_directory(self.compiler.include_dirs, dir, 1) + _add_directory(self.compiler.include_dirs, dir, 1) break if frameworks: exts.append(Extension( - "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], - extra_compile_args=frameworks, extra_link_args=frameworks - )) - feature.tcl = feature.tk = 1 # mark as present + "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], + extra_compile_args=frameworks, extra_link_args=frameworks)) + feature.tcl = feature.tk = 1 # mark as present elif feature.tcl and feature.tk: exts.append(Extension( - "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], - libraries=[feature.tcl, feature.tk] - )) + "PIL._imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], + libraries=[feature.tcl, feature.tk])) if os.path.isfile("_imagingmath.c"): - exts.append(Extension("_imagingmath", ["_imagingmath.c"])) + exts.append(Extension("PIL._imagingmath", ["_imagingmath.c"])) self.extensions[:] = exts @@ -369,56 +521,57 @@ class pil_build_ext(build_ext): def summary_report(self, feature, unsafe_zlib): - print "-" * 68 - print "PIL", VERSION, "SETUP SUMMARY" - print "-" * 68 - print "version ", VERSION - v = string.split(sys.version, "[") - print "platform ", sys.platform, string.strip(v[0]) + print("-" * 68) + print("PIL SETUP SUMMARY") + print("-" * 68) + print("version Pillow %s" % VERSION) + v = sys.version.split("[") + print("platform %s %s" % (sys.platform, v[0].strip())) for v in v[1:]: - print " ", string.strip("[" + v) - print "-" * 68 + print(" [%s" % v.strip()) + print("-" * 68) options = [ (feature.tcl and feature.tk, "TKINTER"), (feature.jpeg, "JPEG"), (feature.zlib, "ZLIB (PNG/ZIP)"), - # (feature.tiff, "experimental TIFF G3/G4 read"), + (feature.tiff, "LIBTIFF"), (feature.freetype, "FREETYPE2"), - (feature.lcms, "LITTLECMS"), - ] + (feature.lcms, "LITTLECMS2"), + (feature.webp, "WEBP"), + (feature.webpmux, "WEBPMUX"), ] all = 1 for option in options: if option[0]: - print "---", option[1], "support available" + print("--- %s support available" % option[1]) else: - print "***", option[1], "support not available", + print("*** %s support not available" % option[1]) if option[1] == "TKINTER" and _tkinter: version = _tkinter.TCL_VERSION - print "(Tcl/Tk %s libraries needed)" % version, - print + print("(Tcl/Tk %s libraries needed)" % version) all = 0 if feature.zlib and unsafe_zlib: - print - print "*** Warning: zlib", unsafe_zlib, - print "may contain a security vulnerability." - print "*** Consider upgrading to zlib 1.2.3 or newer." - print "*** See: http://www.kb.cert.org/vuls/id/238678" - print " http://www.kb.cert.org/vuls/id/680620" - print " http://www.gzip.org/zlib/advisory-2002-03-11.txt" - print + print("") + print("*** Warning: zlib", unsafe_zlib) + print("may contain a security vulnerability.") + print("*** Consider upgrading to zlib 1.2.3 or newer.") + print("*** See: http://www.kb.cert.org/vuls/id/238678") + print(" http://www.kb.cert.org/vuls/id/680620") + print(" http://www.gzip.org/zlib/advisory-2002-03-11.txt") + print("") - print "-" * 68 + print("-" * 68) if not all: - print "To add a missing option, make sure you have the required" - print "library, and set the corresponding ROOT variable in the" - print "setup.py script." - print + print("To add a missing option, make sure you have the required") + print("library, and set the corresponding ROOT variable in the") + print("setup.py script.") + print("") - print "To check the build, run the selftest.py script." + print("To check the build, run the selftest.py script.") + print("") def check_zlib_version(self, include_dirs): # look for unsafe versions of zlib @@ -435,43 +588,62 @@ class pil_build_ext(build_ext): if m.group(1) < "1.2.3": return m.group(1) -# -# build! + # http://hg.python.org/users/barry/rev/7e8deab93d5a + def add_multiarch_paths(self): + # Debian/Ubuntu multiarch support. + # https://wiki.ubuntu.com/MultiarchSpec + # self.build_temp + tmpfile = os.path.join(self.build_temp, 'multiarch') + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + ret = os.system( + 'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' % + tmpfile) + try: + if ret >> 8 == 0: + fp = open(tmpfile, 'r') + multiarch_path_component = fp.readline().strip() + _add_directory( + self.compiler.library_dirs, + '/usr/lib/' + multiarch_path_component) + _add_directory( + self.compiler.include_dirs, + '/usr/include/' + multiarch_path_component) + finally: + os.unlink(tmpfile) -if __name__ == "__main__": +setup( + name=NAME, + version=VERSION, + description='Python Imaging Library (Fork)', + long_description=( + _read('README.rst') + b'\n' + + _read('CHANGES.rst')).decode('utf-8'), + author='Alex Clark (fork author)', + author_email='aclark@aclark.net', + url='http://python-imaging.github.io/', + classifiers=[ + "Development Status :: 6 - Mature", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", + "Topic :: Multimedia :: Graphics :: Capture :: Scanners", + "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", + "Topic :: Multimedia :: Graphics :: Graphics Conversion", + "Topic :: Multimedia :: Graphics :: Viewers", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", ], + cmdclass={"build_ext": pil_build_ext}, + ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], + include_package_data=True, + packages=find_packages(), + scripts=glob.glob("Scripts/pil*.py"), + test_suite='PIL.tests', + keywords=["Imaging",], + license='Standard PIL License', + zip_safe=True, + ) - try: - # add necessary to distutils (for backwards compatibility) - from distutils.dist import DistributionMetadata - DistributionMetadata.classifiers = None - DistributionMetadata.download_url = None - DistributionMetadata.platforms = None - except: - pass - - setup( - author=AUTHOR[0], author_email=AUTHOR[1], - classifiers=[ - "Development Status :: 6 - Mature", - "Topic :: Multimedia :: Graphics", - "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", - "Topic :: Multimedia :: Graphics :: Capture :: Scanners", - "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", - "Topic :: Multimedia :: Graphics :: Graphics Conversion", - "Topic :: Multimedia :: Graphics :: Viewers", - ], - cmdclass = {"build_ext": pil_build_ext}, - description=DESCRIPTION, -# download_url=DOWNLOAD_URL % (NAME, VERSION), - ext_modules = [Extension("_imaging", ["_imaging.c"])], # dummy - extra_path = "PIL", - license="Python (MIT style)", - long_description=DESCRIPTION, - name=NAME, - packages=find_packages(), - setup_requires=["setuptools_hg"], - platforms="Python 1.5.2 and later.", - scripts = glob.glob("Scripts/pil*.py"), - url=HOMEPAGE, - version=VERSION, - ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..8d6ea7067 --- /dev/null +++ b/tox.ini @@ -0,0 +1,14 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py26, py27, py32, py33 + +[testenv] +commands = + {envpython} setup.py clean + {envpython} setup.py build_ext --inplace + {envpython} selftest.py + {envpython} Tests/run.py --installed