diff --git a/BUILDME b/BUILDME new file mode 100644 index 000000000..a3c629537 --- /dev/null +++ b/BUILDME @@ -0,0 +1,11 @@ +# $Id$ +# quick build (for lazy programmers). for more information on the +# build process, see the README file. + +if [ "$1" = "clean" ]; then + python setup.py clean + rm -f *.so PIL/*.so +fi + +python setup.py build_ext -i +python selftest.py diff --git a/CHANGES b/CHANGES new file mode 100644 index 000000000..b516d32bb --- /dev/null +++ b/CHANGES @@ -0,0 +1,1767 @@ +-*- 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 new file mode 100644 index 000000000..8846fb945 --- /dev/null +++ b/CONTENTS @@ -0,0 +1,299 @@ +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 new file mode 100644 index 000000000..b03ae458c --- /dev/null +++ b/Docs/effbot.css @@ -0,0 +1,118 @@ +/* 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 new file mode 100644 index 000000000..fbc76a99d --- /dev/null +++ b/Docs/index.html @@ -0,0 +1,103 @@ + + + + + +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 new file mode 100644 index 000000000..28aa4e11b --- /dev/null +++ b/Docs/pythondoc-PIL.ArgImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..1f2008c17 --- /dev/null +++ b/Docs/pythondoc-PIL.BdfFontFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..17fe0d1f1 --- /dev/null +++ b/Docs/pythondoc-PIL.BmpImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..c17a1199d --- /dev/null +++ b/Docs/pythondoc-PIL.BufrStubImagePlugin.html @@ -0,0 +1,20 @@ + + + + +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 new file mode 100644 index 000000000..61e4c9187 --- /dev/null +++ b/Docs/pythondoc-PIL.ContainerIO.html @@ -0,0 +1,87 @@ + + + + +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 new file mode 100644 index 000000000..0dafd34ec --- /dev/null +++ b/Docs/pythondoc-PIL.CurImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..335ac4339 --- /dev/null +++ b/Docs/pythondoc-PIL.DcxImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..262b34632 --- /dev/null +++ b/Docs/pythondoc-PIL.EpsImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..39aab438b --- /dev/null +++ b/Docs/pythondoc-PIL.ExifTags.html @@ -0,0 +1,19 @@ + + + + +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 new file mode 100644 index 000000000..1492f74a2 --- /dev/null +++ b/Docs/pythondoc-PIL.FitsStubImagePlugin.html @@ -0,0 +1,20 @@ + + + + +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 new file mode 100644 index 000000000..0d7faa008 --- /dev/null +++ b/Docs/pythondoc-PIL.FliImagePlugin.html @@ -0,0 +1,25 @@ + + + + +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 new file mode 100644 index 000000000..7f9b82faf --- /dev/null +++ b/Docs/pythondoc-PIL.FontFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..f4631b890 --- /dev/null +++ b/Docs/pythondoc-PIL.FpxImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..a318ed7c5 --- /dev/null +++ b/Docs/pythondoc-PIL.GbrImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..554ffb980 --- /dev/null +++ b/Docs/pythondoc-PIL.GdImageFile.html @@ -0,0 +1,43 @@ + + + + +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 new file mode 100644 index 000000000..06a0401e4 --- /dev/null +++ b/Docs/pythondoc-PIL.GifImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..d9e3d3231 --- /dev/null +++ b/Docs/pythondoc-PIL.GimpGradientFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..421fc9c0f --- /dev/null +++ b/Docs/pythondoc-PIL.GimpPaletteFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..735db2b03 --- /dev/null +++ b/Docs/pythondoc-PIL.GribStubImagePlugin.html @@ -0,0 +1,20 @@ + + + + +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 new file mode 100644 index 000000000..d03591a0f --- /dev/null +++ b/Docs/pythondoc-PIL.Hdf5StubImagePlugin.html @@ -0,0 +1,20 @@ + + + + +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 new file mode 100644 index 000000000..4a0531419 --- /dev/null +++ b/Docs/pythondoc-PIL.IcnsImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..5e2b9230f --- /dev/null +++ b/Docs/pythondoc-PIL.IcoImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..918f74598 --- /dev/null +++ b/Docs/pythondoc-PIL.ImImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..e1a49c01f --- /dev/null +++ b/Docs/pythondoc-PIL.Image.html @@ -0,0 +1,931 @@ + + + + +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 new file mode 100644 index 000000000..2e665b577 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageChops.html @@ -0,0 +1,282 @@ + + + + +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 new file mode 100644 index 000000000..b409ccff9 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageColor.html @@ -0,0 +1,25 @@ + + + + +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 new file mode 100644 index 000000000..1e72665f4 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageDraw.html @@ -0,0 +1,111 @@ + + + + +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 new file mode 100644 index 000000000..e7493d098 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageEnhance.html @@ -0,0 +1,77 @@ + + + + +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 new file mode 100644 index 000000000..6755b69ae --- /dev/null +++ b/Docs/pythondoc-PIL.ImageFile.html @@ -0,0 +1,117 @@ + + + + +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 new file mode 100644 index 000000000..78fc226c1 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageFileIO.html @@ -0,0 +1,43 @@ + + + + +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 new file mode 100644 index 000000000..b67e8223b --- /dev/null +++ b/Docs/pythondoc-PIL.ImageFilter.html @@ -0,0 +1,257 @@ + + + + +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 new file mode 100644 index 000000000..6f1cd885d --- /dev/null +++ b/Docs/pythondoc-PIL.ImageFont.html @@ -0,0 +1,141 @@ + + + + +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 new file mode 100644 index 000000000..6fee03f8b --- /dev/null +++ b/Docs/pythondoc-PIL.ImageGL.html @@ -0,0 +1,24 @@ + + + + +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 new file mode 100644 index 000000000..fb1354e05 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageGrab.html @@ -0,0 +1,37 @@ + + + + +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 new file mode 100644 index 000000000..1554571a0 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageOps.html @@ -0,0 +1,221 @@ + + + + +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 new file mode 100644 index 000000000..67e0d114f --- /dev/null +++ b/Docs/pythondoc-PIL.ImagePalette.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..67743bdfe --- /dev/null +++ b/Docs/pythondoc-PIL.ImagePath.html @@ -0,0 +1,54 @@ + + + + +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 new file mode 100644 index 000000000..848e7a200 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageSequence.html @@ -0,0 +1,32 @@ + + + + +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 new file mode 100644 index 000000000..10a308b33 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageStat.html @@ -0,0 +1,37 @@ + + + + +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 new file mode 100644 index 000000000..d5d4e2381 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageTk.html @@ -0,0 +1,141 @@ + + + + +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 new file mode 100644 index 000000000..6771fb148 --- /dev/null +++ b/Docs/pythondoc-PIL.ImageTransform.html @@ -0,0 +1,140 @@ + + + + +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 new file mode 100644 index 000000000..e1608d15a --- /dev/null +++ b/Docs/pythondoc-PIL.ImageWin.html @@ -0,0 +1,141 @@ + + + + +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 new file mode 100644 index 000000000..7f2071436 --- /dev/null +++ b/Docs/pythondoc-PIL.ImtImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..fe503036a --- /dev/null +++ b/Docs/pythondoc-PIL.IptcImagePlugin.html @@ -0,0 +1,36 @@ + + + + +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 new file mode 100644 index 000000000..fc19e7c46 --- /dev/null +++ b/Docs/pythondoc-PIL.JpegImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..a59530323 --- /dev/null +++ b/Docs/pythondoc-PIL.McIdasImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..fdd746735 --- /dev/null +++ b/Docs/pythondoc-PIL.MicImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..b40d0c28e --- /dev/null +++ b/Docs/pythondoc-PIL.MpegImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..c17e99128 --- /dev/null +++ b/Docs/pythondoc-PIL.MspImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..f56a6b41d --- /dev/null +++ b/Docs/pythondoc-PIL.OleFileIO.html @@ -0,0 +1,39 @@ + + + + +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 new file mode 100644 index 000000000..f89d796f0 --- /dev/null +++ b/Docs/pythondoc-PIL.PSDraw.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..65e4de4e6 --- /dev/null +++ b/Docs/pythondoc-PIL.PaletteFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..d5a3cb394 --- /dev/null +++ b/Docs/pythondoc-PIL.PalmImagePlugin.html @@ -0,0 +1,16 @@ + + + + +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 new file mode 100644 index 000000000..feb686038 --- /dev/null +++ b/Docs/pythondoc-PIL.PcdImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..8b1e41be9 --- /dev/null +++ b/Docs/pythondoc-PIL.PcfFontFile.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..011349a8d --- /dev/null +++ b/Docs/pythondoc-PIL.PcxImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..20e0b3f6e --- /dev/null +++ b/Docs/pythondoc-PIL.PdfImagePlugin.html @@ -0,0 +1,16 @@ + + + + +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 new file mode 100644 index 000000000..28b06140c --- /dev/null +++ b/Docs/pythondoc-PIL.PixarImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..ccd8e9325 --- /dev/null +++ b/Docs/pythondoc-PIL.PngImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..24789682a --- /dev/null +++ b/Docs/pythondoc-PIL.PpmImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..7ed4f8084 --- /dev/null +++ b/Docs/pythondoc-PIL.PsdImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..847b35efd --- /dev/null +++ b/Docs/pythondoc-PIL.SgiImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..733f04419 --- /dev/null +++ b/Docs/pythondoc-PIL.SpiderImagePlugin.html @@ -0,0 +1,28 @@ + + + + +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 new file mode 100644 index 000000000..fb3cdd987 --- /dev/null +++ b/Docs/pythondoc-PIL.SunImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..996633b98 --- /dev/null +++ b/Docs/pythondoc-PIL.TarIO.html @@ -0,0 +1,35 @@ + + + + +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 new file mode 100644 index 000000000..f91381e95 --- /dev/null +++ b/Docs/pythondoc-PIL.TgaImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..56546d260 --- /dev/null +++ b/Docs/pythondoc-PIL.TiffImagePlugin.html @@ -0,0 +1,34 @@ + + + + +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 new file mode 100644 index 000000000..15d8c98a1 --- /dev/null +++ b/Docs/pythondoc-PIL.TiffTags.html @@ -0,0 +1,19 @@ + + + + +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 new file mode 100644 index 000000000..3b0b54633 --- /dev/null +++ b/Docs/pythondoc-PIL.WalImageFile.html @@ -0,0 +1,28 @@ + + + + +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 new file mode 100644 index 000000000..51bf10300 --- /dev/null +++ b/Docs/pythondoc-PIL.WmfImagePlugin.html @@ -0,0 +1,31 @@ + + + + +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 new file mode 100644 index 000000000..30282acab --- /dev/null +++ b/Docs/pythondoc-PIL.XVThumbImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..682e05f77 --- /dev/null +++ b/Docs/pythondoc-PIL.XbmImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..218f70477 --- /dev/null +++ b/Docs/pythondoc-PIL.XpmImagePlugin.html @@ -0,0 +1,23 @@ + + + + +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 new file mode 100644 index 000000000..5a48895d1 --- /dev/null +++ b/Images/courB08.bdf @@ -0,0 +1,2676 @@ +STARTFONT 2.1 +COMMENT $XConsortium: courB08.bdf,v 1.5 94/04/10 21:46:11 gildea Exp $ +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 to use these trademarks is hereby granted only in association with the +COMMENT images described in this file. +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 +COMMENT that both those copyright notices and this permission notice appear +COMMENT in supporting documentation, and that the names of Adobe Systems and +COMMENT Digital Equipment Corporation not be used in advertising or +COMMENT publicity pertaining to distribution of the software without +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 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 +COMMENT SYSTEMS AND DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY SPECIAL, +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 +FONT -Adobe-Courier-Bold-R-Normal--11-80-100-100-M-60-ISO8859-1 +SIZE 8 100 100 +FONTBOUNDINGBOX 8 11 -1 -2 +STARTPROPERTIES 22 +COMMENT Begin LogicalFontDescription +FONTNAME_REGISTRY "" +FAMILY_NAME "Courier" +FOUNDRY "Adobe" +WEIGHT_NAME "Bold" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 11 +POINT_SIZE 80 +RESOLUTION_X 100 +RESOLUTION_Y 100 +SPACING "M" +AVERAGE_WIDTH 60 +CHARSET_REGISTRY "ISO8859" +CHARSET_ENCODING "1" +COMMENT END LogicalFontDescription +CHARSET_COLLECTIONS "ASCII ISO8859-1 ADOBE-STANDARD" +FACE_NAME "Courier Bold" +COPYRIGHT "Copyright (c) 1987 Adobe Systems, Inc., Portions Copyright 1988 Digital Equipment Corp." +COMMENT ***** end of inserted font properties +FONT_ASCENT 8 +FONT_DESCENT 2 +CAP_HEIGHT 6 +X_HEIGHT 5 +ENDPROPERTIES +CHARS 194 +STARTCHAR space +ENCODING 32 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 1 1 0 0 +BITMAP +00 +ENDCHAR +STARTCHAR exclam +ENCODING 33 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 6 1 0 +BITMAP +C0 +C0 +C0 +C0 +00 +C0 +ENDCHAR +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 3 1 3 +BITMAP +A0 +A0 +A0 +ENDCHAR +STARTCHAR numbersign +ENCODING 35 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 -1 +BITMAP +50 +50 +F8 +50 +50 +F8 +50 +50 +ENDCHAR +STARTCHAR dollar +ENCODING 36 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 9 0 -1 +BITMAP +20 +78 +C8 +F0 +78 +18 +D8 +F0 +20 +ENDCHAR +STARTCHAR percent +ENCODING 37 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +E0 +A8 +F0 +20 +78 +A8 +38 +ENDCHAR +STARTCHAR ampersand +ENCODING 38 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +38 +60 +30 +7C +D8 +7C +ENDCHAR +STARTCHAR quoteright +ENCODING 39 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 3 1 4 +BITMAP +60 +40 +80 +ENDCHAR +STARTCHAR parenleft +ENCODING 40 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 8 1 -1 +BITMAP +20 +40 +C0 +C0 +C0 +C0 +40 +20 +ENDCHAR +STARTCHAR parenright +ENCODING 41 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 8 1 -1 +BITMAP +80 +40 +60 +60 +60 +60 +40 +80 +ENDCHAR +STARTCHAR asterisk +ENCODING 42 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 4 0 3 +BITMAP +20 +F0 +60 +90 +ENDCHAR +STARTCHAR plus +ENCODING 43 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 1 +BITMAP +20 +20 +F8 +20 +20 +ENDCHAR +STARTCHAR comma +ENCODING 44 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 3 1 -2 +BITMAP +60 +40 +80 +ENDCHAR +STARTCHAR minus +ENCODING 45 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 1 0 3 +BITMAP +F8 +ENDCHAR +STARTCHAR period +ENCODING 46 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 1 1 0 +BITMAP +C0 +ENDCHAR +STARTCHAR slash +ENCODING 47 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 8 1 -1 +BITMAP +10 +10 +20 +20 +40 +40 +80 +80 +ENDCHAR +STARTCHAR zero +ENCODING 48 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR one +ENCODING 49 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +30 +F0 +30 +30 +30 +30 +FC +ENDCHAR +STARTCHAR two +ENCODING 50 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +18 +30 +60 +D8 +F8 +ENDCHAR +STARTCHAR three +ENCODING 51 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +18 +70 +18 +D8 +70 +ENDCHAR +STARTCHAR four +ENCODING 52 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +18 +38 +58 +D8 +FC +18 +18 +ENDCHAR +STARTCHAR five +ENCODING 53 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +F8 +C0 +F0 +D8 +18 +98 +F0 +ENDCHAR +STARTCHAR six +ENCODING 54 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +C0 +F0 +D8 +D8 +70 +ENDCHAR +STARTCHAR seven +ENCODING 55 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +F8 +D8 +18 +30 +30 +60 +60 +ENDCHAR +STARTCHAR eight +ENCODING 56 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +D8 +70 +D8 +D8 +70 +ENDCHAR +STARTCHAR nine +ENCODING 57 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +70 +D8 +D8 +78 +18 +D8 +70 +ENDCHAR +STARTCHAR colon +ENCODING 58 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 4 1 0 +BITMAP +C0 +00 +00 +C0 +ENDCHAR +STARTCHAR semicolon +ENCODING 59 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 6 0 -2 +BITMAP +60 +00 +00 +60 +40 +80 +ENDCHAR +STARTCHAR less +ENCODING 60 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 5 0 1 +BITMAP +30 +60 +C0 +60 +30 +ENDCHAR +STARTCHAR equal +ENCODING 61 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 3 0 2 +BITMAP +F0 +00 +F0 +ENDCHAR +STARTCHAR greater +ENCODING 62 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 5 1 1 +BITMAP +C0 +60 +30 +60 +C0 +ENDCHAR +STARTCHAR question +ENCODING 63 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +70 +98 +30 +60 +00 +60 +ENDCHAR +STARTCHAR at +ENCODING 64 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 -1 +BITMAP +70 +C8 +98 +A8 +A8 +9C +C0 +70 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +F8 +6C +78 +6C +6C +F8 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +78 +D8 +C0 +C0 +D8 +70 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +F8 +6C +6C +6C +6C +F8 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +FC +60 +78 +60 +6C +FC +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +FC +60 +78 +60 +60 +F0 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +70 +D8 +C0 +F8 +D8 +78 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +EE +6C +7C +6C +6C +EE +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 6 0 0 +BITMAP +F0 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +3C +18 +18 +D8 +D8 +70 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +EC +68 +70 +78 +6C +F6 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +F0 +60 +60 +60 +6C +FC +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +C4 +6C +6C +7C +54 +D4 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +EE +74 +74 +6C +6C +E4 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +F8 +6C +6C +78 +60 +F0 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 -1 +BITMAP +70 +D8 +D8 +D8 +D8 +70 +18 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +F8 +6C +6C +78 +6C +F6 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +78 +C8 +F0 +38 +98 +F0 +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +FC +B4 +30 +30 +30 +78 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +EE +6C +6C +6C +6C +38 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +EE +6C +28 +38 +38 +10 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +D6 +54 +54 +7C +38 +28 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 0 0 +BITMAP +CC +78 +30 +30 +78 +CC +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +E6 +66 +3C +18 +18 +3C +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +F8 +D8 +30 +60 +D8 +F8 +ENDCHAR +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 8 1 -1 +BITMAP +E0 +C0 +C0 +C0 +C0 +C0 +C0 +E0 +ENDCHAR +STARTCHAR backslash +ENCODING 92 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 8 0 -1 +BITMAP +80 +80 +40 +40 +20 +20 +10 +10 +ENDCHAR +STARTCHAR bracketright +ENCODING 93 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 8 1 -1 +BITMAP +E0 +60 +60 +60 +60 +60 +60 +E0 +ENDCHAR +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 3 0 4 +BITMAP +20 +70 +D8 +ENDCHAR +STARTCHAR underscore +ENCODING 95 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 1 0 -2 +BITMAP +FC +ENDCHAR +STARTCHAR quoteleft +ENCODING 96 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 3 1 4 +BITMAP +C0 +40 +20 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 0 0 +BITMAP +70 +D8 +78 +D8 +FC +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 -1 0 +BITMAP +E0 +60 +78 +6C +6C +6C +F8 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +70 +D8 +C0 +D8 +70 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +38 +18 +78 +D8 +D8 +D8 +7C +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +70 +D8 +F8 +C0 +78 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +38 +60 +F8 +60 +60 +60 +F8 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 -2 +BITMAP +6C +D8 +D8 +D8 +78 +18 +F0 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 -1 0 +BITMAP +E0 +60 +78 +6C +6C +6C +6C +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +30 +00 +F0 +30 +30 +30 +FC +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 9 0 -2 +BITMAP +30 +00 +F0 +30 +30 +30 +30 +30 +E0 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +E0 +60 +6C +78 +70 +78 +6E +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +F0 +30 +30 +30 +30 +30 +FC +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 -1 0 +BITMAP +F8 +7C +54 +54 +54 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 -1 0 +BITMAP +D8 +6C +6C +6C +6C +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 -1 -2 +BITMAP +F8 +6C +6C +6C +78 +60 +F0 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 -2 +BITMAP +6C +D8 +D8 +D8 +78 +18 +3C +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 0 0 +BITMAP +DC +74 +60 +60 +F0 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 0 0 +BITMAP +78 +E0 +78 +1C +F8 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +60 +60 +F8 +60 +60 +6C +38 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 5 -1 0 +BITMAP +EC +6C +6C +6C +3E +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 -1 0 +BITMAP +EC +6C +38 +38 +10 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 5 -1 0 +BITMAP +D6 +54 +7C +3C +28 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 0 0 +BITMAP +EC +78 +30 +78 +DC +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 -2 +BITMAP +EE +6C +6C +28 +38 +30 +E0 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +F8 +B0 +60 +D8 +F8 +ENDCHAR +STARTCHAR braceleft +ENCODING 123 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 8 1 -1 +BITMAP +30 +60 +60 +C0 +60 +60 +60 +30 +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 1 7 2 -1 +BITMAP +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR braceright +ENCODING 125 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 8 0 -1 +BITMAP +C0 +60 +60 +30 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 2 0 3 +BITMAP +68 +B0 +ENDCHAR +STARTCHAR exclamdown +ENCODING 161 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 7 1 -2 +BITMAP +C0 +00 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR cent +ENCODING 162 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 -1 +BITMAP +20 +20 +78 +C8 +C0 +78 +20 +20 +ENDCHAR +STARTCHAR sterling +ENCODING 163 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +38 +68 +20 +F8 +20 +64 +F8 +ENDCHAR +STARTCHAR currency +ENCODING 164 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 1 +BITMAP +88 +70 +50 +70 +88 +ENDCHAR +STARTCHAR yen +ENCODING 165 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +CC +48 +FC +30 +FC +30 +78 +ENDCHAR +STARTCHAR brokenbar +ENCODING 166 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 1 9 2 -2 +BITMAP +80 +80 +80 +80 +00 +80 +80 +80 +80 +ENDCHAR +STARTCHAR section +ENCODING 167 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 -1 +BITMAP +78 +48 +60 +90 +48 +30 +90 +F0 +ENDCHAR +STARTCHAR dieresis +ENCODING 168 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 1 1 5 +BITMAP +A0 +ENDCHAR +STARTCHAR copyright +ENCODING 169 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +30 +48 +B4 +A4 +B4 +48 +30 +ENDCHAR +STARTCHAR ordfeminine +ENCODING 170 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 5 1 2 +BITMAP +E0 +10 +D0 +00 +F0 +ENDCHAR +STARTCHAR guillemotleft +ENCODING 171 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 5 -1 0 +BITMAP +36 +6C +D8 +6C +36 +ENDCHAR +STARTCHAR logicalnot +ENCODING 172 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 3 0 2 +BITMAP +F8 +08 +08 +ENDCHAR +STARTCHAR hyphen +ENCODING 173 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 1 0 3 +BITMAP +F8 +ENDCHAR +STARTCHAR registered +ENCODING 174 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 0 0 +BITMAP +38 +44 +BA +B2 +AA +44 +38 +ENDCHAR +STARTCHAR macron +ENCODING 175 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 1 0 5 +BITMAP +F0 +ENDCHAR +STARTCHAR degree +ENCODING 176 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 3 0 4 +BITMAP +60 +90 +60 +ENDCHAR +STARTCHAR plusminus +ENCODING 177 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 6 0 0 +BITMAP +20 +20 +F8 +20 +00 +F8 +ENDCHAR +STARTCHAR twosuperior +ENCODING 178 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 4 1 3 +BITMAP +60 +A0 +40 +E0 +ENDCHAR +STARTCHAR threesuperior +ENCODING 179 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 4 1 3 +BITMAP +E0 +40 +20 +C0 +ENDCHAR +STARTCHAR acute +ENCODING 180 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 2 2 5 +BITMAP +40 +80 +ENDCHAR +STARTCHAR mu +ENCODING 181 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 -2 +BITMAP +EC +6C +6C +6C +7E +40 +40 +ENDCHAR +STARTCHAR paragraph +ENCODING 182 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 -1 +BITMAP +7C +A8 +A8 +68 +28 +28 +28 +6C +ENDCHAR +STARTCHAR periodcentered +ENCODING 183 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 2 1 1 3 +BITMAP +C0 +ENDCHAR +STARTCHAR cedilla +ENCODING 184 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 3 1 -2 +BITMAP +40 +20 +C0 +ENDCHAR +STARTCHAR onesuperior +ENCODING 185 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 3 4 1 3 +BITMAP +C0 +40 +40 +E0 +ENDCHAR +STARTCHAR ordmasculine +ENCODING 186 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 5 1 2 +BITMAP +60 +90 +60 +00 +F0 +ENDCHAR +STARTCHAR guillemotright +ENCODING 187 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 5 -1 0 +BITMAP +D8 +6C +36 +6C +D8 +ENDCHAR +STARTCHAR onequarter +ENCODING 188 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +C0 +44 +48 +F4 +2C +5E +04 +ENDCHAR +STARTCHAR onehalf +ENCODING 189 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +C0 +44 +48 +F6 +2A +44 +0E +ENDCHAR +STARTCHAR threequarters +ENCODING 190 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +E0 +44 +28 +D4 +2C +5E +04 +ENDCHAR +STARTCHAR questiondown +ENCODING 191 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 -2 +BITMAP +30 +00 +30 +30 +60 +C8 +70 +ENDCHAR +STARTCHAR Agrave +ENCODING 192 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +20 +10 +00 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR Aacute +ENCODING 193 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +10 +20 +00 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR Acircumflex +ENCODING 194 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +10 +28 +00 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR Atilde +ENCODING 195 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +34 +48 +00 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR Adieresis +ENCODING 196 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +28 +00 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR Aring +ENCODING 197 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +30 +48 +30 +78 +38 +28 +7C +6C +EE +ENDCHAR +STARTCHAR AE +ENCODING 198 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +7E +3A +6C +78 +DA +DE +ENDCHAR +STARTCHAR Ccedilla +ENCODING 199 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 -2 +BITMAP +78 +D8 +C0 +C0 +D8 +70 +10 +60 +ENDCHAR +STARTCHAR Egrave +ENCODING 200 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 9 -1 0 +BITMAP +20 +10 +00 +FC +64 +78 +60 +6C +FC +ENDCHAR +STARTCHAR Eacute +ENCODING 201 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 9 -1 0 +BITMAP +10 +20 +00 +FC +64 +78 +60 +6C +FC +ENDCHAR +STARTCHAR Ecircumflex +ENCODING 202 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 9 -1 0 +BITMAP +20 +50 +00 +FC +64 +78 +60 +6C +FC +ENDCHAR +STARTCHAR Edieresis +ENCODING 203 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 -1 0 +BITMAP +50 +00 +FC +64 +78 +60 +6C +FC +ENDCHAR +STARTCHAR Igrave +ENCODING 204 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 9 0 0 +BITMAP +40 +20 +00 +F0 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR Iacute +ENCODING 205 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 9 0 0 +BITMAP +20 +40 +00 +F0 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR Icircumflex +ENCODING 206 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 9 0 0 +BITMAP +40 +A0 +00 +F0 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR Idieresis +ENCODING 207 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 4 8 0 0 +BITMAP +A0 +00 +F0 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR Eth +ENCODING 208 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +F8 +6C +F4 +64 +6C +F8 +ENDCHAR +STARTCHAR Ntilde +ENCODING 209 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +34 +48 +00 +EE +64 +74 +7C +6C +EC +ENDCHAR +STARTCHAR Ograve +ENCODING 210 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 9 0 0 +BITMAP +40 +20 +00 +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR Oacute +ENCODING 211 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 9 0 0 +BITMAP +20 +40 +00 +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR Ocircumflex +ENCODING 212 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 9 0 0 +BITMAP +20 +50 +00 +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR Otilde +ENCODING 213 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 9 0 0 +BITMAP +68 +90 +00 +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR Odieresis +ENCODING 214 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +50 +00 +70 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR multiply +ENCODING 215 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 1 +BITMAP +88 +50 +20 +50 +88 +ENDCHAR +STARTCHAR Oslash +ENCODING 216 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +3A +6C +7C +6C +6C +B8 +ENDCHAR +STARTCHAR Ugrave +ENCODING 217 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +20 +10 +00 +EE +6C +6C +6C +6C +38 +ENDCHAR +STARTCHAR Uacute +ENCODING 218 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +08 +10 +00 +EE +6C +6C +6C +6C +38 +ENDCHAR +STARTCHAR Ucircumflex +ENCODING 219 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +10 +28 +00 +EE +6C +6C +6C +6C +38 +ENDCHAR +STARTCHAR Udieresis +ENCODING 220 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +28 +00 +EE +6C +6C +6C +6C +38 +ENDCHAR +STARTCHAR Yacute +ENCODING 221 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 0 +BITMAP +04 +08 +00 +E6 +66 +3C +18 +18 +3C +ENDCHAR +STARTCHAR Thorn +ENCODING 222 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 6 -1 0 +BITMAP +E0 +78 +6C +6C +78 +E0 +ENDCHAR +STARTCHAR germandbls +ENCODING 223 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 6 -1 0 +BITMAP +38 +68 +7C +66 +66 +EC +ENDCHAR +STARTCHAR agrave +ENCODING 224 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +20 +10 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR aacute +ENCODING 225 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +10 +20 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR acircumflex +ENCODING 226 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +20 +50 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR atilde +ENCODING 227 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +68 +90 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR adieresis +ENCODING 228 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +50 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR aring +ENCODING 229 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 9 0 0 +BITMAP +30 +48 +30 +00 +70 +98 +78 +D8 +FC +ENDCHAR +STARTCHAR ae +ENCODING 230 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 -1 0 +BITMAP +6C +B4 +7C +B0 +DC +ENDCHAR +STARTCHAR ccedilla +ENCODING 231 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 -2 +BITMAP +70 +D8 +C0 +D8 +70 +10 +60 +ENDCHAR +STARTCHAR egrave +ENCODING 232 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +40 +20 +00 +70 +D8 +F8 +C0 +78 +ENDCHAR +STARTCHAR eacute +ENCODING 233 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +40 +00 +70 +D8 +F8 +C0 +78 +ENDCHAR +STARTCHAR ecircumflex +ENCODING 234 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +50 +00 +70 +D8 +F8 +C0 +78 +ENDCHAR +STARTCHAR edieresis +ENCODING 235 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +50 +00 +70 +D8 +F8 +C0 +78 +ENDCHAR +STARTCHAR igrave +ENCODING 236 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +20 +10 +00 +70 +30 +30 +30 +FC +ENDCHAR +STARTCHAR iacute +ENCODING 237 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +10 +20 +00 +70 +30 +30 +30 +FC +ENDCHAR +STARTCHAR icircumflex +ENCODING 238 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 8 0 0 +BITMAP +20 +50 +00 +70 +30 +30 +30 +FC +ENDCHAR +STARTCHAR idieresis +ENCODING 239 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +50 +00 +70 +30 +30 +30 +FC +ENDCHAR +STARTCHAR eth +ENCODING 240 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +D0 +60 +B0 +78 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR ntilde +ENCODING 241 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +34 +48 +00 +D8 +6C +6C +6C +6E +ENDCHAR +STARTCHAR ograve +ENCODING 242 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +40 +20 +00 +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR oacute +ENCODING 243 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +40 +00 +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR ocircumflex +ENCODING 244 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +50 +00 +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR otilde +ENCODING 245 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +68 +90 +00 +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR odieresis +ENCODING 246 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +50 +00 +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR divide +ENCODING 247 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 5 0 1 +BITMAP +20 +00 +F8 +00 +20 +ENDCHAR +STARTCHAR oslash +ENCODING 248 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 5 7 0 -1 +BITMAP +08 +70 +D8 +F8 +D8 +70 +80 +ENDCHAR +STARTCHAR ugrave +ENCODING 249 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +20 +10 +00 +EC +6C +6C +6C +3E +ENDCHAR +STARTCHAR uacute +ENCODING 250 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +10 +20 +00 +EC +6C +6C +6C +3E +ENDCHAR +STARTCHAR ucircumflex +ENCODING 251 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 8 -1 0 +BITMAP +10 +28 +00 +EC +6C +6C +6C +3E +ENDCHAR +STARTCHAR udieresis +ENCODING 252 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +28 +00 +EC +6C +6C +6C +3E +ENDCHAR +STARTCHAR yacute +ENCODING 253 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 10 -1 -2 +BITMAP +08 +10 +00 +EE +6C +6C +28 +38 +30 +F0 +ENDCHAR +STARTCHAR thorn +ENCODING 254 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 9 -1 -2 +BITMAP +E0 +60 +78 +6C +6C +6C +78 +60 +F0 +ENDCHAR +STARTCHAR ydieresis +ENCODING 255 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 9 -1 -2 +BITMAP +28 +00 +EE +6C +6C +28 +38 +30 +F0 +ENDCHAR +STARTCHAR trademark +ENCODING 8482 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 4 -1 3 +BITMAP +F6 +5E +5A +5A +ENDCHAR +STARTCHAR perthousand +ENCODING 8240 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 7 0 0 +BITMAP +60 +A8 +D0 +60 +6C +B4 +D8 +ENDCHAR +STARTCHAR oe +ENCODING 339 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 6 5 0 0 +BITMAP +6C +B4 +BC +B0 +6C +ENDCHAR +STARTCHAR fl +ENCODING 64258 +SWIDTH 600 0 +DWIDTH 6 0 +BBX 7 7 -1 0 +BITMAP +3C +6C +FC +6C +6C +6C +FE +ENDCHAR +ENDFONT diff --git a/Images/courB08.pbm b/Images/courB08.pbm new file mode 100644 index 000000000..aedce1a31 Binary files /dev/null and b/Images/courB08.pbm differ diff --git a/Images/courB08.pil b/Images/courB08.pil new file mode 100644 index 000000000..28982c0fa Binary files /dev/null and b/Images/courB08.pil differ diff --git a/Images/lena.gif b/Images/lena.gif new file mode 100644 index 000000000..64a9b93c8 Binary files /dev/null and b/Images/lena.gif differ diff --git a/Images/lena.jpg b/Images/lena.jpg new file mode 100644 index 000000000..ed01c4e67 Binary files /dev/null and b/Images/lena.jpg differ diff --git a/Images/lena.png b/Images/lena.png new file mode 100644 index 000000000..192875931 Binary files /dev/null and b/Images/lena.png differ diff --git a/Images/lena.ppm b/Images/lena.ppm new file mode 100644 index 000000000..06a398ea5 Binary files /dev/null and b/Images/lena.ppm differ diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..aaed01cdd --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,7 @@ +exclude .hgignore +exclude .hgtags +exclude BUILDME.bat +exclude make-manifest.py +exclude SHIP +exclude SHIP.bat +recursive-exclude Tests * diff --git a/PIL.egg-info/PKG-INFO b/PIL.egg-info/PKG-INFO new file mode 100644 index 000000000..c27993fb2 --- /dev/null +++ b/PIL.egg-info/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.0 +Name: PIL +Version: 1.1.7 +Summary: Python Imaging Library +Home-page: http://www.pythonware.com/products/pil +Author: Secret Labs AB (PythonWare) +Author-email: info@pythonware.com +License: Python (MIT style) +Download-URL: http://effbot.org/downloads/PIL-1.1.7.tar.gz +Description: Python Imaging Library +Platform: Python 1.5.2 and later. +Classifier: Development Status :: 6 - Mature +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers diff --git a/PIL.egg-info/SOURCES.txt b/PIL.egg-info/SOURCES.txt new file mode 100644 index 000000000..275ff0f19 --- /dev/null +++ b/PIL.egg-info/SOURCES.txt @@ -0,0 +1,283 @@ +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 new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/PIL.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/PIL.egg-info/top_level.txt b/PIL.egg-info/top_level.txt new file mode 100644 index 000000000..8eb445d00 --- /dev/null +++ b/PIL.egg-info/top_level.txt @@ -0,0 +1,2 @@ +PIL +_imaging diff --git a/PIL.pth b/PIL.pth new file mode 100644 index 000000000..b338169ce --- /dev/null +++ b/PIL.pth @@ -0,0 +1 @@ +PIL diff --git a/PIL/ArgImagePlugin.py b/PIL/ArgImagePlugin.py new file mode 100644 index 000000000..815fcea33 --- /dev/null +++ b/PIL/ArgImagePlugin.py @@ -0,0 +1,504 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# ARG animation support code +# +# history: +# 1996-12-30 fl Created +# 1996-01-06 fl Added safe scripting environment +# 1996-01-10 fl Added JHDR, UHDR and sYNC support +# 2005-03-02 fl Removed AAPP and ARUN support +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.4" + +import Image, ImageFile, ImagePalette + +from PngImagePlugin import i16, i32, ChunkStream, _MODES + +MAGIC = "\212ARG\r\n\032\n" + +# -------------------------------------------------------------------- +# ARG parser + +class ArgStream(ChunkStream): + "Parser callbacks for ARG data" + + def __init__(self, fp): + + ChunkStream.__init__(self, fp) + + self.eof = 0 + + self.im = None + self.palette = None + + self.__reset() + + def __reset(self): + + # reset decoder state (called on init and sync) + + self.count = 0 + self.id = None + self.action = ("NONE",) + + self.images = {} + self.names = {} + + + def chunk_AHDR(self, offset, bytes): + "AHDR -- animation header" + + # assertions + if self.count != 0: + 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]))] + except: + raise SyntaxError, "unknown ARG mode" + + if Image.DEBUG: + print "AHDR size", self.size + print "AHDR mode", self.mode, self.rawmode + + return s + + def chunk_AFRM(self, offset, bytes): + "AFRM -- next frame follows" + + # assertions + if self.count != 0: + raise SyntaxError, "misplaced AFRM chunk" + + self.show = 1 + self.id = 0 + self.count = 1 + self.repair = None + + s = self.fp.read(bytes) + if len(s) >= 2: + self.id = i16(s) + if len(s) >= 4: + self.count = i16(s[2:4]) + if len(s) >= 6: + self.repair = i16(s[4:6]) + else: + self.repair = None + + if Image.DEBUG: + print "AFRM", self.id, self.count + + return s + + def chunk_ADEF(self, offset, bytes): + "ADEF -- store image" + + # assertions + if self.count != 0: + raise SyntaxError, "misplaced ADEF chunk" + + self.show = 0 + self.id = 0 + self.count = 1 + self.repair = None + + s = self.fp.read(bytes) + if len(s) >= 2: + self.id = i16(s) + if len(s) >= 4: + self.count = i16(s[2:4]) + + if Image.DEBUG: + print "ADEF", self.id, self.count + + return s + + def chunk_NAME(self, offset, bytes): + "NAME -- name the current image" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced NAME chunk" + + name = self.fp.read(bytes) + self.names[self.id] = name + + return name + + def chunk_AEND(self, offset, bytes): + "AEND -- end of animation" + + if Image.DEBUG: + print "AEND" + + self.eof = 1 + + 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]))] + except: + raise SyntaxError, "unknown image mode" + + if full: + if ord(s[12]): + pass # interlace not yet supported + if ord(s[11]): + raise SyntaxError, "unknown filter category" + + return size, mode, rawmode + + def chunk_PAST(self, offset, bytes): + "PAST -- paste one image into another" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced PAST chunk" + + if self.repair is not None: + # we must repair the target image before we + # start pasting + + # brute force; a better solution would be to + # update only the dirty rectangles in images[id]. + # note that if images[id] doesn't exist, it must + # be created + + self.images[self.id] = self.images[self.repair].copy() + self.repair = None + + s = self.fp.read(bytes) + im = self.images[i16(s)] + x, y = i32(s[2:6]), i32(s[6:10]) + bbox = x, y, im.size[0]+x, im.size[1]+y + + if im.mode in ["RGBA"]: + # paste with transparency + # FIXME: should handle P+transparency as well + self.images[self.id].paste(im, bbox, im) + else: + # paste without transparency + self.images[self.id].paste(im, bbox) + + self.action = ("PAST",) + self.__store() + + return s + + def chunk_BLNK(self, offset, bytes): + "BLNK -- create blank image" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced BLNK chunk" + + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # store image (FIXME: handle colour) + self.action = ("BLNK",) + self.im = Image.core.fill(mode, size, 0) + self.__store() + + return s + + def chunk_IHDR(self, offset, bytes): + "IHDR -- full image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced IHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s) + + # decode and store image + self.action = ("IHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.zip_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_DHDR(self, offset, bytes): + "DHDR -- delta image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced DHDR chunk" + + s = self.fp.read(bytes) + + size, mode, rawmode = self.__getmodesize(s) + + # delta header + diff = ord(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 + + # FIXME: decode and apply image + self.action = ("DHDR", diff, bbox) + + # setup decoder + self.im = Image.core.new(mode, size) + + self.decoder = Image.core.zip_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + + self.data = "" + + return s + + def chunk_JHDR(self, offset, bytes): + "JHDR -- JPEG image follows" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced JHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # decode and store image + self.action = ("JHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.jpeg_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_UHDR(self, offset, bytes): + "UHDR -- uncompressed image data follows (EXPERIMENTAL)" + + # assertions + if self.count == 0: + raise SyntaxError, "misplaced UHDR chunk" + + # image header + s = self.fp.read(bytes) + size, mode, rawmode = self.__getmodesize(s, 0) + + # decode and store image + self.action = ("UHDR",) + self.im = Image.core.new(mode, size) + self.decoder = Image.core.raw_decoder(rawmode) + self.decoder.setimage(self.im, (0,0) + size) + self.data = "" + + return s + + def chunk_IDAT(self, offset, bytes): + "IDAT -- image data block" + + # pass compressed chunks through the decoder + s = self.fp.read(bytes) + self.data = self.data + s + n, e = self.decoder.decode(self.data) + if n < 0: + # end of image + if e < 0: + raise IOError, "decoder error %d" % e + else: + self.data = self.data[n:] + + return s + + def chunk_DEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_JEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_UEND(self, offset, bytes): + return self.chunk_IEND(offset, bytes) + + def chunk_IEND(self, offset, bytes): + "IEND -- end of image" + + # we now have a new image. carry out the operation + # defined by the image header. + + # won't need these anymore + del self.decoder + del self.data + + self.__store() + + return self.fp.read(bytes) + + def __store(self): + + # apply operation + cid = self.action[0] + + if cid in ["BLNK", "IHDR", "JHDR", "UHDR"]: + # store + self.images[self.id] = self.im + + elif cid == "DHDR": + # paste + cid, mode, bbox = self.action + im0 = self.images[self.id] + im1 = self.im + if mode == 0: + im1 = im1.chop_add_modulo(im0.crop(bbox)) + im0.paste(im1, bbox) + + self.count = self.count - 1 + + if self.count == 0 and self.show: + self.im = self.images[self.id] + raise EOFError # end of this frame + + def chunk_PLTE(self, offset, bytes): + "PLTE -- palette data" + + s = self.fp.read(bytes) + if self.mode == "P": + self.palette = ImagePalette.raw("RGB", s) + return s + + def chunk_sYNC(self, offset, bytes): + "SYNC -- reset decoder" + + if self.count != 0: + raise SyntaxError, "misplaced sYNC chunk" + + s = self.fp.read(bytes) + self.__reset() + return s + + +# -------------------------------------------------------------------- +# ARG reader + +def _accept(prefix): + return prefix[:8] == MAGIC + +## +# Image plugin for the experimental Animated Raster Graphics format. + +class ArgImageFile(ImageFile.ImageFile): + + format = "ARG" + format_description = "Animated raster graphics" + + def _open(self): + + if Image.warnings: + Image.warnings.warn( + "The ArgImagePlugin driver is obsolete, and will be removed " + "from a future release of PIL. If you rely on this module, " + "please contact the PIL authors.", + RuntimeWarning + ) + + if self.fp.read(8) != MAGIC: + raise SyntaxError, "not an ARG file" + + self.arg = ArgStream(self.fp) + + # read and process the first chunk (AHDR) + + cid, offset, bytes = self.arg.read() + + if cid != "AHDR": + raise SyntaxError, "expected an AHDR chunk" + + s = self.arg.call(cid, offset, bytes) + + self.arg.crc(cid, s) + + # image characteristics + self.mode = self.arg.mode + self.size = self.arg.size + + def load(self): + + if self.arg.im is None: + self.seek(0) + + # image data + self.im = self.arg.im + self.palette = self.arg.palette + + # set things up for further processing + Image.Image.load(self) + + def seek(self, frame): + + if self.arg.eof: + raise EOFError, "end of animation" + + self.fp = self.arg.fp + + while 1: + + # + # process chunks + + cid, offset, bytes = self.arg.read() + + if self.arg.eof: + raise EOFError, "end of animation" + + try: + s = self.arg.call(cid, offset, bytes) + except EOFError: + break + + except "glurk": # AttributeError + if Image.DEBUG: + print cid, bytes, "(unknown)" + s = self.fp.read(bytes) + + self.arg.crc(cid, s) + + self.fp.read(4) # ship extra CRC + + def tell(self): + return 0 + + def verify(self): + "Verify ARG file" + + # back up to first chunk + self.fp.seek(8) + + self.arg.verify(self) + self.arg.close() + + self.fp = None + +# +# -------------------------------------------------------------------- + +Image.register_open("ARG", ArgImageFile, _accept) + +Image.register_extension("ARG", ".arg") + +Image.register_mime("ARG", "video/x-arg") diff --git a/PIL/BdfFontFile.py b/PIL/BdfFontFile.py new file mode 100644 index 000000000..47b5f05c8 --- /dev/null +++ b/PIL/BdfFontFile.py @@ -0,0 +1,133 @@ +# +# The Python Imaging Library +# $Id$ +# +# bitmap distribution font (bdf) file parser +# +# history: +# 1996-05-16 fl created (as bdf2pil) +# 1997-08-25 fl converted to FontFile driver +# 2001-05-25 fl removed bogus __init__ call +# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) +# 2003-04-22 fl more robustification (from Graham Dumpleton) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import Image +import FontFile + +import string + +# -------------------------------------------------------------------- +# parse X Bitmap Distribution Format (BDF) +# -------------------------------------------------------------------- + +bdf_slant = { + "R": "Roman", + "I": "Italic", + "O": "Oblique", + "RI": "Reverse Italic", + "RO": "Reverse Oblique", + "OT": "Other" +} + +bdf_spacing = { + "P": "Proportional", + "M": "Monospaced", + "C": "Cell" +} + +def bdf_char(f): + + # skip to STARTCHAR + while 1: + s = f.readline() + if not s: + return None + if s[:9] == "STARTCHAR": + break + id = string.strip(s[9:]) + + # load symbol properties + props = {} + while 1: + s = f.readline() + if not s or s[:6] == "BITMAP": + break + i = string.find(s, " ") + props[s[:i]] = s[i+1:-1] + + # load bitmap + bitmap = [] + while 1: + s = f.readline() + if not s or s[:7] == "ENDCHAR": + break + bitmap.append(s[:-1]) + bitmap = string.join(bitmap, "") + + [x, y, l, d] = map(int, string.split(props["BBX"])) + [dx, dy] = map(int, string.split(props["DWIDTH"])) + + bbox = (dx, dy), (l, -d-y, x+l, -d), (0, 0, x, y) + + try: + im = Image.fromstring("1", (x, y), bitmap, "hex", "1") + except ValueError: + # deal with zero-width characters + im = Image.new("1", (x, y)) + + return id, int(props["ENCODING"]), bbox, im + +## +# Font file plugin for the X11 BDF format. + +class BdfFontFile(FontFile.FontFile): + + def __init__(self, fp): + + FontFile.FontFile.__init__(self) + + s = fp.readline() + if s[:13] != "STARTFONT 2.1": + raise SyntaxError, "not a valid BDF file" + + props = {} + comments = [] + + while 1: + s = fp.readline() + if not s or s[:13] == "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]) + + font = string.split(props["FONT"], "-") + + font[4] = bdf_slant[string.upper(font[4])] + font[11] = bdf_spacing[string.upper(font[11])] + + ascent = int(props["FONT_ASCENT"]) + descent = int(props["FONT_DESCENT"]) + + fontname = string.join(font[1:], ";") + + # print "#", fontname + # for i in comments: + # print "#", i + + font = [] + while 1: + c = bdf_char(fp) + if not c: + break + id, ch, (xy, dst, src), im = c + if ch >= 0 and ch < len(self.glyph): + self.glyph[ch] = xy, dst, src, im diff --git a/PIL/BmpImagePlugin.py b/PIL/BmpImagePlugin.py new file mode 100644 index 000000000..09c0a2141 --- /dev/null +++ b/PIL/BmpImagePlugin.py @@ -0,0 +1,251 @@ +# +# The Python Imaging Library. +# $Id$ +# +# BMP file handler +# +# Windows (and OS/2) native bitmap storage format. +# +# history: +# 1995-09-01 fl Created +# 1996-04-30 fl Added save +# 1997-08-27 fl Fixed save of 1-bit images +# 1998-03-06 fl Load P images as L where possible +# 1998-07-03 fl Load P images as 1 where possible +# 1998-12-29 fl Handle small palettes +# 2002-12-30 fl Fixed load of 1-bit palette images +# 2003-04-21 fl Fixed load of 1-bit monochrome images +# 2003-04-23 fl Added limited support for BI_BITFIELDS compression +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + + +import string +import Image, ImageFile, ImagePalette + + +# +# -------------------------------------------------------------------- +# 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"), + 4: ("P", "P;4"), + 8: ("P", "P"), + 16: ("RGB", "BGR;15"), + 24: ("RGB", "BGR"), + 32: ("RGB", "BGRX") +} + +def _accept(prefix): + return prefix[:2] == "BM" + +## +# Image plugin for the Windows BMP format. + +class BmpImageFile(ImageFile.ImageFile): + + format = "BMP" + format_description = "Windows Bitmap" + + def _bitmap(self, header = 0, offset = 0): + + if header: + self.fp.seek(header) + + read = self.fp.read + + # CORE/INFO + s = read(4) + s = s + ImageFile._safe_read(self.fp, i32(s)-4) + + if len(s) == 12: + + # OS/2 1.0 CORE + bits = i16(s[10:]) + self.size = i16(s[4:]), i16(s[6:]) + compression = 0 + lutsize = 3 + colors = 0 + direction = -1 + + elif len(s) in [40, 64]: + + # WIN 3.1 or OS/2 2.0 INFO + bits = i16(s[14:]) + self.size = i32(s[4:]), i32(s[8:]) + compression = i32(s[16:]) + lutsize = 4 + colors = i32(s[32:]) + direction = -1 + if s[11] == '\xff': + # upside-down storage + self.size = self.size[0], 2**32 - self.size[1] + direction = 0 + + else: + raise IOError("Unsupported BMP header type (%d)" % len(s)) + + if not colors: + colors = 1 << bits + + # MODE + try: + self.mode, rawmode = BIT2MODE[bits] + except KeyError: + raise IOError("Unsupported BMP pixel depth (%d)" % bits) + + if compression == 3: + # BI_BITFIELDS compression + mask = i32(read(4)), i32(read(4)), i32(read(4)) + if bits == 32 and mask == (0xff0000, 0x00ff00, 0x0000ff): + rawmode = "BGRX" + elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): + rawmode = "BGR;16" + elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): + rawmode = "BGR;15" + else: + # print bits, map(hex, mask) + raise IOError("Unsupported BMP bitfields layout") + elif compression != 0: + raise IOError("Unsupported BMP compression (%d)" % compression) + + # LUT + if self.mode == "P": + palette = [] + greyscale = 1 + if colors == 2: + indices = (0, 255) + else: + indices = range(colors) + for i in indices: + rgb = read(lutsize)[:3] + if rgb != chr(i)*3: + greyscale = 0 + palette.append(rgb) + if greyscale: + if colors == 2: + self.mode = rawmode = "1" + else: + self.mode = rawmode = "L" + else: + self.mode = "P" + self.palette = ImagePalette.raw( + "BGR", string.join(palette, "") + ) + + if not offset: + offset = self.fp.tell() + + self.tile = [("raw", + (0, 0) + self.size, + offset, + (rawmode, ((self.size[0]*bits+31)>>3)&(~3), direction))] + + self.info["compression"] = compression + + def _open(self): + + # HEAD + s = self.fp.read(14) + if s[:2] != "BM": + raise SyntaxError("Not a BMP file") + offset = i32(s[10:]) + + self._bitmap(offset=offset) + + +class DibImageFile(BmpImageFile): + + format = "DIB" + format_description = "Windows Bitmap" + + def _open(self): + self._bitmap() + +# +# -------------------------------------------------------------------- +# 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), + "P": ("P", 8, 256), + "RGB": ("BGR", 24, 0), +} + +def _save(im, fp, filename, check=0): + + try: + rawmode, bits, colors = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as BMP" % im.mode) + + if check: + return check + + 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) + o32(offset+image) + # file size + o32(0) + # reserved + o32(offset)) # image data offset + + # bitmap info header + fp.write(o32(header) + # info header size + o32(im.size[0]) + # width + o32(im.size[1]) + # height + o16(1) + # planes + o16(bits) + # depth + o32(0) + # compression (0=uncompressed) + o32(image) + # size of bitmap + o32(1) + o32(1) + # resolution + o32(colors) + # colors used + o32(colors)) # colors important + + fp.write("\000" * (header - 40)) # padding (for OS/2 format) + + if im.mode == "1": + for i in (0, 255): + fp.write(chr(i) * 4) + elif im.mode == "L": + for i in range(256): + fp.write(chr(i) * 4) + elif im.mode == "P": + fp.write(im.im.getpalette("RGB", "BGRX")) + + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, stride, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BmpImageFile.format, BmpImageFile, _accept) +Image.register_save(BmpImageFile.format, _save) + +Image.register_extension(BmpImageFile.format, ".bmp") diff --git a/PIL/BufrStubImagePlugin.py b/PIL/BufrStubImagePlugin.py new file mode 100644 index 000000000..4b111b393 --- /dev/null +++ b/PIL/BufrStubImagePlugin.py @@ -0,0 +1,68 @@ +# +# The Python Imaging Library +# $Id$ +# +# BUFR stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile + +_handler = None + +## +# Install application-specific BUFR image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:4] == "BUFR" or prefix[:4] == "ZCZC" + +class BufrStubImageFile(ImageFile.StubImageFile): + + format = "BUFR" + format_description = "BUFR" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a BUFR file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("BUFR save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept) +Image.register_save(BufrStubImageFile.format, _save) + +Image.register_extension(BufrStubImageFile.format, ".bufr") diff --git a/PIL/ContainerIO.py b/PIL/ContainerIO.py new file mode 100644 index 000000000..dff50e6bf --- /dev/null +++ b/PIL/ContainerIO.py @@ -0,0 +1,116 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a class to read from a container file +# +# History: +# 1995-06-18 fl Created +# 1995-09-07 fl Added readline(), readlines() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +## +# A file object that provides read access to a part of an existing +# file (for example a TAR file). + +class ContainerIO: + + ## + # Create file object. + # + # @param file Existing file. + # @param offset Start of region, in bytes. + # @param length Size of region, in bytes. + + def __init__(self, file, offset, length): + self.fh = file + self.pos = 0 + self.offset = offset + self.length = length + self.fh.seek(offset) + + ## + # Always false. + + def isatty(self): + return 0 + + ## + # Move file pointer. + # + # @param offset Offset in bytes. + # @param mode Starting position. Use 0 for beginning of region, 1 + # for current offset, and 2 for end of region. You cannot move + # the pointer outside the defined region. + + def seek(self, offset, mode = 0): + if mode == 1: + self.pos = self.pos + offset + elif mode == 2: + self.pos = self.length + offset + else: + self.pos = offset + # clamp + self.pos = max(0, min(self.pos, self.length)) + self.fh.seek(self.offset + self.pos) + + ## + # Get current file pointer. + # + # @return Offset from start of region, in bytes. + + def tell(self): + return self.pos + + ## + # Read data. + # + # @def read(bytes=0) + # @param bytes Number of bytes to read. If omitted or zero, + # read until end of region. + # @return An 8-bit string. + + def read(self, n = 0): + if n: + n = min(n, self.length - self.pos) + else: + n = self.length - self.pos + if not n: # EOF + return "" + self.pos = self.pos + n + return self.fh.read(n) + + ## + # Read a line of text. + # + # @return An 8-bit string. + + def readline(self): + s = "" + while 1: + c = self.read(1) + if not c: + break + s = s + c + if c == "\n": + break + return s + + ## + # Read multiple lines of text. + # + # @return A list of 8-bit strings. + + def readlines(self): + l = [] + while 1: + s = self.readline() + if not s: + break + l.append(s) + return l diff --git a/PIL/CurImagePlugin.py b/PIL/CurImagePlugin.py new file mode 100644 index 000000000..6a126e701 --- /dev/null +++ b/PIL/CurImagePlugin.py @@ -0,0 +1,88 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Cursor support for PIL +# +# notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# history: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +import Image, BmpImagePlugin + + +# +# -------------------------------------------------------------------- + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def i32(c): + return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) + + +def _accept(prefix): + return prefix[:4] == "\0\0\2\0" + +## +# Image plugin for Windows Cursor files. + +class CurImageFile(BmpImagePlugin.BmpImageFile): + + format = "CUR" + format_description = "Windows Cursor" + + def _open(self): + + offset = self.fp.tell() + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError, "not an CUR file" + + # pick the largest cursor 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 "hotspot x", i16(s[4:]) + #print "hotspot y", i16(s[6:]) + #print "bytes", i32(s[8:]) + #print "offset", i32(s[12:]) + + # load as bitmap + self._bitmap(i32(m[12:]) + offset) + + # patch up the bitmap height + self.size = self.size[0], self.size[1]/2 + d, e, o, a = self.tile[0] + self.tile[0] = d, (0,0)+self.size, o, a + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open("CUR", CurImageFile, _accept) + +Image.register_extension("CUR", ".cur") diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py new file mode 100644 index 000000000..32da7d831 --- /dev/null +++ b/PIL/DcxImagePlugin.py @@ -0,0 +1,78 @@ +# +# The Python Imaging Library. +# $Id$ +# +# DCX file handling +# +# DCX is a container file format defined by Intel, commonly used +# for fax applications. Each DCX file consists of a directory +# (a list of file offsets) followed by a set of (usually 1-bit) +# PCX files. +# +# History: +# 1995-09-09 fl Created +# 1996-03-20 fl Properly derived from PcxImageFile. +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2002-07-30 fl Fixed file handling +# +# Copyright (c) 1997-98 by Secret Labs AB. +# Copyright (c) 1995-96 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +import Image + +from 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) + +def _accept(prefix): + return i32(prefix) == MAGIC + +## +# Image plugin for the Intel DCX format. + +class DcxImageFile(PcxImageFile): + + format = "DCX" + format_description = "Intel DCX" + + def _open(self): + + # Header + s = self.fp.read(4) + if i32(s) != MAGIC: + raise SyntaxError, "not a DCX file" + + # Component directory + self._offset = [] + for i in range(1024): + offset = i32(self.fp.read(4)) + if not offset: + break + self._offset.append(offset) + + self.__fp = self.fp + self.seek(0) + + def seek(self, frame): + if frame >= len(self._offset): + raise EOFError("attempt to seek outside DCX directory") + self.frame = frame + self.fp = self.__fp + self.fp.seek(self._offset[frame]) + PcxImageFile._open(self) + + def tell(self): + return self.frame + + +Image.register_open("DCX", DcxImageFile, _accept) + +Image.register_extension("DCX", ".dcx") diff --git a/PIL/EpsImagePlugin.py b/PIL/EpsImagePlugin.py new file mode 100644 index 000000000..1927b3227 --- /dev/null +++ b/PIL/EpsImagePlugin.py @@ -0,0 +1,349 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EPS file handling +# +# History: +# 1995-09-01 fl Created (0.1) +# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) +# 1996-08-22 fl Don't choke on floating point BoundingBox values +# 1996-08-23 fl Handle files from Macintosh (0.3) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.5" + +import re, string +import Image, ImageFile + +# +# -------------------------------------------------------------------- + +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) + +split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$") +field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$") + +def Ghostscript(tile, size, fp): + """Render an image using Ghostscript (Unix only)""" + + # Unpack decoder tile + decoder, tile, offset, data = tile[0] + length, bbox = data + + import tempfile, os + + file = tempfile.mktemp() + + # Build ghostscript command + command = ["gs", + "-q", # quite mode + "-g%dx%d" % size, # set output geometry (pixels) + "-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) + + # push data through ghostscript + try: + gs = os.popen(command, "w") + # adjust for image origin + if bbox[0] != 0 or bbox[1] != 0: + gs.write("%d %d translate\n" % (-bbox[0], -bbox[1])) + fp.seek(offset) + while length > 0: + s = fp.read(8192) + if not s: + break + length = length - len(s) + gs.write(s) + status = gs.close() + if status: + raise IOError("gs failed (status %d)" % status) + im = Image.core.open_ppm(file) + finally: + try: os.unlink(file) + except: pass + + return im + + +class PSFile: + """Wrapper that treats either CR or LF as end of line.""" + def __init__(self, fp): + self.fp = fp + self.char = None + def __getattr__(self, id): + v = getattr(self.fp, id) + setattr(self, id, v) + return v + def seek(self, offset, whence=0): + self.char = None + self.fp.seek(offset, whence) + def tell(self): + pos = self.fp.tell() + if self.char: + pos = pos - 1 + return pos + def readline(self): + s = "" + if self.char: + c = self.char + self.char = None + else: + c = self.fp.read(1) + while c not in "\r\n": + s = s + c + c = self.fp.read(1) + if c == "\r": + self.char = self.fp.read(1) + if self.char == "\n": + self.char = None + return s + "\n" + + +def _accept(prefix): + return prefix[:4] == "%!PS" or i32(prefix) == 0xC6D3D0C5L + +## +# Image plugin for Encapsulated Postscript. This plugin supports only +# a few variants of this format. + +class EpsImageFile(ImageFile.ImageFile): + """EPS File Parser for the Python Imaging Library""" + + format = "EPS" + format_description = "Encapsulated Postscript" + + def _open(self): + + # FIXME: should check the first 512 bytes to see if this + # really is necessary (platform-dependent, though...) + + fp = PSFile(self.fp) + + # HEAD + s = fp.read(512) + if s[:4] == "%!PS": + offset = 0 + fp.seek(0, 2) + length = fp.tell() + elif i32(s) == 0xC6D3D0C5L: + offset = i32(s[4:]) + length = i32(s[8:]) + fp.seek(offset) + else: + raise SyntaxError, "not an EPS file" + + fp.seek(offset) + + box = None + + self.mode = "RGB" + self.size = 1, 1 # FIXME: huh? + + # + # Load EPS header + + s = fp.readline() + + while s: + + if len(s) > 255: + raise SyntaxError, "not an EPS file" + + if s[-2:] == '\r\n': + s = s[:-2] + elif s[-1:] == '\n': + s = s[:-1] + + try: + m = split.match(s) + except re.error, v: + raise SyntaxError, "not an EPS file" + + if m: + k, v = m.group(1, 2) + self.info[k] = v + if k == "BoundingBox": + try: + # 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))) + self.size = box[2] - box[0], box[3] - box[1] + self.tile = [("eps", (0,0) + self.size, offset, + (length, box))] + except: + pass + + else: + + m = field.match(s) + + 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] = "" + else: + raise IOError, "bad EPS header" + + s = fp.readline() + + if s[:1] != "%": + break + + + # + # Scan for an "ImageData" descriptor + + while s[0] == "%": + + if len(s) > 255: + raise SyntaxError, "not an EPS file" + + if s[-2:] == '\r\n': + s = s[:-2] + elif s[-1:] == '\n': + s = s[:-1] + + if s[:11] == "%ImageData:": + + [x, y, bi, mo, z3, z4, en, id] =\ + string.split(s[11:], maxsplit=7) + + x = int(x); y = int(y) + + bi = int(bi) + mo = int(mo) + + en = int(en) + + if en == 1: + decoder = "eps_binary" + elif en == 2: + decoder = "eps_hex" + else: + break + if bi != 8: + break + if mo == 1: + self.mode = "L" + elif mo == 2: + self.mode = "LAB" + elif mo == 3: + self.mode = "RGB" + else: + break + + if id[:1] == id[-1:] == '"': + id = id[1:-1] + + # Scan forward to the actual image data + while 1: + s = fp.readline() + if not s: + break + if s[:len(id)] == id: + self.size = x, y + self.tile2 = [(decoder, + (0, 0, x, y), + fp.tell(), + 0)] + return + + s = fp.readline() + if not s: + break + + if not box: + raise IOError, "cannot determine EPS bounding box" + + def load(self): + # Load EPS via Ghostscript + if not self.tile: + return + self.im = Ghostscript(self.tile, self.size, self.fp) + self.mode = self.im.mode + self.size = self.im.size + self.tile = [] + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename, eps=1): + """EPS Writer for the Python Imaging Library.""" + + # + # make sure image data is available + im.load() + + # + # determine postscript image mode + if im.mode == "L": + operator = (8, 1, "image") + elif im.mode == "RGB": + operator = (8, 3, "false 3 colorimage") + elif im.mode == "CMYK": + operator = (8, 4, "false 4 colorimage") + else: + raise ValueError, "image mode is not supported" + + if eps: + # + # write EPS header + fp.write("%!PS-Adobe-3.0 EPSF-3.0\n") + fp.write("%%Creator: PIL 0.1 EpsEncode\n") + #fp.write("%%CreationDate: %s"...) + fp.write("%%%%BoundingBox: 0 0 %d %d\n" % im.size) + fp.write("%%Pages: 1\n") + fp.write("%%EndComments\n") + fp.write("%%Page: 1 1\n") + fp.write("%%ImageData: %d %d " % im.size) + fp.write("%d %d 0 1 1 \"%s\"\n" % operator) + + # + # image header + fp.write("gsave\n") + fp.write("10 dict begin\n") + fp.write("/buf %d string def\n" % (im.size[0] * operator[1])) + fp.write("%d %d scale\n" % im.size) + 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]) + + ImageFile._save(im, fp, [("eps", (0,0)+im.size, 0, None)]) + + fp.write("\n%%%%EndBinary\n") + fp.write("grestore end\n") + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_open(EpsImageFile.format, EpsImageFile, _accept) + +Image.register_save(EpsImageFile.format, _save) + +Image.register_extension(EpsImageFile.format, ".ps") +Image.register_extension(EpsImageFile.format, ".eps") + +Image.register_mime(EpsImageFile.format, "application/postscript") diff --git a/PIL/ExifTags.py b/PIL/ExifTags.py new file mode 100644 index 000000000..b033aad35 --- /dev/null +++ b/PIL/ExifTags.py @@ -0,0 +1,157 @@ +# +# The Python Imaging Library. +# $Id$ +# +# EXIF tags +# +# Copyright (c) 2003 by Secret Labs AB +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known EXIF tags. +## + +## +# Maps EXIF tags to tag names. + +TAGS = { + + # possibly incomplete + 0x0100: "ImageWidth", + 0x0101: "ImageLength", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x010e: "ImageDescription", + 0x010f: "Make", + 0x0110: "Model", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteConunts", + 0x011a: "XResolution", + 0x011a: "XResolution", + 0x011b: "YResolution", + 0x011b: "YResolution", + 0x011c: "PlanarConfiguration", + 0x0128: "ResolutionUnit", + 0x0128: "ResolutionUnit", + 0x012d: "TransferFunction", + 0x0131: "Software", + 0x0132: "DateTime", + 0x013b: "Artist", + 0x013e: "WhitePoint", + 0x013f: "PrimaryChromaticities", + 0x0201: "JpegIFOffset", + 0x0202: "JpegIFByteCount", + 0x0211: "YCbCrCoefficients", + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite", + 0x0214: "ReferenceBlackWhite", + 0x1000: "RelatedImageFileFormat", + 0x1001: "RelatedImageLength", + 0x1001: "RelatedImageWidth", + 0x828d: "CFARepeatPatternDim", + 0x828e: "CFAPattern", + 0x828f: "BatteryLevel", + 0x8298: "Copyright", + 0x829a: "ExposureTime", + 0x829d: "FNumber", + 0x8769: "ExifOffset", + 0x8773: "InterColorProfile", + 0x8822: "ExposureProgram", + 0x8824: "SpectralSensitivity", + 0x8825: "GPSInfo", + 0x8827: "ISOSpeedRatings", + 0x8828: "OECF", + 0x8829: "Interlace", + 0x882a: "TimeZoneOffset", + 0x882b: "SelfTimerMode", + 0x9000: "ExifVersion", + 0x9003: "DateTimeOriginal", + 0x9004: "DateTimeDigitized", + 0x9101: "ComponentsConfiguration", + 0x9102: "CompressedBitsPerPixel", + 0x9201: "ShutterSpeedValue", + 0x9202: "ApertureValue", + 0x9203: "BrightnessValue", + 0x9204: "ExposureBiasValue", + 0x9205: "MaxApertureValue", + 0x9206: "SubjectDistance", + 0x9207: "MeteringMode", + 0x9208: "LightSource", + 0x9209: "Flash", + 0x920a: "FocalLength", + 0x920b: "FlashEnergy", + 0x920c: "SpatialFrequencyResponse", + 0x920d: "Noise", + 0x9211: "ImageNumber", + 0x9212: "SecurityClassification", + 0x9213: "ImageHistory", + 0x9214: "SubjectLocation", + 0x9215: "ExposureIndex", + 0x9216: "TIFF/EPStandardID", + 0x927c: "MakerNote", + 0x9286: "UserComment", + 0x9290: "SubsecTime", + 0x9291: "SubsecTimeOriginal", + 0x9292: "SubsecTimeDigitized", + 0xa000: "FlashPixVersion", + 0xa001: "ColorSpace", + 0xa002: "ExifImageWidth", + 0xa003: "ExifImageHeight", + 0xa004: "RelatedSoundFile", + 0xa005: "ExifInteroperabilityOffset", + 0xa20b: "FlashEnergy", + 0xa20c: "SpatialFrequencyResponse", + 0xa20e: "FocalPlaneXResolution", + 0xa20f: "FocalPlaneYResolution", + 0xa210: "FocalPlaneResolutionUnit", + 0xa214: "SubjectLocation", + 0xa215: "ExposureIndex", + 0xa217: "SensingMethod", + 0xa300: "FileSource", + 0xa301: "SceneType", + 0xa302: "CFAPattern", + +} + +## +# Maps EXIF GSP tags to tag names. + +GPSTAGS = { + 0: "GPSVersionID", + 1: "GPSLatitudeRef", + 2: "GPSLatitude", + 3: "GPSLongitudeRef", + 4: "GPSLongitude", + 5: "GPSAltitudeRef", + 6: "GPSAltitude", + 7: "GPSTimeStamp", + 8: "GPSSatellites", + 9: "GPSStatus", + 10: "GPSMeasureMode", + 11: "GPSDOP", + 12: "GPSSpeedRef", + 13: "GPSSpeed", + 14: "GPSTrackRef", + 15: "GPSTrack", + 16: "GPSImgDirectionRef", + 17: "GPSImgDirection", + 18: "GPSMapDatum", + 19: "GPSDestLatitudeRef", + 20: "GPSDestLatitude", + 21: "GPSDestLongitudeRef", + 22: "GPSDestLongitude", + 23: "GPSDestBearingRef", + 24: "GPSDestBearing", + 25: "GPSDestDistanceRef", + 26: "GPSDestDistance" +} diff --git a/PIL/FitsStubImagePlugin.py b/PIL/FitsStubImagePlugin.py new file mode 100644 index 000000000..ac8be80a9 --- /dev/null +++ b/PIL/FitsStubImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library +# $Id$ +# +# FITS stub adapter +# +# Copyright (c) 1998-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile + +_handler = None + +## +# Install application-specific FITS image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:6] == "SIMPLE" + +class FITSStubImageFile(ImageFile.StubImageFile): + + format = "FITS" + format_description = "FITS" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(6)): + raise SyntaxError("Not a FITS file") + + # FIXME: add more sanity checks here; mandatory header items + # include SIMPLE, BITPIX, NAXIS, etc. + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("FITS save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept) +Image.register_save(FITSStubImageFile.format, _save) + +Image.register_extension(FITSStubImageFile.format, ".fit") +Image.register_extension(FITSStubImageFile.format, ".fits") diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py new file mode 100644 index 000000000..706c36a7f --- /dev/null +++ b/PIL/FliImagePlugin.py @@ -0,0 +1,142 @@ +# +# The Python Imaging Library. +# $Id$ +# +# FLI/FLC file handling. +# +# History: +# 95-09-01 fl Created +# 97-01-03 fl Fixed parser, setup decoder tile +# 98-07-15 fl Renamed offset attribute to avoid name clash +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import Image, ImageFile, ImagePalette +import string + + +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) + +# +# decoder + +def _accept(prefix): + return i16(prefix[4:6]) in [0xAF11, 0xAF12] + +## +# Image plugin for the FLI/FLC animation format. Use the seek +# method to load individual frames. + +class FliImageFile(ImageFile.ImageFile): + + format = "FLI" + format_description = "Autodesk FLI/FLC Animation" + + def _open(self): + + # HEAD + s = self.fp.read(128) + magic = i16(s[4:6]) + if magic not in [0xAF11, 0xAF12]: + raise SyntaxError, "not an FLI/FLC file" + + # image characteristics + self.mode = "P" + self.size = i16(s[8:10]), i16(s[10:12]) + + # animation speed + duration = i32(s[16:20]) + if magic == 0xAF11: + duration = (duration * 1000) / 70 + self.info["duration"] = duration + + # look for palette + palette = map(lambda a: (a,a,a), range(256)) + + s = self.fp.read(16) + + self.__offset = 128 + + if i16(s[4:6]) == 0xF100: + # prefix chunk; ignore it + self.__offset = self.__offset + i32(s) + s = self.fp.read(16) + + if i16(s[4:6]) == 0xF1FA: + # look for palette chunk + s = self.fp.read(6) + if i16(s[4:6]) == 11: + self._palette(palette, 2) + 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, "")) + + # set things up to decode first frame + self.frame = -1 + self.__fp = self.fp + + self.seek(0) + + def _palette(self, palette, shift): + # load palette + + 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]) + 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 + 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 + self.frame = frame + + # move to next frame + self.fp = self.__fp + self.fp.seek(self.__offset) + + s = self.fp.read(4) + if not s: + raise EOFError + + framesize = i32(s) + + self.decodermaxblock = framesize + self.tile = [("fli", (0,0)+self.size, self.__offset, None)] + + self.__offset = self.__offset + framesize + + def tell(self): + + return self.frame + +# +# registry + +Image.register_open("FLI", FliImageFile, _accept) + +Image.register_extension("FLI", ".fli") +Image.register_extension("FLI", ".flc") diff --git a/PIL/FontFile.py b/PIL/FontFile.py new file mode 100644 index 000000000..d9d8eb38a --- /dev/null +++ b/PIL/FontFile.py @@ -0,0 +1,146 @@ +# +# The Python Imaging Library +# $Id$ +# +# base class for raster font file parsers +# +# history: +# 1997-06-05 fl created +# 1997-08-19 fl restrict image width +# +# Copyright (c) 1997-1998 by Secret Labs AB +# Copyright (c) 1997-1998 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import os +import Image + +import marshal + +try: + import zlib +except ImportError: + zlib = None + +WIDTH = 800 + +def puti16(fp, values): + # write network order (big-endian) 16-bit sequence + for v in values: + if v < 0: + v = v + 65536 + fp.write(chr(v>>8&255) + chr(v&255)) + +## +# Base class for raster font file handlers. + +class FontFile: + + bitmap = None + + def __init__(self): + + self.info = {} + self.glyph = [None] * 256 + + def __getitem__(self, ix): + return self.glyph[ix] + + def compile(self): + "Create metrics and bitmap" + + if self.bitmap: + return + + # create bitmap large enough to hold all data + h = w = maxwidth = 0 + lines = 1 + for glyph in self: + if glyph: + d, dst, src, im = glyph + h = max(h, src[3] - src[1]) + w = w + (src[2] - src[0]) + if w > WIDTH: + lines = lines + 1 + w = (src[2] - src[0]) + maxwidth = max(maxwidth, w) + + xsize = maxwidth + ysize = lines * h + + if xsize == 0 and ysize == 0: + return "" + + self.ysize = h + + # paste glyphs into bitmap + self.bitmap = Image.new("1", (xsize, ysize)) + self.metrics = [None] * 256 + x = y = 0 + for i in range(256): + glyph = self[i] + if glyph: + d, dst, src, im = glyph + xx, yy = src[2] - src[0], src[3] - src[1] + x0, y0 = x, y + x = x + xx + if x > WIDTH: + x, y = 0, y + h + x0, y0 = x, y + x = xx + s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0 + self.bitmap.paste(im.crop(src), s) + # print chr(i), dst, s + self.metrics[i] = d, dst, s + + + def save1(self, filename): + "Save font in version 1 format" + + self.compile() + + # font data + self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG") + + # 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") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, [0] * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) + fp.close() + + + def save2(self, filename): + "Save font in version 2 format" + + # THIS IS WORK IN PROGRESS + + self.compile() + + data = marshal.dumps((self.metrics, self.info)) + + if zlib: + data = "z" + zlib.compress(data, 9) + else: + data = "u" + data + + fp = open(os.path.splitext(filename)[0] + ".pil", "wb") + + fp.write("PILfont2\n" + self.name + "\n" + "DATA\n") + + fp.write(data) + + self.bitmap.save(fp, "PNG") + + fp.close() + + + save = save1 # for now diff --git a/PIL/FpxImagePlugin.py b/PIL/FpxImagePlugin.py new file mode 100644 index 000000000..b78fe8c70 --- /dev/null +++ b/PIL/FpxImagePlugin.py @@ -0,0 +1,224 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library. +# $Id$ +# +# FlashPix support for PIL +# +# History: +# 97-01-25 fl Created (reads uncompressed RGB images only) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +import Image, ImageFile +from OleFileIO import * + + +# we map from colour field tuples to (mode, rawmode) descriptors +MODES = { + # opacity + (0x00007ffe): ("A", "L"), + # monochrome + (0x00010000,): ("L", "L"), + (0x00018000, 0x00017ffe): ("RGBA", "LA"), + # photo YCC + (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), + (0x00028000, 0x00028001, 0x00028002, 0x00027ffe): ("RGBA", "YCCA;P"), + # standard RGB (NIFRGB) + (0x00030000, 0x00030001, 0x00030002): ("RGB","RGB"), + (0x00038000, 0x00038001, 0x00038002, 0x00037ffe): ("RGBA","RGBA"), +} + +# +# -------------------------------------------------------------------- + +def _accept(prefix): + return prefix[:8] == MAGIC + +## +# Image plugin for the FlashPix images. + +class FpxImageFile(ImageFile.ImageFile): + + format = "FPX" + format_description = "FlashPix" + + def _open(self): + # + # read the OLE directory and see if this is a likely + # to be a FlashPix file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + 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" + + self._open_index(1) + + def _open_index(self, index = 1): + # + # get the Image Contents Property Set + + prop = self.ole.getproperties([ + "Data Object Store %06d" % index, + "\005Image Contents" + ]) + + # size (highest resolution) + + self.size = prop[0x1000002], prop[0x1000003] + + size = max(self.size) + i = 1 + while size > 64: + size = size / 2 + i = i + 1 + self.maxid = i - 1 + + # mode. instead of using a single field for this, flashpix + # requires you to specify the mode for each channel in each + # resolution subimage, and leaves it to the decoder to make + # sure that they all match. for now, we'll cheat and assume + # that this is always the case. + + id = self.maxid << 16 + + s = prop[0x2000002|id] + + colors = [] + for i in range(i32(s, 4)): + # note: for now, we ignore the "uncalibrated" flag + colors.append(i32(s, 8+i*4) & 0x7fffffff) + + self.mode, self.rawmode = MODES[tuple(colors)] + + # load JPEG tables, if any + self.jpeg = {} + for i in range(256): + id = 0x3000001|(i << 16) + if prop.has_key(id): + self.jpeg[i] = prop[id] + + # print len(self.jpeg), "tables loaded" + + self._open_subimage(1, self.maxid) + + def _open_subimage(self, index = 1, subimage = 0): + # + # setup tile descriptors for a given subimage + + stream = [ + "Data Object Store %06d" % index, + "Resolution %04d" % subimage, + "Subimage 0000 Header" + ] + + fp = self.ole.openstream(stream) + + # skip prefix + p = fp.read(28) + + # header stream + s = fp.read(36) + + size = i32(s, 4), i32(s, 8) + tilecount = i32(s, 12) + tilesize = i32(s, 16), i32(s, 20) + channels = i32(s, 24) + offset = i32(s, 28) + length = i32(s, 32) + + # print size, self.mode, self.rawmode + + if size != self.size: + raise IOError, "subimage mismatch" + + # get tile descriptors + fp.seek(28 + offset) + s = fp.read(i32(s, 12) * length) + + x = y = 0 + xsize, ysize = size + xtile, ytile = tilesize + self.tile = [] + + for i in range(0, len(s), length): + + compression = i32(s, i+8) + + if compression == 0: + self.tile.append(("raw", (x,y,x+xtile,y+ytile), + i32(s, i) + 28, (self.rawmode))) + + elif compression == 1: + + # FIXME: the fill decoder is not implemented + self.tile.append(("fill", (x,y,x+xtile,y+ytile), + i32(s, i) + 28, (self.rawmode, s[12:16]))) + + elif compression == 2: + + internal_color_conversion = ord(s[14]) + jpeg_tables = ord(s[15]) + rawmode = self.rawmode + + if internal_color_conversion: + # The image is stored as usual (usually YCbCr). + if rawmode == "RGBA": + # For "RGBA", data is stored as YCbCrA based on + # negative RGB. The following trick works around + # this problem : + jpegmode, rawmode = "YCbCrK", "CMYK" + else: + jpegmode = None # let the decoder decide + + else: + # The image is stored as defined by rawmode + jpegmode = rawmode + + self.tile.append(("jpeg", (x,y,x+xtile,y+ytile), + i32(s, i) + 28, (rawmode, jpegmode))) + + # FIXME: jpeg tables are tile dependent; the prefix + # data must be placed in the tile descriptor itself! + + if jpeg_tables: + self.tile_prefix = self.jpeg[jpeg_tables] + + else: + raise IOError, "unknown/invalid compression" + + x = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + self.stream = stream + self.fp = None + + def load(self): + + if not self.fp: + self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"]) + + ImageFile.ImageFile.load(self) + +# +# -------------------------------------------------------------------- + +Image.register_open("FPX", FpxImageFile, _accept) + +Image.register_extension("FPX", ".fpx") diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py new file mode 100644 index 000000000..c71a0f77f --- /dev/null +++ b/PIL/GbrImagePlugin.py @@ -0,0 +1,70 @@ +# +# The Python Imaging Library +# $Id$ +# +# load a GIMP brush file +# +# History: +# 96-03-14 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24L) + +def _accept(prefix): + return i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + +## +# Image plugin for the GIMP brush format. + +class GbrImageFile(ImageFile.ImageFile): + + format = "GBR" + format_description = "GIMP brush file" + + def _open(self): + + 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" + + 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" + + comment = self.fp.read(header_size - 20)[:-1] + + self.mode = "L" + self.size = width, height + + self.info["comment"] = comment + + # Since the brush is so small, we read the data immediately + self.data = self.fp.read(width * height) + + def load(self): + + if not self.data: + return + + # 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 = "" + +# +# registry + +Image.register_open("GBR", GbrImageFile, _accept) + +Image.register_extension("GBR", ".gbr") diff --git a/PIL/GdImageFile.py b/PIL/GdImageFile.py new file mode 100644 index 000000000..b5f1dd25d --- /dev/null +++ b/PIL/GdImageFile.py @@ -0,0 +1,85 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GD file handling +# +# History: +# 1996-04-12 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +# NOTE: This format cannot be automatically recognized, so the +# class is not registered for use with Image.open(). To open a +# gd file, use the GdImageFile.open() function instead. + +# THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This +# implementation is provided for convenience and demonstrational +# purposes only. + + +__version__ = "0.1" + +import ImageFile, ImagePalette + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +## +# 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. + +class GdImageFile(ImageFile.ImageFile): + + format = "GD" + format_description = "GD uncompressed images" + + def _open(self): + + # Header + s = self.fp.read(775) + + self.mode = "L" # FIXME: "P" + self.size = i16(s[0:2]), i16(s[2:4]) + + # transparency index + tindex = i16(s[5:7]) + if tindex < 256: + self.info["transparent"] = tindex + + self.palette = ImagePalette.raw("RGB", s[7:]) + + self.tile = [("raw", (0,0)+self.size, 775, ("L", 0, -1))] + +## +# Load texture from a GD image file. +# +# @param filename GD file name, or an opened file handle. +# @param mode Optional mode. In this version, if the mode argument +# is given, it must be "r". +# @return An image instance. +# @exception IOError If the image could not be read. + +def open(fp, mode = "r"): + + if mode != "r": + raise ValueError("bad mode") + + if type(fp) == type(""): + import __builtin__ + filename = fp + fp = __builtin__.open(fp, "rb") + else: + filename = "" + + try: + return GdImageFile(fp, filename) + except SyntaxError: + raise IOError("cannot identify this image file") diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py new file mode 100644 index 000000000..4d03493b5 --- /dev/null +++ b/PIL/GifImagePlugin.py @@ -0,0 +1,407 @@ +# +# The Python Imaging Library. +# $Id$ +# +# GIF file handling +# +# History: +# 1995-09-01 fl Created +# 1996-12-14 fl Added interlace support +# 1996-12-30 fl Added animation support +# 1997-01-05 fl Added write support, fixed local colour map bug +# 1997-02-23 fl Make sure to load raster data in getdata() +# 1997-07-05 fl Support external decoder (0.4) +# 1998-07-09 fl Handle all modes when saving (0.5) +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) +# 2001-04-17 fl Added palette optimization (0.7) +# 2002-06-06 fl Added transparency support for save (0.8) +# 2004-02-24 fl Disable interlacing for small images +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.9" + + +import Image, ImageFile, ImagePalette + + +# -------------------------------------------------------------------- +# Helpers + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def o16(i): + return chr(i&255) + chr(i>>8&255) + + +# -------------------------------------------------------------------- +# Identify/read GIF files + +def _accept(prefix): + return prefix[:6] in ["GIF87a", "GIF89a"] + +## +# Image plugin for GIF images. This plugin supports both GIF87 and +# GIF89 images. + +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)) + return None + + def _open(self): + + # Screen + s = self.fp.read(13) + if s[:6] not in ["GIF87a", "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]) + + bits = (flags & 7) + 1 + + if flags & 128: + # get global palette + self.info["background"] = ord(s[11]) + # check if palette contains colour indices + p = self.fp.read(3<= 3 and ord(block[0]) == 1: + self.info["loop"] = i16(block[1:3]) + while self.data(): + pass + + elif s == ",": + # + # local image + # + s = self.fp.read(9) + + # extent + x0, y0 = i16(s[0:]), i16(s[2:]) + x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:]) + flags = ord(s[8]) + + interlace = (flags & 64) != 0 + + if flags & 128: + bits = (flags & 7) + 1 + self.palette =\ + ImagePalette.raw("RGB", self.fp.read(3<%s" % (file, filename)) + else: + os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename)) + try: os.unlink(file) + except: pass + + +# -------------------------------------------------------------------- +# GIF utilities + +def getheader(im, info=None): + """Return a list of strings representing a GIF header""" + + optimize = info and info.get("optimize", 0) + + s = [ + "GIF87a" + # magic + o16(im.size[0]) + # size + o16(im.size[1]) + + chr(7 + 128) + # flags: bits + palette + chr(0) + # background + chr(0) # reserved/aspect + ] + + if optimize: + # minimize color palette + i = 0 + maxcolor = 0 + for count in im.histogram(): + if count: + maxcolor = i + i = i + 1 + else: + maxcolor = 256 + + # global palette + if im.mode == "P": + # colour palette + s.append(im.im.getpalette("RGB")[:maxcolor*3]) + else: + # greyscale + for i in range(maxcolor): + s.append(chr(i) * 3) + + return s + +def getdata(im, offset = (0, 0), **params): + """Return a list of strings representing this image. + The first string is a local image header, the rest contains + encoded image data.""" + + class collector: + data = [] + def write(self, data): + self.data.append(data) + + im.load() # make sure raster data is available + + fp = collector() + + try: + im.encoderinfo = params + + # local image header + fp.write("," + + o16(offset[0]) + # offset + o16(offset[1]) + + o16(im.size[0]) + # size + o16(im.size[1]) + + chr(0) + # flags + chr(8)) # bits + + ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])]) + + fp.write("\0") # end of image data + + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GifImageFile.format, GifImageFile, _accept) +Image.register_save(GifImageFile.format, _save) +Image.register_extension(GifImageFile.format, ".gif") +Image.register_mime(GifImageFile.format, "image/gif") + +# +# Uncomment the following line if you wish to use NETPBM/PBMPLUS +# instead of the built-in "uncompressed" GIF encoder + +# Image.register_save(GifImageFile.format, _save_netpbm) diff --git a/PIL/GimpGradientFile.py b/PIL/GimpGradientFile.py new file mode 100644 index 000000000..1a850dc53 --- /dev/null +++ b/PIL/GimpGradientFile.py @@ -0,0 +1,124 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read (and render) GIMP gradient files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from math import pi, log, sin, sqrt +import string + +# -------------------------------------------------------------------- +# Stuff to translate curve segments to palette values (derived from +# the corresponding code in GIMP, written by Federico Mena Quintero. +# See the GIMP distribution for more information.) +# + +EPSILON = 1e-10 + +def linear(middle, pos): + if pos <= middle: + if middle < EPSILON: + return 0.0 + else: + return 0.5 * pos / middle + else: + pos = pos - middle + middle = 1.0 - middle + if middle < EPSILON: + return 1.0 + else: + return 0.5 + 0.5 * pos / middle + +def curved(middle, pos): + return pos ** (log(0.5) / log(max(middle, EPSILON))) + +def sine(middle, pos): + return (sin((-pi / 2.0) + pi * linear(middle, pos)) + 1.0) / 2.0 + +def sphere_increasing(middle, pos): + return sqrt(1.0 - (linear(middle, pos) - 1.0) ** 2) + +def sphere_decreasing(middle, pos): + return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2) + +SEGMENTS = [ linear, curved, sine, sphere_increasing, sphere_decreasing ] + +class GradientFile: + + gradient = None + + def getpalette(self, entries = 256): + + palette = [] + + ix = 0 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + for i in range(entries): + + x = i / float(entries-1) + + while x1 < x: + ix = ix + 1 + x0, x1, xm, rgb0, rgb1, segment = self.gradient[ix] + + w = x1 - x0 + + if w < EPSILON: + scale = segment(0.5, 0.5) + else: + scale = segment((xm - x0) / w, (x - x0) / w) + + # expand to RGBA + r = chr(int(255 * ((rgb1[0] - rgb0[0]) * scale + rgb0[0]) + 0.5)) + g = chr(int(255 * ((rgb1[1] - rgb0[1]) * scale + rgb0[1]) + 0.5)) + b = chr(int(255 * ((rgb1[2] - rgb0[2]) * scale + rgb0[2]) + 0.5)) + a = chr(int(255 * ((rgb1[3] - rgb0[3]) * scale + rgb0[3]) + 0.5)) + + # add to palette + palette.append(r + g + b + a) + + return string.join(palette, ""), "RGBA" + +## +# File handler for GIMP's gradient format. + +class GimpGradientFile(GradientFile): + + def __init__(self, fp): + + if fp.readline()[:13] != "GIMP Gradient": + raise SyntaxError, "not a GIMP gradient file" + + count = int(fp.readline()) + + gradient = [] + + for i in range(count): + + s = string.split(fp.readline()) + w = map(float, s[:11]) + + x0, x1 = w[0], w[2] + xm = w[1] + rgb0 = w[3:7] + rgb1 = w[7:11] + + segment = SEGMENTS[int(s[11])] + cspace = int(s[12]) + + if cspace != 0: + raise IOError, "cannot handle HSV colour space" + + gradient.append((x0, x1, xm, rgb0, rgb1, segment)) + + self.gradient = gradient diff --git a/PIL/GimpPaletteFile.py b/PIL/GimpPaletteFile.py new file mode 100644 index 000000000..aba85b71f --- /dev/null +++ b/PIL/GimpPaletteFile.py @@ -0,0 +1,61 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read GIMP palette files +# +# History: +# 1997-08-23 fl Created +# 2004-09-07 fl Support GIMP 2.0 palette files. +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1997-2004. +# +# See the README file for information on usage and redistribution. +# + +import re, string + +## +# File handler for GIMP's palette format. + +class GimpPaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = map(lambda i: chr(i)*3, range(256)) + + if fp.readline()[:12] != "GIMP Palette": + raise SyntaxError, "not a GIMP palette file" + + i = 0 + + while i <= 255: + + s = fp.readline() + + if not s: + break + # skip fields and comment lines + if re.match("\w+:|#", s): + continue + if len(s) > 100: + raise SyntaxError, "bad palette file" + + v = tuple(map(int, string.split(s)[:3])) + if len(v) != 3: + raise ValueError, "bad palette entry" + + if 0 <= i <= 255: + self.palette[i] = chr(v[0]) + chr(v[1]) + chr(v[2]) + + i = i + 1 + + self.palette = string.join(self.palette, "") + + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/PIL/GribStubImagePlugin.py b/PIL/GribStubImagePlugin.py new file mode 100644 index 000000000..e232f90db --- /dev/null +++ b/PIL/GribStubImagePlugin.py @@ -0,0 +1,68 @@ +# +# The Python Imaging Library +# $Id$ +# +# GRIB stub adapter +# +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile + +_handler = None + +## +# Install application-specific GRIB image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[0:4] == "GRIB" and prefix[7] == chr(1) + +class GribStubImageFile(ImageFile.StubImageFile): + + format = "GRIB" + format_description = "GRIB" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not a GRIB file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("GRIB save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(GribStubImageFile.format, GribStubImageFile, _accept) +Image.register_save(GribStubImageFile.format, _save) + +Image.register_extension(GribStubImageFile.format, ".grib") diff --git a/PIL/Hdf5StubImagePlugin.py b/PIL/Hdf5StubImagePlugin.py new file mode 100644 index 000000000..bc5e37433 --- /dev/null +++ b/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,70 @@ +# +# The Python Imaging Library +# $Id$ +# +# HDF5 stub adapter +# +# Copyright (c) 2000-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile + +_handler = None + +## +# Install application-specific HDF5 image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +# -------------------------------------------------------------------- +# Image adapter + +def _accept(prefix): + return prefix[:8] == "\x89HDF\r\n\x1a\n" + +class HDF5StubImageFile(ImageFile.StubImageFile): + + format = "HDF5" + format_description = "HDF5" + + def _open(self): + + offset = self.fp.tell() + + if not _accept(self.fp.read(8)): + raise SyntaxError("Not an HDF file") + + self.fp.seek(offset) + + # make something up + self.mode = "F" + self.size = 1, 1 + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("HDF5 save handler not installed") + _handler.save(im, fp, filename) + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept) +Image.register_save(HDF5StubImageFile.format, _save) + +Image.register_extension(HDF5StubImageFile.format, ".h5") +Image.register_extension(HDF5StubImageFile.format, ".hdf") diff --git a/PIL/IcnsImagePlugin.py b/PIL/IcnsImagePlugin.py new file mode 100644 index 000000000..4d68fa232 --- /dev/null +++ b/PIL/IcnsImagePlugin.py @@ -0,0 +1,211 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Mac OS X icns file decoder, based on icns.py by Bob Ippolito. +# +# history: +# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. +# +# Copyright (c) 2004 by Bob Ippolito. +# Copyright (c) 2004 by Secret Labs. +# Copyright (c) 2004 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFile +import string, struct + +HEADERSIZE = 8 + +def nextheader(fobj): + return struct.unpack('>4sI', fobj.read(HEADERSIZE)) + +def read_32t(fobj, (start, length), (width, height)): + # The 128x128 icon seems to have an extra header for some reason. + fobj.seek(start) + sig = fobj.read(4) + if sig != '\x00\x00\x00\x00': + raise SyntaxError, 'Unknown signature, expecting 0x00000000' + return read_32(fobj, (start + 4, length - 4), (width, height)) + +def read_32(fobj, (start, length), size): + """ + Read a 32bit RGB icon resource. Seems to be either uncompressed or + an RLE packbits-like scheme. + """ + fobj.seek(start) + sizesq = size[0] * size[1] + if length == sizesq * 3: + # uncompressed ("RGBRGBGB") + indata = fobj.read(length) + im = Image.frombuffer("RGB", size, indata, "raw", "RGB", 0, 1) + else: + # decode image + im = Image.new("RGB", size, None) + for band_ix in range(3): + data = [] + bytesleft = sizesq + while bytesleft > 0: + byte = fobj.read(1) + if not byte: + break + byte = ord(byte) + if byte & 0x80: + blocksize = byte - 125 + byte = fobj.read(1) + for i in range(blocksize): + data.append(byte) + else: + blocksize = byte + 1 + data.append(fobj.read(blocksize)) + bytesleft = bytesleft - blocksize + if bytesleft <= 0: + break + if bytesleft != 0: + raise SyntaxError( + "Error reading channel [%r left]" % bytesleft + ) + band = Image.frombuffer( + "L", size, string.join(data, ""), "raw", "L", 0, 1 + ) + im.im.putband(band.im, band_ix) + return {"RGB": im} + +def read_mk(fobj, (start, length), size): + # Alpha masks seem to be uncompressed + fobj.seek(start) + band = Image.frombuffer( + "L", size, fobj.read(size[0]*size[1]), "raw", "L", 0, 1 + ) + return {"A": band} + +class IcnsFile: + + SIZES = { + (128, 128): [ + ('it32', read_32t), + ('t8mk', read_mk), + ], + (48, 48): [ + ('ih32', read_32), + ('h8mk', read_mk), + ], + (32, 32): [ + ('il32', read_32), + ('l8mk', read_mk), + ], + (16, 16): [ + ('is32', read_32), + ('s8mk', read_mk), + ], + } + + def __init__(self, fobj): + """ + fobj is a file-like object as an icns resource + """ + # signature : (start, length) + self.dct = dct = {} + self.fobj = fobj + sig, filesize = nextheader(fobj) + if sig != 'icns': + raise SyntaxError, 'not an icns file' + i = HEADERSIZE + while i < filesize: + sig, blocksize = nextheader(fobj) + i = i + HEADERSIZE + blocksize = blocksize - HEADERSIZE + dct[sig] = (i, blocksize) + fobj.seek(blocksize, 1) + i = i + blocksize + + def itersizes(self): + sizes = [] + for size, fmts in self.SIZES.items(): + for (fmt, reader) in fmts: + if self.dct.has_key(fmt): + sizes.append(size) + break + return sizes + + def bestsize(self): + sizes = self.itersizes() + if not sizes: + raise SyntaxError, "No 32bit icon resources found" + return max(sizes) + + def dataforsize(self, size): + """ + Get an icon resource as {channel: array}. Note that + the arrays are bottom-up like windows bitmaps and will likely + need to be flipped or transposed in some way. + """ + dct = {} + for code, reader in self.SIZES[size]: + desc = self.dct.get(code) + if desc is not None: + dct.update(reader(self.fobj, desc, size)) + return dct + + def getimage(self, size=None): + if size is None: + size = self.bestsize() + channels = self.dataforsize(size) + im = channels.get("RGB").copy() + try: + im.putalpha(channels["A"]) + except KeyError: + pass + return im + +## +# Image plugin for Mac OS icons. + +class IcnsImageFile(ImageFile.ImageFile): + """ + PIL read-only image support for Mac OS .icns files. + Chooses the best resolution, but will possibly load + a different size image if you mutate the size attribute + before calling 'load'. + + The info dictionary has a key 'sizes' that is a list + of sizes that the icns file has. + """ + + format = "ICNS" + format_description = "Mac OS icns resource" + + def _open(self): + self.icns = IcnsFile(self.fp) + self.mode = 'RGBA' + self.size = self.icns.bestsize() + self.info['sizes'] = self.icns.itersizes() + # Just use this to see if it's loaded or not yet. + self.tile = ('',) + + def load(self): + Image.Image.load(self) + if not self.tile: + return + self.load_prepare() + # This is likely NOT the best way to do it, but whatever. + im = self.icns.getimage(self.size) + self.im = im.im + self.mode = im.mode + self.size = im.size + self.fp = None + self.icns = None + self.tile = () + self.load_end() + + +Image.register_open("ICNS", IcnsImageFile, lambda x: x[:4] == 'icns') +Image.register_extension("ICNS", '.icns') + +if __name__ == '__main__': + import os, sys + im = Image.open(open(sys.argv[1], "rb")) + im.save("out.png") + os.startfile("out.png") diff --git a/PIL/IcoImagePlugin.py b/PIL/IcoImagePlugin.py new file mode 100644 index 000000000..94846dc0c --- /dev/null +++ b/PIL/IcoImagePlugin.py @@ -0,0 +1,86 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Windows Icon support for PIL +# +# Notes: +# uses BmpImagePlugin.py to read the bitmap data. +# +# History: +# 96-05-27 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +import Image, BmpImagePlugin + + +# +# -------------------------------------------------------------------- + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def i32(c): + return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) + + +def _accept(prefix): + return prefix[:4] == "\0\0\1\0" + +## +# Image plugin for Windows Icon files. + +class IcoImageFile(BmpImagePlugin.BmpImageFile): + + format = "ICO" + format_description = "Windows Icon" + + def _open(self): + + # check magic + s = self.fp.read(6) + if not _accept(s): + raise SyntaxError, "not an ICO file" + + # pick the largest icon in the file + m = "" + for i in range(i16(s[4:])): + s = self.fp.read(16) + if not m: + m = s + elif ord(s[0]) > ord(m[0]) and ord(s[1]) > ord(m[1]): + m = s + #print "width", ord(s[0]) + #print "height", ord(s[1]) + #print "colors", ord(s[2]) + #print "reserved", ord(s[3]) + #print "planes", i16(s[4:]) + #print "bitcount", i16(s[6:]) + #print "bytes", i32(s[8:]) + #print "offset", i32(s[12:]) + + # load as bitmap + self._bitmap(i32(m[12:])) + + # patch up the bitmap height + self.size = self.size[0], self.size[1]/2 + d, e, o, a = self.tile[0] + self.tile[0] = d, (0,0)+self.size, o, a + + return + + +# +# -------------------------------------------------------------------- + +Image.register_open("ICO", IcoImageFile, _accept) + +Image.register_extension("ICO", ".ico") diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py new file mode 100644 index 000000000..a76dcf04e --- /dev/null +++ b/PIL/ImImagePlugin.py @@ -0,0 +1,336 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IFUNC IM file handling for PIL +# +# history: +# 1995-09-01 fl Created. +# 1997-01-03 fl Save palette images +# 1997-01-08 fl Added sequence support +# 1997-01-23 fl Added P and RGB save support +# 1997-05-31 fl Read floating point images +# 1997-06-22 fl Save floating point images +# 1997-08-27 fl Read and save 1-bit images +# 1998-06-25 fl Added support for RGB+LUT images +# 1998-07-02 fl Added support for YCC images +# 1998-07-15 fl Renamed offset attribute to avoid name clash +# 1998-12-29 fl Added I;16 support +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# 2003-09-26 fl Added LA/PA support +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.7" + +import re, string +import Image, ImageFile, ImagePalette + + +# -------------------------------------------------------------------- +# Standard tags + +COMMENT = "Comment" +DATE = "Date" +EQUIPMENT = "Digitalization equipment" +FRAMES = "File size (no of images)" +LUT = "Lut" +NAME = "Name" +SCALE = "Scale (x,y)" +SIZE = "Image size (x*y)" +MODE = "Image type" + +TAGS = { COMMENT:0, DATE:0, EQUIPMENT:0, FRAMES:0, LUT:0, NAME:0, + SCALE:0, SIZE:0, MODE:0 } + +OPEN = { + # ifunc93/p3cfunc formats + "0 1 image": ("1", "1"), + "L 1 image": ("1", "1"), + "Greyscale image": ("L", "L"), + "Grayscale image": ("L", "L"), + "RGB image": ("RGB", "RGB;L"), + "RLB image": ("RGB", "RLB"), + "RYB image": ("RGB", "RLB"), + "B1 image": ("1", "1"), + "B2 image": ("P", "P;2"), + "B4 image": ("P", "P;4"), + "X 24 image": ("RGB", "RGB"), + "L 32 S image": ("I", "I;32"), + "L 32 F image": ("F", "F;32"), + # old p3cfunc formats + "RGB3 image": ("RGB", "RGB;T"), + "RYB3 image": ("RGB", "RYB;T"), + # extensions + "LA image": ("LA", "LA;L"), + "RGBA image": ("RGBA", "RGBA;L"), + "RGBX image": ("RGBX", "RGBX;L"), + "CMYK image": ("CMYK", "CMYK;L"), + "YCC image": ("YCbCr", "YCbCr;L"), +} + +# ifunc95 extensions +for i in ["8", "8S", "16", "16S", "32", "32F"]: + OPEN["L %s image" % i] = ("F", "F;%s" % i) + OPEN["L*%s image" % i] = ("F", "F;%s" % i) +for i in ["16", "16L", "16B"]: + OPEN["L %s image" % i] = ("I;%s" % i, "I;%s" % i) + OPEN["L*%s image" % i] = ("I;%s" % i, "I;%s" % i) +for i in ["32S"]: + OPEN["L %s image" % i] = ("I", "I;%s" % i) + OPEN["L*%s image" % i] = ("I", "I;%s" % i) +for i in range(2, 33): + OPEN["L*%s image" % i] = ("F", "F;%s" % i) + + +# -------------------------------------------------------------------- +# Read IM directory + +split = re.compile(r"^([A-Za-z][^:]*):[ \t]*(.*)[ \t]*$") + +def number(s): + try: + return int(s) + except ValueError: + return float(s) + +## +# Image plugin for the IFUNC IM file format. + +class ImImageFile(ImageFile.ImageFile): + + format = "IM" + format_description = "IFUNC Image Memory" + + def _open(self): + + # Quick rejection: if there's not an LF among the first + # 100 bytes, this is (probably) not a text header. + + if not "\n" in self.fp.read(100): + raise SyntaxError, "not an IM file" + self.fp.seek(0) + + n = 0 + + # Default values + self.info[MODE] = "L" + self.info[SIZE] = (512, 512) + self.info[FRAMES] = 1 + + self.rawmode = "L" + + while 1: + + s = self.fp.read(1) + + # Some versions of IFUNC uses \n\r instead of \r\n... + if s == "\r": + continue + + if not s or s[0] == chr(0) or s[0] == chr(26): + break + + # FIXME: this may read whole file if not a text file + s = s + self.fp.readline() + + if len(s) > 100: + raise SyntaxError, "not an IM file" + + if s[-2:] == '\r\n': + s = s[:-2] + elif s[-1:] == '\n': + s = s[:-1] + + try: + m = split.match(s) + except re.error, v: + raise SyntaxError, "not an IM file" + + if m: + + k, v = m.group(1,2) + + # Convert value as appropriate + if k in [FRAMES, SCALE, SIZE]: + v = string.replace(v, "*", ",") + v = tuple(map(number, string.split(v, ","))) + if len(v) == 1: + v = v[0] + elif k == MODE and OPEN.has_key(v): + v, self.rawmode = OPEN[v] + + # Add to dictionary. Note that COMMENT tags are + # combined into a list of strings. + if k == COMMENT: + if self.info.has_key(k): + self.info[k].append(v) + else: + self.info[k] = [v] + else: + self.info[k] = v + + if TAGS.has_key(k): + n = n + 1 + + else: + + raise SyntaxError, "Syntax error in IM header: " + s + + if not n: + raise SyntaxError, "Not an IM file" + + # Basic attributes + self.size = self.info[SIZE] + self.mode = self.info[MODE] + + # Skip forward to start of image data + while s and s[0] != chr(26): + s = self.fp.read(1) + if not s: + raise SyntaxError, "File truncated" + + if self.info.has_key(LUT): + # convert lookup table to palette or lut attribute + palette = self.fp.read(768) + greyscale = 1 # greyscale palette + linear = 1 # linear greyscale palette + for i in range(256): + if palette[i] == palette[i+256] == palette[i+512]: + if palette[i] != chr(i): + linear = 0 + else: + greyscale = 0 + if self.mode == "L" or self.mode == "LA": + if greyscale: + if not linear: + self.lut = map(ord, palette[:256]) + else: + if self.mode == "L": + self.mode = self.rawmode = "P" + elif self.mode == "LA": + self.mode = self.rawmode = "PA" + self.palette = ImagePalette.raw("RGB;L", palette) + elif self.mode == "RGB": + if not greyscale or not linear: + self.lut = map(ord, palette) + + self.frame = 0 + + self.__offset = offs = self.fp.tell() + + self.__fp = self.fp # FIXME: hack + + if self.rawmode[:2] == "F;": + + # ifunc95 formats + try: + # use bit decoder (if necessary) + bits = int(self.rawmode[2:]) + if bits not in [8, 16, 32]: + self.tile = [("bit", (0,0)+self.size, offs, + (bits, 8, 3, 0, -1))] + return + except ValueError: + pass + + if self.rawmode in ["RGB;T", "RYB;T"]: + # Old LabEye/3PC files. Would be very surprised if anyone + # ever stumbled upon such a file ;-) + size = self.size[0] * self.size[1] + self.tile = [("raw", (0,0)+self.size, offs, ("G", 0, -1)), + ("raw", (0,0)+self.size, offs+size, ("R", 0, -1)), + ("raw", (0,0)+self.size, offs+2*size, ("B", 0, -1))] + else: + # LabEye/IFUNC files + self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))] + + def seek(self, frame): + + if frame < 0 or frame >= self.info[FRAMES]: + raise EOFError, "seek outside sequence" + + if self.frame == frame: + return + + self.frame = frame + + if self.mode == "1": + bits = 1 + else: + bits = 8 * len(self.mode) + + size = ((self.size[0] * bits + 7) / 8) * self.size[1] + offs = self.__offset + frame * size + + self.fp = self.__fp + + self.tile = [("raw", (0,0)+self.size, offs, (self.rawmode, 0, -1))] + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- +# Save IM files + +SAVE = { + # mode: (im type, raw mode) + "1": ("0 1", "1"), + "L": ("Greyscale", "L"), + "LA": ("LA", "LA;L"), + "P": ("Greyscale", "P"), + "PA": ("LA", "PA;L"), + "I": ("L 32S", "I;32S"), + "I;16": ("L 16", "I;16"), + "I;16L": ("L 16L", "I;16L"), + "I;16B": ("L 16B", "I;16B"), + "F": ("L 32F", "F;32F"), + "RGB": ("RGB", "RGB;L"), + "RGBA": ("RGBA", "RGBA;L"), + "RGBX": ("RGBX", "RGBX;L"), + "CMYK": ("CMYK", "CMYK;L"), + "YCbCr": ("YCC", "YCbCr;L") +} + +def _save(im, fp, filename, check=0): + + try: + type, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError, "Cannot save %s images as IM" % im.mode + + try: + frames = im.encoderinfo["frames"] + except KeyError: + frames = 1 + + if check: + return check + + fp.write("Image type: %s image\r\n" % type) + if filename: + fp.write("Name: %s\r\n" % filename) + fp.write("Image size (x*y): %d*%d\r\n" % im.size) + fp.write("File size (no of images): %d\r\n" % frames) + if im.mode == "P": + fp.write("Lut: 1\r\n") + fp.write("\000" * (511-fp.tell()) + "\032") + if im.mode == "P": + fp.write(im.im.getpalette("RGB", "RGB;L")) # 768 bytes + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, -1))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("IM", ImImageFile) +Image.register_save("IM", _save) + +Image.register_extension("IM", ".im") diff --git a/PIL/Image.py b/PIL/Image.py new file mode 100644 index 000000000..77a532afa --- /dev/null +++ b/PIL/Image.py @@ -0,0 +1,2127 @@ +# +# The Python Imaging Library. +# $Id$ +# +# the Image class wrapper +# +# partial release history: +# 1995-09-09 fl Created +# 1996-03-11 fl PIL release 0.0 (proof of concept) +# 1996-04-30 fl PIL release 0.1b1 +# 1999-07-28 fl PIL release 1.0 final +# 2000-06-07 fl PIL release 1.1 +# 2000-10-20 fl PIL release 1.1.1 +# 2001-05-07 fl PIL release 1.1.2 +# 2002-03-15 fl PIL release 1.1.3 +# 2003-05-10 fl PIL release 1.1.4 +# 2005-03-28 fl PIL release 1.1.5 +# 2006-12-02 fl PIL release 1.1.6 +# 2009-11-15 fl PIL release 1.1.7 +# +# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-2009 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +VERSION = "1.1.7" + +try: + import warnings +except ImportError: + warnings = None + +class _imaging_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imaging C module is not installed") + +try: + # give Tk a chance to set up the environment, in case we're + # using an _imaging module linked against libtcl/libtk (use + # __import__ to hide this from naive packagers; we don't really + # depend on Tk unless ImageTk is used, and that module already + # imports Tkinter) + __import__("FixTk") +except ImportError: + pass + +try: + # If the _imaging C module is not present, you can still use + # the "open" function to identify files, but you cannot load + # them. Note that other modules should not refer to _imaging + # directly; import Image and use the Image.core variable instead. + import _imaging + core = _imaging + del _imaging +except ImportError, v: + core = _imaging_not_installed() + if str(v)[:20] == "Module use of python" and warnings: + # The _imaging C module is present, but not compiled for + # the right version (windows only). Print a warning, if + # possible. + warnings.warn( + "The _imaging extension was built for another version " + "of Python; most PIL functions will be disabled", + RuntimeWarning + ) + +import ImageMode +import ImagePalette + +import os, string, sys + +# type stuff +from types import IntType, StringType, TupleType + +try: + UnicodeStringType = type(unicode("")) + ## + # (Internal) Checks if an object is a string. If the current + # Python version supports Unicode, this checks for both 8-bit + # and Unicode strings. + def isStringType(t): + return isinstance(t, StringType) or isinstance(t, UnicodeStringType) +except NameError: + def isStringType(t): + return isinstance(t, StringType) + +## +# (Internal) Checks if an object is a tuple. + +def isTupleType(t): + return isinstance(t, TupleType) + +## +# (Internal) Checks if an object is an image object. + +def isImageType(t): + return hasattr(t, "im") + +## +# (Internal) Checks if an object is a string, and that it points to a +# directory. + +def isDirectory(f): + return isStringType(f) and os.path.isdir(f) + +from operator import isNumberType, isSequenceType + +# +# Debug level + +DEBUG = 0 + +# +# Constants (also defined in _imagingmodule.c!) + +NONE = 0 + +# transpose +FLIP_LEFT_RIGHT = 0 +FLIP_TOP_BOTTOM = 1 +ROTATE_90 = 2 +ROTATE_180 = 3 +ROTATE_270 = 4 + +# transforms +AFFINE = 0 +EXTENT = 1 +PERSPECTIVE = 2 +QUAD = 3 +MESH = 4 + +# resampling filters +NONE = 0 +NEAREST = 0 +ANTIALIAS = 1 # 3-lobed lanczos +LINEAR = BILINEAR = 2 +CUBIC = BICUBIC = 3 + +# dithers +NONE = 0 +NEAREST = 0 +ORDERED = 1 # Not yet implemented +RASTERIZE = 2 # Not yet implemented +FLOYDSTEINBERG = 3 # default + +# palettes/quantizers +WEB = 0 +ADAPTIVE = 1 + +# categories +NORMAL = 0 +SEQUENCE = 1 +CONTAINER = 2 + +# -------------------------------------------------------------------- +# Registries + +ID = [] +OPEN = {} +MIME = {} +SAVE = {} +EXTENSION = {} + +# -------------------------------------------------------------------- +# Modes supported by this version + +_MODEINFO = { + # NOTE: this table will be removed in future versions. use + # getmode* functions or ImageMode descriptors instead. + + # official modes + "1": ("L", "L", ("1",)), + "L": ("L", "L", ("L",)), + "I": ("L", "I", ("I",)), + "F": ("L", "F", ("F",)), + "P": ("RGB", "L", ("P",)), + "RGB": ("RGB", "L", ("R", "G", "B")), + "RGBX": ("RGB", "L", ("R", "G", "B", "X")), + "RGBA": ("RGB", "L", ("R", "G", "B", "A")), + "CMYK": ("RGB", "L", ("C", "M", "Y", "K")), + "YCbCr": ("RGB", "L", ("Y", "Cb", "Cr")), + + # Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and + # BGR;24. Use these modes only if you know exactly what you're + # doing... + +} + +try: + byteorder = sys.byteorder +except AttributeError: + import struct + if struct.unpack("h", "\0\1")[0] == 1: + byteorder = "big" + else: + byteorder = "little" + +if byteorder == 'little': + _ENDIAN = '<' +else: + _ENDIAN = '>' + +_MODE_CONV = { + # official modes + "1": ('|b1', None), # broken + "L": ('|u1', None), + "I": (_ENDIAN + 'i4', None), + "F": (_ENDIAN + 'f4', None), + "P": ('|u1', None), + "RGB": ('|u1', 3), + "RGBX": ('|u1', 4), + "RGBA": ('|u1', 4), + "CMYK": ('|u1', 4), + "YCbCr": ('|u1', 4), +} + +def _conv_type_shape(im): + shape = im.size[1], im.size[0] + typ, extra = _MODE_CONV[im.mode] + if extra is None: + return shape, typ + else: + return shape+(extra,), typ + + +MODES = _MODEINFO.keys() +MODES.sort() + +# raw modes that may be memory mapped. NOTE: if you change this, you +# may have to modify the stride calculation in map.c too! +_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") + +## +# Gets the "base" mode for given mode. This function returns "L" for +# images that contain grayscale data, and "RGB" for images that +# contain color data. +# +# @param mode Input mode. +# @return "L" or "RGB". +# @exception KeyError If the input mode was not a standard mode. + +def getmodebase(mode): + return ImageMode.getmode(mode).basemode + +## +# Gets the storage type mode. Given a mode, this function returns a +# single-layer mode suitable for storing individual bands. +# +# @param mode Input mode. +# @return "L", "I", or "F". +# @exception KeyError If the input mode was not a standard mode. + +def getmodetype(mode): + return ImageMode.getmode(mode).basetype + +## +# Gets a list of individual band names. Given a mode, this function +# returns a tuple containing the names of individual bands (use +# {@link #getmodetype} to get the mode used to store each individual +# band. +# +# @param mode Input mode. +# @return A tuple containing band names. The length of the tuple +# gives the number of bands in an image of the given mode. +# @exception KeyError If the input mode was not a standard mode. + +def getmodebandnames(mode): + return ImageMode.getmode(mode).bands + +## +# Gets the number of individual bands for this mode. +# +# @param mode Input mode. +# @return The number of bands in this mode. +# @exception KeyError If the input mode was not a standard mode. + +def getmodebands(mode): + return len(ImageMode.getmode(mode).bands) + +# -------------------------------------------------------------------- +# Helpers + +_initialized = 0 + +## +# Explicitly loads standard file format drivers. + +def preinit(): + "Load standard file format drivers." + + global _initialized + if _initialized >= 1: + return + + try: + import BmpImagePlugin + except ImportError: + pass + try: + import GifImagePlugin + except ImportError: + pass + try: + import JpegImagePlugin + except ImportError: + pass + try: + import PpmImagePlugin + except ImportError: + pass + try: + import PngImagePlugin + except ImportError: + pass +# try: +# import TiffImagePlugin +# except ImportError: +# pass + + _initialized = 1 + +## +# Explicitly initializes the Python Imaging Library. This function +# loads all available file format drivers. + +def init(): + "Load all file format drivers." + + global _initialized + if _initialized >= 2: + return 0 + + visited = {} + + directories = sys.path + + try: + directories = directories + [os.path.dirname(__file__)] + except NameError: + pass + + # only check directories (including current, if present in the path) + for directory in filter(isDirectory, directories): + fullpath = os.path.abspath(directory) + if visited.has_key(fullpath): + continue + for file in os.listdir(directory): + if file[-14:] == "ImagePlugin.py": + f, e = os.path.splitext(file) + try: + sys.path.insert(0, directory) + try: + __import__(f, globals(), locals(), []) + finally: + del sys.path[0] + except ImportError: + if DEBUG: + print "Image: failed to import", + print f, ":", sys.exc_value + visited[fullpath] = None + + if OPEN or SAVE: + _initialized = 2 + return 1 + +# -------------------------------------------------------------------- +# Codec factories (used by tostring/fromstring and ImageFile.load) + +def _getdecoder(mode, decoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isTupleType(args): + args = (args,) + + try: + # get decoder + decoder = getattr(core, decoder_name + "_decoder") + # print decoder, (mode,) + args + extra + return apply(decoder, (mode,) + args + extra) + except AttributeError: + raise IOError("decoder %s not available" % decoder_name) + +def _getencoder(mode, encoder_name, args, extra=()): + + # tweak arguments + if args is None: + args = () + elif not isTupleType(args): + args = (args,) + + try: + # get encoder + encoder = getattr(core, encoder_name + "_encoder") + # print encoder, (mode,) + args + extra + return apply(encoder, (mode,) + args + extra) + except AttributeError: + raise IOError("encoder %s not available" % encoder_name) + + +# -------------------------------------------------------------------- +# Simple expression analyzer + +class _E: + def __init__(self, data): self.data = data + def __coerce__(self, other): return self, _E(other) + def __add__(self, other): return _E((self.data, "__add__", other.data)) + def __mul__(self, other): return _E((self.data, "__mul__", other.data)) + +def _getscaleoffset(expr): + stub = ["stub"] + data = expr(_E(stub)).data + try: + (a, b, c) = data # simplified syntax + if (a is stub and b == "__mul__" and isNumberType(c)): + return c, 0.0 + if (a is stub and b == "__add__" and isNumberType(c)): + return 1.0, c + except TypeError: pass + try: + ((a, b, c), d, e) = data # full syntax + if (a is stub and b == "__mul__" and isNumberType(c) and + d == "__add__" and isNumberType(e)): + return c, e + except TypeError: pass + raise ValueError("illegal expression") + + +# -------------------------------------------------------------------- +# Implementation wrapper + +## +# This class represents an image object. To create Image objects, use +# the appropriate factory functions. There's hardly ever any reason +# to call the Image constructor directly. +# +# @see #open +# @see #new +# @see #fromstring + +class Image: + + format = None + format_description = None + + def __init__(self): + # FIXME: take "new" parameters / other image? + # FIXME: turn mode and size into delegating properties? + self.im = None + self.mode = "" + self.size = (0, 0) + self.palette = None + self.info = {} + self.category = NORMAL + self.readonly = 0 + + def _new(self, im): + new = Image() + new.im = im + new.mode = im.mode + new.size = im.size + new.palette = self.palette + if im.mode == "P": + new.palette = ImagePalette.ImagePalette() + try: + new.info = self.info.copy() + except AttributeError: + # fallback (pre-1.5.2) + new.info = {} + for k, v in self.info: + new.info[k] = v + return new + + _makeself = _new # compatibility + + def _copy(self): + self.load() + self.im = self.im.copy() + self.readonly = 0 + + def _dump(self, file=None, format=None): + import tempfile + if not file: + file = tempfile.mktemp() + self.load() + if not format or format == "PPM": + self.im.save_ppm(file) + else: + file = file + "." + format + self.save(file, format) + return file + + def __repr__(self): + return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( + self.__class__.__module__, self.__class__.__name__, + self.mode, self.size[0], self.size[1], + id(self) + ) + + def __getattr__(self, name): + if name == "__array_interface__": + # numpy array interface support + new = {} + shape, typestr = _conv_type_shape(self) + new['shape'] = shape + new['typestr'] = typestr + new['data'] = self.tostring() + return new + raise AttributeError(name) + + ## + # Returns a string containing pixel data. + # + # @param encoder_name What encoder to use. The default is to + # use the standard "raw" encoder. + # @param *args Extra arguments to the encoder. + # @return An 8-bit string. + + def tostring(self, encoder_name="raw", *args): + "Return image as a binary string" + + # may pass tuple instead of argument list + if len(args) == 1 and isTupleType(args[0]): + args = args[0] + + if encoder_name == "raw" and args == (): + args = self.mode + + self.load() + + # unpack data + e = _getencoder(self.mode, encoder_name, args) + e.setimage(self.im) + + bufsize = max(65536, self.size[0] * 4) # see RawEncode.c + + data = [] + while 1: + l, s, d = e.encode(bufsize) + data.append(d) + if s: + break + if s < 0: + raise RuntimeError("encoder error %d in tostring" % s) + + return string.join(data, "") + + ## + # Returns the image converted to an X11 bitmap. This method + # only works for mode "1" images. + # + # @param name The name prefix to use for the bitmap variables. + # @return A string containing an X11 bitmap. + # @exception ValueError If the mode is not "1" + + def tobitmap(self, name="image"): + "Return image as an XBM bitmap" + + self.load() + if self.mode != "1": + raise ValueError("not a bitmap") + data = self.tostring("xbm") + return string.join(["#define %s_width %d\n" % (name, self.size[0]), + "#define %s_height %d\n"% (name, self.size[1]), + "static char %s_bits[] = {\n" % name, data, "};"], "") + + ## + # Loads this image with pixel data from a string. + #

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

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

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

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

+ # When translating a greyscale image into a bilevel image (mode + # "1"), all non-zero values are set to 255 (white). To use other + # thresholds, use the {@link #Image.point} method. + # + # @def convert(mode, matrix=None, **options) + # @param mode The requested mode. + # @param matrix An optional conversion matrix. If given, this + # should be 4- or 16-tuple containing floating point values. + # @param options Additional options, given as keyword arguments. + # @keyparam dither Dithering method, used when converting from + # mode "RGB" to "P". + # Available methods are NONE or FLOYDSTEINBERG (default). + # @keyparam palette Palette to use when converting from mode "RGB" + # to "P". Available palettes are WEB or ADAPTIVE. + # @keyparam colors Number of colors to use for the ADAPTIVE palette. + # Defaults to 256. + # @return An Image object. + + def convert(self, mode=None, data=None, dither=None, + palette=WEB, colors=256): + "Convert to other pixel format" + + if not mode: + # determine default mode + if self.mode == "P": + self.load() + if self.palette: + mode = self.palette.mode + else: + mode = "RGB" + else: + return self.copy() + + self.load() + + if data: + # matrix conversion + if mode not in ("L", "RGB"): + raise ValueError("illegal conversion") + im = self.im.convert_matrix(mode, data) + return self._new(im) + + if mode == "P" and palette == ADAPTIVE: + im = self.im.quantize(colors) + return self._new(im) + + # colourspace conversion + if dither is None: + dither = FLOYDSTEINBERG + + try: + im = self.im.convert(mode, dither) + except ValueError: + try: + # normalize source image and try again + im = self.im.convert(getmodebase(self.mode)) + im = im.convert(mode, dither) + except KeyError: + raise ValueError("illegal conversion") + + return self._new(im) + + def quantize(self, colors=256, method=0, kmeans=0, palette=None): + + # methods: + # 0 = median cut + # 1 = maximum coverage + + # NOTE: this functionality will be moved to the extended + # quantizer interface in a later version of PIL. + + self.load() + + if palette: + # use palette from reference image + palette.load() + if palette.mode != "P": + raise ValueError("bad mode for palette image") + if self.mode != "RGB" and self.mode != "L": + raise ValueError( + "only RGB or L mode images can be quantized to a palette" + ) + im = self.im.convert("P", 1, palette.im) + return self._makeself(im) + + im = self.im.quantize(colors, method, kmeans) + return self._new(im) + + ## + # Copies this image. Use this method if you wish to paste things + # into an image, but still retain the original. + # + # @return An Image object. + + def copy(self): + "Copy raster data" + + self.load() + im = self.im.copy() + return self._new(im) + + ## + # Returns a rectangular region from this image. The box is a + # 4-tuple defining the left, upper, right, and lower pixel + # coordinate. + #

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

+ # Note that this method modifies the Image object in place. If + # the image has already been loaded, this method has no effect. + # + # @param mode The requested mode. + # @param size The requested size. + + def draft(self, mode, size): + "Configure image decoder" + + pass + + def _expand(self, xmargin, ymargin=None): + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin, 0)) + + ## + # Filters this image using the given filter. For a list of + # available filters, see the ImageFilter module. + # + # @param filter Filter kernel. + # @return An Image object. + # @see ImageFilter + + def filter(self, filter): + "Apply environment filter to image" + + self.load() + + if callable(filter): + filter = filter() + if not hasattr(filter, "filter"): + raise TypeError("filter argument should be ImageFilter.Filter instance or class") + + if self.im.bands == 1: + return self._new(filter.filter(self.im)) + # fix to handle multiband images since _imaging doesn't + ims = [] + for c in range(self.im.bands): + ims.append(self._new(filter.filter(self.im.getband(c)))) + return merge(self.mode, ims) + + ## + # Returns a tuple containing the name of each band in this image. + # For example, getbands on an RGB image returns ("R", "G", "B"). + # + # @return A tuple containing band names. + + def getbands(self): + "Get band names" + + return ImageMode.getmode(self.mode).bands + + ## + # Calculates the bounding box of the non-zero regions in the + # image. + # + # @return The bounding box is returned as a 4-tuple defining the + # left, upper, right, and lower pixel coordinate. If the image + # is completely empty, this method returns None. + + def getbbox(self): + "Get bounding box of actual data (non-zero pixels) in image" + + self.load() + return self.im.getbbox() + + ## + # Returns a list of colors used in this image. + # + # @param maxcolors Maximum number of colors. If this number is + # exceeded, this method returns None. The default limit is + # 256 colors. + # @return An unsorted list of (count, pixel) values. + + def getcolors(self, maxcolors=256): + "Get colors from image, up to given limit" + + self.load() + if self.mode in ("1", "L", "P"): + h = self.im.histogram() + out = [] + for i in range(256): + if h[i]: + out.append((h[i], i)) + if len(out) > maxcolors: + return None + return out + return self.im.getcolors(maxcolors) + + ## + # Returns the contents of this image as a sequence object + # containing pixel values. The sequence object is flattened, so + # that values for line one follow directly after the values of + # line zero, and so on. + #

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

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

+ # If a mask is provided, the method returns a histogram for those + # parts of the image where the mask image is non-zero. The mask + # image must have the same size as the image, and be either a + # bi-level image (mode "1") or a greyscale image ("L"). + # + # @def histogram(mask=None) + # @param mask An optional mask. + # @return A list containing pixel counts. + + def histogram(self, mask=None, extrema=None): + "Take histogram of image" + + self.load() + if mask: + mask.load() + return self.im.histogram((0, 0), mask.im) + if self.mode in ("I", "F"): + if extrema is None: + extrema = self.getextrema() + return self.im.histogram(extrema) + return self.im.histogram() + + ## + # (Deprecated) Returns a copy of the image where the data has been + # offset by the given distances. Data wraps around the edges. If + # yoffset is omitted, it is assumed to be equal to xoffset. + #

+ # This method is deprecated. New code should use the offset + # function in the ImageChops module. + # + # @param xoffset The horizontal distance. + # @param yoffset The vertical distance. If omitted, both + # distances are set to the same value. + # @return An Image object. + + def offset(self, xoffset, yoffset=None): + "(deprecated) Offset image in horizontal and/or vertical direction" + if warnings: + warnings.warn( + "'offset' is deprecated; use 'ImageChops.offset' instead", + DeprecationWarning, stacklevel=2 + ) + import ImageChops + return ImageChops.offset(self, xoffset, yoffset) + + ## + # Pastes another image into this image. The box argument is either + # a 2-tuple giving the upper left corner, a 4-tuple defining the + # left, upper, right, and lower pixel coordinate, or None (same as + # (0, 0)). If a 4-tuple is given, the size of the pasted image + # must match the size of the region. + #

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

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

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

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

+ # If an image is given as the second argument and there is no + # third, the box defaults to (0, 0), and the second argument + # is interpreted as a mask image. + # @param mask An optional mask image. + # @return An Image object. + + def paste(self, im, box=None, mask=None): + "Paste other image into region" + + if isImageType(box) and mask is None: + # abbreviated paste(im, mask) syntax + mask = box; box = None + + if box is None: + # cover all of self + box = (0, 0) + self.size + + if len(box) == 2: + # lower left corner given; get size from image or mask + if isImageType(im): + size = im.size + elif isImageType(mask): + size = mask.size + else: + # FIXME: use self.size here? + raise ValueError( + "cannot determine region size; use 4-item box" + ) + box = box + (box[0]+size[0], box[1]+size[1]) + + if isStringType(im): + import ImageColor + im = ImageColor.getcolor(im, self.mode) + + elif isImageType(im): + im.load() + if self.mode != im.mode: + if self.mode != "RGB" or im.mode not in ("RGBA", "RGBa"): + # should use an adapter for this! + im = im.convert(self.mode) + im = im.im + + self.load() + if self.readonly: + self._copy() + + if mask: + mask.load() + self.im.paste(im, box, mask.im) + else: + self.im.paste(im, box) + + ## + # Maps this image through a lookup table or function. + # + # @param lut A lookup table, containing 256 values per band in the + # image. A function can be used instead, it should take a single + # argument. The function is called once for each possible pixel + # value, and the resulting table is applied to all bands of the + # image. + # @param mode Output mode (default is same as input). In the + # current version, this can only be used if the source image + # has mode "L" or "P", and the output has mode "1". + # @return An Image object. + + def point(self, lut, mode=None): + "Map image through lookup table" + + self.load() + + if isinstance(lut, ImagePointHandler): + return lut.point(self) + + if not isSequenceType(lut): + # if it isn't a list, it should be a function + if self.mode in ("I", "I;16", "F"): + # check if the function can be used with point_transform + scale, offset = _getscaleoffset(lut) + return self._new(self.im.point_transform(scale, offset)) + # for other modes, convert the function to a table + lut = map(lut, range(256)) * self.im.bands + + if self.mode == "F": + # FIXME: _imaging returns a confusing error message for this case + raise ValueError("point operation not supported for this mode") + + return self._new(self.im.point(lut, mode)) + + ## + # Adds or replaces the alpha layer in this image. If the image + # does not have an alpha layer, it's converted to "LA" or "RGBA". + # The new layer must be either "L" or "1". + # + # @param im The new alpha layer. This can either be an "L" or "1" + # image having the same size as this image, or an integer or + # other color value. + + def putalpha(self, alpha): + "Set alpha layer" + + self.load() + if self.readonly: + self._copy() + + if self.mode not in ("LA", "RGBA"): + # attempt to promote self to a matching alpha mode + try: + mode = getmodebase(self.mode) + "A" + try: + self.im.setmode(mode) + except (AttributeError, ValueError): + # do things the hard way + im = self.im.convert(mode) + if im.mode not in ("LA", "RGBA"): + raise ValueError # sanity check + self.im = im + self.mode = self.im.mode + except (KeyError, ValueError): + raise ValueError("illegal image mode") + + if self.mode == "LA": + band = 1 + else: + band = 3 + + if isImageType(alpha): + # alpha layer + if alpha.mode not in ("1", "L"): + raise ValueError("illegal image mode") + alpha.load() + if alpha.mode == "1": + alpha = alpha.convert("L") + else: + # constant alpha + try: + self.im.fillband(band, alpha) + except (AttributeError, ValueError): + # do things the hard way + alpha = new("L", self.size, alpha) + else: + return + + self.im.putband(alpha.im, band) + + ## + # Copies pixel data to this image. This method copies data from a + # sequence object into the image, starting at the upper left + # corner (0, 0), and continuing until either the image or the + # sequence ends. The scale and offset values are used to adjust + # the sequence values: pixel = value*scale + offset. + # + # @param data A sequence object. + # @param scale An optional scale value. The default is 1.0. + # @param offset An optional offset value. The default is 0.0. + + def putdata(self, data, scale=1.0, offset=0.0): + "Put data from a sequence object into an image." + + self.load() + if self.readonly: + self._copy() + + self.im.putdata(data, scale, offset) + + ## + # Attaches a palette to this image. The image must be a "P" or + # "L" image, and the palette sequence must contain 768 integer + # values, where each group of three values represent the red, + # green, and blue values for the corresponding pixel + # index. Instead of an integer sequence, you can use an 8-bit + # string. + # + # @def putpalette(data) + # @param data A palette sequence (either a list or a string). + + def putpalette(self, data, rawmode="RGB"): + "Put palette data into an image." + + if self.mode not in ("L", "P"): + raise ValueError("illegal image mode") + self.load() + if isinstance(data, ImagePalette.ImagePalette): + palette = ImagePalette.raw(data.rawmode, data.palette) + else: + if not isStringType(data): + data = string.join(map(chr, data), "") + palette = ImagePalette.raw(rawmode, data) + self.mode = "P" + self.palette = palette + self.palette.mode = "RGB" + self.load() # install new palette + + ## + # Modifies the pixel at the given position. The colour is given as + # a single numerical value for single-band images, and a tuple for + # multi-band images. + #

+ # Note that this method is relatively slow. For more extensive + # changes, use {@link #Image.paste} or the ImageDraw module + # instead. + # + # @param xy The pixel coordinate, given as (x, y). + # @param value The pixel value. + # @see #Image.paste + # @see #Image.putdata + # @see ImageDraw + + def putpixel(self, xy, value): + "Set pixel value" + + self.load() + if self.readonly: + self._copy() + + return self.im.putpixel(xy, value) + + ## + # Returns a resized copy of this image. + # + # @def resize(size, filter=NEAREST) + # @param size The requested size in pixels, as a 2-tuple: + # (width, height). + # @param filter An optional resampling filter. This can be + # one of NEAREST (use nearest neighbour), BILINEAR + # (linear interpolation in a 2x2 environment), BICUBIC + # (cubic spline interpolation in a 4x4 environment), or + # ANTIALIAS (a high-quality downsampling filter). + # If omitted, or if the image has mode "1" or "P", it is + # set NEAREST. + # @return An Image object. + + def resize(self, size, resample=NEAREST): + "Resize image" + + if resample not in (NEAREST, BILINEAR, BICUBIC, ANTIALIAS): + raise ValueError("unknown resampling filter") + + self.load() + + if self.mode in ("1", "P"): + resample = NEAREST + + if resample == ANTIALIAS: + # requires stretch support (imToolkit & PIL 1.1.3) + try: + im = self.im.stretch(size, resample) + except AttributeError: + raise ValueError("unsupported resampling filter") + else: + im = self.im.resize(size, resample) + + return self._new(im) + + ## + # Returns a rotated copy of this image. This method returns a + # copy of this image, rotated the given number of degrees counter + # clockwise around its centre. + # + # @def rotate(angle, filter=NEAREST) + # @param angle In degrees counter clockwise. + # @param filter An optional resampling filter. This can be + # one of NEAREST (use nearest neighbour), BILINEAR + # (linear interpolation in a 2x2 environment), or BICUBIC + # (cubic spline interpolation in a 4x4 environment). + # If omitted, or if the image has mode "1" or "P", it is + # set NEAREST. + # @param expand Optional expansion flag. If true, expands the output + # image to make it large enough to hold the entire rotated image. + # If false or omitted, make the output image the same size as the + # input image. + # @return An Image object. + + def rotate(self, angle, resample=NEAREST, expand=0): + "Rotate image. Angle given as degrees counter-clockwise." + + if expand: + import math + angle = -angle * math.pi / 180 + matrix = [ + math.cos(angle), math.sin(angle), 0.0, + -math.sin(angle), math.cos(angle), 0.0 + ] + def transform(x, y, (a, b, c, d, e, f)=matrix): + return a*x + b*y + c, d*x + e*y + f + + # calculate output size + w, h = self.size + xx = [] + yy = [] + for x, y in ((0, 0), (w, 0), (w, h), (0, h)): + x, y = transform(x, y) + xx.append(x) + yy.append(y) + w = int(math.ceil(max(xx)) - math.floor(min(xx))) + h = int(math.ceil(max(yy)) - math.floor(min(yy))) + + # adjust center + x, y = transform(w / 2.0, h / 2.0) + matrix[2] = self.size[0] / 2.0 - x + matrix[5] = self.size[1] / 2.0 - y + + return self.transform((w, h), AFFINE, matrix, resample) + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + self.load() + + if self.mode in ("1", "P"): + resample = NEAREST + + return self._new(self.im.rotate(angle, resample)) + + ## + # Saves this image under the given filename. If no format is + # specified, the format to use is determined from the filename + # extension, if possible. + #

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

+ # You can use a file object instead of a filename. In this case, + # you must always specify the format. The file object must + # implement the seek, tell, and write + # methods, and be opened in binary mode. + # + # @def save(file, format=None, **options) + # @param file File name or file object. + # @param format Optional format override. If omitted, the + # format to use is determined from the filename extension. + # If a file object was used instead of a filename, this + # parameter should always be used. + # @param **options Extra parameters to the image writer. + # @return None + # @exception KeyError If the output format could not be determined + # from the file name. Use the format option to solve this. + # @exception IOError If the file could not be written. The file + # may have been created, and may contain partial data. + + def save(self, fp, format=None, **params): + "Save image to file or stream" + + if isStringType(fp): + filename = fp + else: + if hasattr(fp, "name") and isStringType(fp.name): + filename = fp.name + else: + filename = "" + + # may mutate self! + self.load() + + self.encoderinfo = params + self.encoderconfig = () + + preinit() + + ext = string.lower(os.path.splitext(filename)[1]) + + if not format: + try: + format = EXTENSION[ext] + except KeyError: + init() + try: + format = EXTENSION[ext] + except KeyError: + raise KeyError(ext) # unknown extension + + try: + save_handler = SAVE[string.upper(format)] + except KeyError: + init() + save_handler = SAVE[string.upper(format)] # unknown format + + if isStringType(fp): + import __builtin__ + fp = __builtin__.open(fp, "wb") + close = 1 + else: + close = 0 + + try: + save_handler(self, fp, filename) + finally: + # do what we can to clean up + if close: + fp.close() + + ## + # Seeks to the given frame in this sequence file. If you seek + # beyond the end of the sequence, the method raises an + # EOFError exception. When a sequence file is opened, the + # library automatically seeks to frame 0. + #

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

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

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

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

+ # Also note that this function modifies the Image object in place. + # If you need to use the full resolution image as well, apply this + # method to a {@link #Image.copy} of the original image. + # + # @param size Requested size. + # @param resample Optional resampling filter. This can be one + # of NEAREST, BILINEAR, BICUBIC, or + # ANTIALIAS (best quality). If omitted, it defaults + # to NEAREST (this will be changed to ANTIALIAS in a + # future version). + # @return None + + def thumbnail(self, size, resample=NEAREST): + "Create thumbnail representation (modifies image in place)" + + # FIXME: the default resampling filter will be changed + # to ANTIALIAS in future versions + + # preserve aspect ratio + x, y = self.size + if x > size[0]: y = max(y * size[0] / x, 1); x = size[0] + if y > size[1]: x = max(x * size[1] / y, 1); y = size[1] + size = x, y + + if size == self.size: + return + + self.draft(None, size) + + self.load() + + try: + im = self.resize(size, resample) + except ValueError: + if resample != ANTIALIAS: + raise + im = self.resize(size, NEAREST) # fallback + + self.im = im.im + self.mode = im.mode + self.size = size + + self.readonly = 0 + + # FIXME: the different tranform methods need further explanation + # instead of bloating the method docs, add a separate chapter. + + ## + # Transforms this image. This method creates a new image with the + # given size, and the same mode as the original, and copies data + # to the new image using the given transform. + #

+ # @def transform(size, method, data, resample=NEAREST) + # @param size The output size. + # @param method The transformation method. This is one of + # EXTENT (cut out a rectangular subregion), AFFINE + # (affine transform), PERSPECTIVE (perspective + # transform), QUAD (map a quadrilateral to a + # rectangle), or MESH (map a number of source quadrilaterals + # in one operation). + # @param data Extra data to the transformation method. + # @param resample Optional resampling filter. It can be one of + # NEAREST (use nearest neighbour), BILINEAR + # (linear interpolation in a 2x2 environment), or + # BICUBIC (cubic spline interpolation in a 4x4 + # environment). If omitted, or if the image has mode + # "1" or "P", it is set to NEAREST. + # @return An Image object. + + def transform(self, size, method, data=None, resample=NEAREST, fill=1): + "Transform image" + + if isinstance(method, ImageTransformHandler): + return method.transform(size, self, resample=resample, fill=fill) + if hasattr(method, "getdata"): + # compatibility w. old-style transform objects + method, data = method.getdata() + if data is None: + raise ValueError("missing method data") + im = new(self.mode, size, None) + if method == MESH: + # list of quads + for box, quad in data: + im.__transformer(box, self, QUAD, quad, resample, fill) + else: + im.__transformer((0, 0)+size, self, method, data, resample, fill) + + return im + + def __transformer(self, box, image, method, data, + resample=NEAREST, fill=1): + + # FIXME: this should be turned into a lazy operation (?) + + w = box[2]-box[0] + h = box[3]-box[1] + + if method == AFFINE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4]) + elif method == EXTENT: + # convert extent to an affine transform + x0, y0, x1, y1 = data + xs = float(x1 - x0) / w + ys = float(y1 - y0) / h + method = AFFINE + data = (x0 + xs/2, xs, 0, y0 + ys/2, 0, ys) + elif method == PERSPECTIVE: + # change argument order to match implementation + data = (data[2], data[0], data[1], + data[5], data[3], data[4], + data[6], data[7]) + elif method == QUAD: + # quadrilateral warp. data specifies the four corners + # given as NW, SW, SE, and NE. + nw = data[0:2]; sw = data[2:4]; se = data[4:6]; ne = data[6:8] + x0, y0 = nw; As = 1.0 / w; At = 1.0 / h + data = (x0, (ne[0]-x0)*As, (sw[0]-x0)*At, + (se[0]-sw[0]-ne[0]+x0)*As*At, + y0, (ne[1]-y0)*As, (sw[1]-y0)*At, + (se[1]-sw[1]-ne[1]+y0)*As*At) + else: + raise ValueError("unknown transformation method") + + if resample not in (NEAREST, BILINEAR, BICUBIC): + raise ValueError("unknown resampling filter") + + image.load() + + self.load() + + if image.mode in ("1", "P"): + resample = NEAREST + + self.im.transform2(box, image.im, method, data, resample, fill) + + ## + # Returns a flipped or rotated copy of this image. + # + # @param method One of FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, + # ROTATE_90, ROTATE_180, or ROTATE_270. + + def transpose(self, method): + "Transpose image (flip or rotate in 90 degree steps)" + + self.load() + im = self.im.transpose(method) + return self._new(im) + +# -------------------------------------------------------------------- +# Lazy operations + +class _ImageCrop(Image): + + def __init__(self, im, box): + + Image.__init__(self) + + x0, y0, x1, y1 = box + if x1 < x0: + x1 = x0 + if y1 < y0: + y1 = y0 + + self.mode = im.mode + self.size = x1-x0, y1-y0 + + self.__crop = x0, y0, x1, y1 + + self.im = im.im + + def load(self): + + # lazy evaluation! + if self.__crop: + self.im = self.im.crop(self.__crop) + self.__crop = None + + if self.im: + return self.im.pixel_access(self.readonly) + + # FIXME: future versions should optimize crop/paste + # sequences! + +# -------------------------------------------------------------------- +# Abstract handlers. + +class ImagePointHandler: + # used as a mixin by point transforms (for use with im.point) + pass + +class ImageTransformHandler: + # used as a mixin by geometry transforms (for use with im.transform) + pass + +# -------------------------------------------------------------------- +# Factories + +# +# Debugging + +def _wedge(): + "Create greyscale wedge (for debugging only)" + + return Image()._new(core.wedge("L")) + +## +# Creates a new image with the given mode and size. +# +# @param mode The mode to use for the new image. +# @param size A 2-tuple, containing (width, height) in pixels. +# @param color What colour to use for the image. Default is black. +# If given, this should be a single integer or floating point value +# for single-band modes, and a tuple for multi-band modes (one value +# per band). When creating RGB images, you can also use colour +# strings as supported by the ImageColor module. If the colour is +# None, the image is not initialised. +# @return An Image object. + +def new(mode, size, color=0): + "Create a new image" + + if color is None: + # don't initialize + return Image()._new(core.new(mode, size)) + + if isStringType(color): + # css3-style specifier + + import ImageColor + color = ImageColor.getcolor(color, mode) + + return Image()._new(core.fill(mode, size, color)) + +## +# Creates an image memory from pixel data in a string. +#

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

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

+# Note that this function decodes pixel data only, not entire images. +# If you have an entire image in a string, wrap it in a +# StringIO object, and use {@link #open} to load it. +# +# @param mode The image mode. +# @param size The image size. +# @param data An 8-bit string containing raw data for the given mode. +# @param decoder_name What decoder to use. +# @param *args Additional parameters for the given decoder. +# @return An Image object. + +def fromstring(mode, size, data, decoder_name="raw", *args): + "Load image from string" + + # may pass tuple instead of argument list + if len(args) == 1 and isTupleType(args[0]): + args = args[0] + + if decoder_name == "raw" and args == (): + args = mode + + im = new(mode, size) + im.fromstring(data, decoder_name, args) + return im + +## +# (New in 1.1.4) Creates an image memory from pixel data in a string +# or byte buffer. +#

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

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

+# In the current version, the default parameters used for the "raw" +# decoder differs from that used for {@link fromstring}. This is a +# bug, and will probably be fixed in a future release. The current +# release issues a warning if you do this; to disable the warning, +# you should provide the full set of parameters. See below for +# details. +# +# @param mode The image mode. +# @param size The image size. +# @param data An 8-bit string or other buffer object containing raw +# data for the given mode. +# @param decoder_name What decoder to use. +# @param *args Additional parameters for the given decoder. For the +# default encoder ("raw"), it's recommended that you provide the +# full set of parameters: +# frombuffer(mode, size, data, "raw", mode, 0, 1). +# @return An Image object. +# @since 1.1.4 + +def frombuffer(mode, size, data, decoder_name="raw", *args): + "Load image from string or buffer" + + # may pass tuple instead of argument list + if len(args) == 1 and isTupleType(args[0]): + args = args[0] + + if decoder_name == "raw": + if args == (): + if warnings: + warnings.warn( + "the frombuffer defaults may change in a future release; " + "for portability, change the call to read:\n" + " frombuffer(mode, size, data, 'raw', mode, 0, 1)", + RuntimeWarning, stacklevel=2 + ) + args = mode, 0, -1 # may change to (mode, 0, 1) post-1.1.6 + if args[0] in _MAPMODES: + im = new(mode, (1,1)) + im = im._new( + core.map_buffer(data, size, decoder_name, None, 0, args) + ) + im.readonly = 1 + return im + + return fromstring(mode, size, data, decoder_name, args) + + +## +# (New in 1.1.6) Creates an image memory from an object exporting +# the array interface (using the buffer protocol). +# +# If obj is not contiguous, then the tostring method is called +# and {@link frombuffer} is used. +# +# @param obj Object with array interface +# @param mode Mode to use (will be determined from type if None) +# @return An image memory. + +def fromarray(obj, mode=None): + arr = obj.__array_interface__ + shape = arr['shape'] + ndim = len(shape) + try: + strides = arr['strides'] + except KeyError: + strides = None + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr['typestr'] + mode, rawmode = _fromarray_typemap[typekey] + except KeyError: + # print typekey + raise TypeError("Cannot handle this data type") + else: + rawmode = mode + if mode in ["1", "L", "I", "P", "F"]: + ndmax = 2 + elif mode == "RGB": + ndmax = 3 + else: + ndmax = 4 + if ndim > ndmax: + raise ValueError("Too many dimensions.") + + size = shape[1], shape[0] + if strides is not None: + obj = obj.tostring() + + return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) + +_fromarray_typemap = { + # (shape, typestr) => mode, rawmode + # first two members of shape are set to one + # ((1, 1), "|b1"): ("1", "1"), # broken + ((1, 1), "|u1"): ("L", "L"), + ((1, 1), "|i1"): ("I", "I;8"), + ((1, 1), "i2"): ("I", "I;16B"), + ((1, 1), "i4"): ("I", "I;32B"), + ((1, 1), "f4"): ("F", "F;32BF"), + ((1, 1), "f8"): ("F", "F;64BF"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), + } + +# shortcuts +_fromarray_typemap[((1, 1), _ENDIAN + "i4")] = ("I", "I") +_fromarray_typemap[((1, 1), _ENDIAN + "f4")] = ("F", "F") + +## +# Opens and identifies the given image file. +#

+# This is a lazy operation; this function identifies the file, but the +# actual image data is not read from the file until you try to process +# the data (or call the {@link #Image.load} method). +# +# @def open(file, mode="r") +# @param file A filename (string) or a file object. The file object +# must implement read, seek, and tell methods, +# and be opened in binary mode. +# @param mode The mode. If given, this argument must be "r". +# @return An Image object. +# @exception IOError If the file cannot be found, or the image cannot be +# opened and identified. +# @see #new + +def open(fp, mode="r"): + "Open an image file, without loading the raster data" + + if mode != "r": + raise ValueError("bad mode") + + if isStringType(fp): + import __builtin__ + filename = fp + fp = __builtin__.open(fp, "rb") + else: + filename = "" + + prefix = fp.read(16) + + preinit() + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + return factory(fp, filename) + except (SyntaxError, IndexError, TypeError): + pass + + if init(): + + for i in ID: + try: + factory, accept = OPEN[i] + if not accept or accept(prefix): + fp.seek(0) + return factory(fp, filename) + except (SyntaxError, IndexError, TypeError): + pass + + raise IOError("cannot identify image file") + +# +# Image processing. + +## +# Creates a new image by interpolating between two input images, using +# a constant alpha. +# +#

+#    out = image1 * (1.0 - alpha) + image2 * alpha
+# 
+# +# @param im1 The first image. +# @param im2 The second image. Must have the same mode and size as +# the first image. +# @param alpha The interpolation alpha factor. If alpha is 0.0, a +# copy of the first image is returned. If alpha is 1.0, a copy of +# the second image is returned. There are no restrictions on the +# alpha value. If necessary, the result is clipped to fit into +# the allowed output range. +# @return An Image object. + +def blend(im1, im2, alpha): + "Interpolate between images." + + im1.load() + im2.load() + return im1._new(core.blend(im1.im, im2.im, alpha)) + +## +# Creates a new image by interpolating between two input images, +# using the mask as alpha. +# +# @param image1 The first image. +# @param image2 The second image. Must have the same mode and +# size as the first image. +# @param mask A mask image. This image can can have mode +# "1", "L", or "RGBA", and must have the same size as the +# other two images. + +def composite(image1, image2, mask): + "Create composite image by blending images using a transparency mask" + + image = image2.copy() + image.paste(image1, None, mask) + return image + +## +# Applies the function (which should take one argument) to each pixel +# in the given image. If the image has more than one band, the same +# function is applied to each band. Note that the function is +# evaluated once for each possible pixel value, so you cannot use +# random components or other generators. +# +# @def eval(image, function) +# @param image The input image. +# @param function A function object, taking one integer argument. +# @return An Image object. + +def eval(image, *args): + "Evaluate image expression" + + return image.point(args[0]) + +## +# Creates a new image from a number of single-band images. +# +# @param mode The mode to use for the output image. +# @param bands A sequence containing one single-band image for +# each band in the output image. All bands must have the +# same size. +# @return An Image object. + +def merge(mode, bands): + "Merge a set of single band images into a new multiband image." + + if getmodebands(mode) != len(bands) or "*" in mode: + raise ValueError("wrong number of bands") + for im in bands[1:]: + if im.mode != getmodetype(mode): + raise ValueError("mode mismatch") + if im.size != bands[0].size: + raise ValueError("size mismatch") + im = core.new(mode, bands[0].size) + for i in range(getmodebands(mode)): + bands[i].load() + im.putband(bands[i].im, i) + return bands[0]._new(im) + +# -------------------------------------------------------------------- +# Plugin registry + +## +# Register an image file plugin. This function should not be used +# in application code. +# +# @param id An image format identifier. +# @param factory An image file factory method. +# @param accept An optional function that can be used to quickly +# reject images having another format. + +def register_open(id, factory, accept=None): + id = string.upper(id) + ID.append(id) + OPEN[id] = factory, accept + +## +# Registers an image MIME type. This function should not be used +# in application code. +# +# @param id An image format identifier. +# @param mimetype The image MIME type for this format. + +def register_mime(id, mimetype): + MIME[string.upper(id)] = mimetype + +## +# Registers an image save function. This function should not be +# used in application code. +# +# @param id An image format identifier. +# @param driver A function to save images in this format. + +def register_save(id, driver): + SAVE[string.upper(id)] = driver + +## +# Registers an image extension. This function should not be +# used in application code. +# +# @param id An image format identifier. +# @param extension An extension used for this format. + +def register_extension(id, extension): + EXTENSION[string.lower(extension)] = string.upper(id) + + +# -------------------------------------------------------------------- +# Simple display support. User code may override this. + +def _show(image, **options): + # override me, as necessary + apply(_showxv, (image,), options) + +def _showxv(image, title=None, **options): + import ImageShow + apply(ImageShow.show, (image, title), options) diff --git a/PIL/ImageChops.py b/PIL/ImageChops.py new file mode 100644 index 000000000..82861fc7a --- /dev/null +++ b/PIL/ImageChops.py @@ -0,0 +1,302 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard channel operations +# +# History: +# 1996-03-24 fl Created +# 1996-08-13 fl Added logical operations (for "1" images) +# 2000-10-12 fl Added offset method (from Image.py) +# +# Copyright (c) 1997-2000 by Secret Labs AB +# Copyright (c) 1996-2000 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image + +## +# The ImageChops module contains a number of arithmetical image +# operations, called channel operations ("chops"). These can be +# used for various purposes, including special effects, image +# compositions, algorithmic painting, and more. +#

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

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

+# Compares the two images, pixel by pixel, and returns a new image +# containing the lighter values. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def lighter(image1, image2): + "Select the lighter pixels from each image" + + image1.load() + image2.load() + return image1._new(image1.im.chop_lighter(image2.im)) + +## +# Compare images, and return darker pixel value +# (min(image1, image2)). +#

+# Compares the two images, pixel by pixel, and returns a new image +# containing the darker values. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def darker(image1, image2): + "Select the darker pixels from each image" + + image1.load() + image2.load() + return image1._new(image1.im.chop_darker(image2.im)) + +## +# Calculate absolute difference +# (abs(image1 - image2)). +#

+# Returns the absolute value of the difference between the two images. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def difference(image1, image2): + "Subtract one image from another" + + image1.load() + image2.load() + return image1._new(image1.im.chop_difference(image2.im)) + +## +# Superimpose positive images +# (image1 * image2 / MAX). +#

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

+# Superimposes two inverted images on top of each other. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def screen(image1, image2): + "Superimpose two negative images" + + image1.load() + image2.load() + return image1._new(image1.im.chop_screen(image2.im)) + +## +# Add images +# ((image1 + image2) / scale + offset). +#

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

+# Subtracts two images, dividing the result by scale and adding the +# offset. If omitted, scale defaults to 1.0, and offset to 0.0. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def subtract(image1, image2, scale=1.0, offset=0): + "Subtract two images" + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) + +## +# Add images without clipping +# ((image1 + image2) % MAX). +#

+# Adds two images, without clipping the result. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def add_modulo(image1, image2): + "Add two images without clipping" + + image1.load() + image2.load() + return image1._new(image1.im.chop_add_modulo(image2.im)) + +## +# Subtract images without clipping +# ((image1 - image2) % MAX). +#

+# Subtracts two images, without clipping the result. +# +# @param image1 First image. +# @param image1 Second image. +# @return An image object. + +def subtract_modulo(image1, image2): + "Subtract two images without clipping" + + image1.load() + image2.load() + return image1._new(image1.im.chop_subtract_modulo(image2.im)) + +## +# Logical AND +# (image1 and image2). + +def logical_and(image1, image2): + "Logical and between two images" + + image1.load() + image2.load() + return image1._new(image1.im.chop_and(image2.im)) + +## +# Logical OR +# (image1 or image2). + +def logical_or(image1, image2): + "Logical or between two images" + + image1.load() + image2.load() + return image1._new(image1.im.chop_or(image2.im)) + +## +# Logical XOR +# (image1 xor image2). + +def logical_xor(image1, image2): + "Logical xor between two images" + + image1.load() + image2.load() + return image1._new(image1.im.chop_xor(image2.im)) + +## +# Blend images using constant transparency weight. +#

+# Same as the blend function in the Image module. + +def blend(image1, image2, alpha): + "Blend two images using a constant transparency weight" + + return Image.blend(image1, image2, alpha) + +## +# Create composite using transparency mask. +#

+# Same as the composite function in the Image module. + +def composite(image1, image2, mask): + "Create composite image by blending images using a transparency mask" + + return Image.composite(image1, image2, mask) + +## +# Offset image data. +#

+# Returns a copy of the image where data has been offset by the given +# distances. Data wraps around the edges. If yoffset is omitted, it +# is assumed to be equal to xoffset. +# +# @param image Source image. +# @param xoffset The horizontal distance. +# @param yoffset The vertical distance. If omitted, both +# distances are set to the same value. +# @return An Image object. + +def offset(image, xoffset, yoffset=None): + "Offset image in horizontal and/or vertical direction" + if yoffset is None: + yoffset = xoffset + image.load() + return image._new(image.im.offset(xoffset, yoffset)) diff --git a/PIL/ImageCms.py b/PIL/ImageCms.py new file mode 100644 index 000000000..b8a6dca71 --- /dev/null +++ b/PIL/ImageCms.py @@ -0,0 +1,786 @@ +# +# The Python Imaging Library. +# $Id$ +# +# optional color managment support, based on Kevin Cazabon's PyCMS +# library. +# +# History: +# 2009-03-08 fl Added to PIL. +# +# Copyright (C) 2002-2003 Kevin Cazabon +# Copyright (c) 2009 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. See +# below for the original description. +# + +DESCRIPTION = """ +pyCMS + + a Python / PIL interface to the littleCMS ICC Color Management System + Copyright (C) 2002-2003 Kevin Cazabon + kevin@cazabon.com + http://www.cazabon.com + + pyCMS home page: http://www.cazabon.com/pyCMS + littleCMS home page: http://www.littlecms.com + (littleCMS is Copyright (C) 1998-2001 Marti Maria) + + Originally released under LGPL. Graciously donated to PIL in + March 2009, for distribution under the standard PIL license + + The pyCMS.py module provides a "clean" interface between Python/PIL and + pyCMSdll, taking care of some of the more complex handling of the direct + pyCMSdll functions, as well as error-checking and making sure that all + relevant data is kept together. + + While it is possible to call pyCMSdll functions directly, it's not highly + recommended. + + Version History: + + 0.1.0 pil mod March 10, 2009 + + Renamed display profile to proof profile. The proof + profile is the profile of the device that is being + simulated, not the profile of the device which is + actually used to display/print the final simulation + (that'd be the output profile) - also see LCMSAPI.txt + input colorspace -> using 'renderingIntent' -> proof + colorspace -> using 'proofRenderingIntent' -> output + colorspace + + Added LCMS FLAGS support. + Added FLAGS["SOFTPROOFING"] as default flag for + buildProofTransform (otherwise the proof profile/intent + would be ignored). + + 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms + + 0.0.2 alpha Jan 6, 2002 + + Added try/except statements arount type() checks of + potential CObjects... Python won't let you use type() + on them, and raises a TypeError (stupid, if you ask me!) + + Added buildProofTransformFromOpenProfiles() function. + Additional fixes in DLL, see DLL code for details. + + 0.0.1 alpha first public release, Dec. 26, 2002 + + Known to-do list with current version (of Python interface, not pyCMSdll): + + none + +""" + +VERSION = "0.1.0 pil" + +# --------------------------------------------------------------------. + +import Image +import _imagingcms + +core = _imagingcms + +# +# intent/direction values + +INTENT_PERCEPTUAL = 0 +INTENT_RELATIVE_COLORIMETRIC = 1 +INTENT_SATURATION = 2 +INTENT_ABSOLUTE_COLORIMETRIC = 3 + +DIRECTION_INPUT = 0 +DIRECTION_OUTPUT = 1 +DIRECTION_PROOF = 2 + +# +# flags + +FLAGS = { + "MATRIXINPUT": 1, + "MATRIXOUTPUT": 2, + "MATRIXONLY": (1|2), + "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot + "NOPRELINEARIZATION": 16, # Don't create prelinearization tables on precalculated transforms (internal use) + "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink) + "NOTCACHE": 64, # Inhibit 1-pixel cache + "NOTPRECALC": 256, + "NULLTRANSFORM": 512, # Don't transform anyway + "HIGHRESPRECALC": 1024, # Use more memory to give better accurancy + "LOWRESPRECALC": 2048, # Use less memory to minimize resouces + "WHITEBLACKCOMPENSATION": 8192, + "BLACKPOINTCOMPENSATION": 8192, + "GAMUTCHECK": 4096, # Out of Gamut alarm + "SOFTPROOFING": 16384, # Do softproofing + "PRESERVEBLACK": 32768, # Black preservation + "NODEFAULTRESOURCEDEF": 16777216, # CRD special + "GRIDPOINTS": lambda n: ((n) & 0xFF) << 16 # Gridpoints +} + +_MAX_FLAG = 0 +for flag in FLAGS.values(): + if isinstance(flag, type(0)): + _MAX_FLAG = _MAX_FLAG | flag + +# --------------------------------------------------------------------. +# Experimental PIL-level API +# --------------------------------------------------------------------. + +## +# Profile. + +class ImageCmsProfile: + + def __init__(self, profile): + # accepts a string (filename), a file-like object, or a low-level + # profile object + if Image.isStringType(profile): + self._set(core.profile_open(profile), profile) + elif hasattr(profile, "read"): + self._set(core.profile_fromstring(profile.read())) + else: + self._set(profile) # assume it's already a profile + + def _set(self, profile, filename=None): + self.profile = profile + self.filename = filename + if profile: + self.product_name = profile.product_name + self.product_info = profile.product_info + else: + self.product_name = None + self.product_info = None + +## +# Transform. This can be used with the procedural API, or with the +# standard {@link Image.point} method. + +class ImageCmsTransform(Image.ImagePointHandler): + + def __init__(self, input, output, input_mode, output_mode, + intent=INTENT_PERCEPTUAL, + proof=None, proof_intent=INTENT_ABSOLUTE_COLORIMETRIC, flags=0): + if proof is None: + self.transform = core.buildTransform( + input.profile, output.profile, + input_mode, output_mode, + intent, + flags + ) + else: + self.transform = core.buildProofTransform( + input.profile, output.profile, proof.profile, + input_mode, output_mode, + intent, proof_intent, + flags + ) + # Note: inputMode and outputMode are for pyCMS compatibility only + self.input_mode = self.inputMode = input_mode + self.output_mode = self.outputMode = output_mode + + def point(self, im): + return self.apply(im) + + def apply(self, im, imOut=None): + im.load() + if imOut is None: + imOut = Image.new(self.output_mode, im.size, None) + result = self.transform.apply(im.im.id, imOut.im.id) + return imOut + + def apply_in_place(self, im): + im.load() + if im.mode != self.output_mode: + raise ValueError("mode mismatch") # wrong output mode + result = self.transform.apply(im.im.id, im.im.id) + return im + +## +# (experimental) Fetches the profile for the current display device. +# Returns None if the profile is not known. + +def get_display_profile(handle=None): + import sys + if sys.platform == "win32": + import ImageWin + if isinstance(handle, ImageWin.HDC): + profile = core.get_display_profile_win32(handle, 1) + else: + profile = core.get_display_profile_win32(handle or 0) + else: + try: + get = _imagingcms.get_display_profile + except AttributeError: + return None + else: + profile = get() + return ImageCmsProfile(profile) + +# --------------------------------------------------------------------. +# pyCMS compatible layer +# --------------------------------------------------------------------. + +## +# (pyCMS) Exception class. This is used for all errors in the pyCMS API. + +class PyCMSError(Exception): + pass + +## +# (pyCMS) Applies an ICC transformation to a given image, mapping from +# inputProfile to outputProfile. + +def profileToProfile(im, inputProfile, outputProfile, renderingIntent=INTENT_PERCEPTUAL, outputMode=None, inPlace=0, flags=0): + """ + ImageCms.profileToProfile(im, inputProfile, outputProfile, + [renderingIntent], [outputMode], [inPlace]) + + Returns either None or a new PIL image object, depending on value of + inPlace (see below). + + im = an open PIL image object (i.e. Image.new(...) or + Image.open(...), etc.) + inputProfile = string, as a valid filename path to the ICC input + profile you wish to use for this image, or a profile object + outputProfile = string, as a valid filename path to the ICC output + profile you wish to use for this image, or a profile object + renderingIntent = integer (0-3) specifying the rendering intent you + wish to use for the transform + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + + see the pyCMS documentation for details on rendering intents and + what they do. + outputMode = a valid PIL mode for the output image (i.e. "RGB", "CMYK", + etc.). Note: if rendering the image "inPlace", outputMode MUST be + the same mode as the input, or omitted completely. If omitted, the + outputMode will be the same as the mode of the input image (im.mode) + inPlace = BOOL (1 = TRUE, None or 0 = FALSE). If TRUE, the original + image is modified in-place, and None is returned. If FALSE + (default), a new Image object is returned with the transform + applied. + flags = integer (0-...) specifying additional flags + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If inPlace == TRUE and outputMode != im.mode, + a PyCMSError will be raised. If an error occurs during application of + the profiles, a PyCMSError will be raised. If outputMode is not a mode + supported by the outputProfile (or by pyCMS), a PyCMSError will be + raised. + + This function applies an ICC transformation to im from inputProfile's + color space to outputProfile's color space using the specified rendering + intent to decide how to handle out-of-gamut colors. + + OutputMode can be used to specify that a color mode conversion is to + be done using these profiles, but the specified profiles must be able + to handle that mode. I.e., if converting im from RGB to CMYK using + profiles, the input profile must handle RGB data, and the output + profile must handle CMYK data. + + """ + + if outputMode is None: + outputMode = im.mode + + if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + transform = ImageCmsTransform( + inputProfile, outputProfile, im.mode, outputMode, renderingIntent, flags=flags + ) + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (IOError, TypeError, ValueError), v: + raise PyCMSError(v) + + return imOut + +## +# (pyCMS) Opens an ICC profile file. + +def getOpenProfile(profileFilename): + """ + ImageCms.getOpenProfile(profileFilename) + + Returns a CmsProfile class object. + + profileFilename = string, as a valid filename path to the ICC profile + you wish to open, or a file-like object. + + The PyCMSProfile object can be passed back into pyCMS for use in creating + transforms and such (as in ImageCms.buildTransformFromOpenProfiles()). + + If profileFilename is not a vaild filename for an ICC profile, a + PyCMSError will be raised. + + """ + + try: + return ImageCmsProfile(profileFilename) + except (IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Builds an ICC transform mapping from the inputProfile to the +# outputProfile. Use applyTransform to apply the transform to a given +# image. + +def buildTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, flags=0): + """ + ImageCms.buildTransform(inputProfile, outputProfile, inMode, outMode, + [renderingIntent]) + + Returns a CmsTransform class object. + + inputProfile = string, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + outputProfile = string, as a valid filename path to the ICC output + profile you wish to use for this transform, or a profile object + inMode = string, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + outMode = string, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + renderingIntent = integer (0-3) specifying the rendering intent you + wish to use for the transform + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + see the pyCMS documentation for details on rendering intents and + what they do. + flags = integer (0-...) specifying additional flags + + If the input or output profiles specified are not valid filenames, a + PyCMSError will be raised. If an error occurs during creation of the + transform, a PyCMSError will be raised. + + If inMode or outMode are not a mode supported by the outputProfile (or + by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile using the renderingIntent to determine what to do + with out-of-gamut colors. It will ONLY work for converting images that + are in inMode to images that are in outMode color format (PIL mode, + i.e. "RGB", "RGBA", "CMYK", etc.). + + Building the transform is a fair part of the overhead in + ImageCms.profileToProfile(), so if you're planning on converting multiple + images using the same input/output settings, this can save you time. + Once you have a transform object, it can be used with + ImageCms.applyProfile() to convert images without the need to re-compute + the lookup table for the transform. + + The reason pyCMS returns a class object rather than a handle directly + to the transform is that it needs to keep track of the PIL input/output + modes that the transform is meant for. These attributes are stored in + the "inMode" and "outMode" attributes of the object (which can be + manually overridden if you really want to, but I don't know of any + time that would be of use, or would even work). + + """ + + if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags) + except (IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Builds an ICC transform mapping from the inputProfile to the +# outputProfile, but tries to simulate the result that would be +# obtained on the proofProfile device. + +def buildProofTransform(inputProfile, outputProfile, proofProfile, inMode, outMode, renderingIntent=INTENT_PERCEPTUAL, proofRenderingIntent=INTENT_ABSOLUTE_COLORIMETRIC, flags=FLAGS["SOFTPROOFING"]): + """ + ImageCms.buildProofTransform(inputProfile, outputProfile, proofProfile, + inMode, outMode, [renderingIntent], [proofRenderingIntent]) + + Returns a CmsTransform class object. + + inputProfile = string, as a valid filename path to the ICC input + profile you wish to use for this transform, or a profile object + outputProfile = string, as a valid filename path to the ICC output + (monitor, usually) profile you wish to use for this transform, + or a profile object + proofProfile = string, as a valid filename path to the ICC proof + profile you wish to use for this transform, or a profile object + inMode = string, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + outMode = string, as a valid PIL mode that the appropriate profile also + supports (i.e. "RGB", "RGBA", "CMYK", etc.) + renderingIntent = integer (0-3) specifying the rendering intent you + wish to use for the input->proof (simulated) transform + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + see the pyCMS documentation for details on rendering intents and + what they do. + proofRenderingIntent = integer (0-3) specifying the rendering intent + you wish to use for proof->output transform + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + see the pyCMS documentation for details on rendering intents and + what they do. + flags = integer (0-...) specifying additional flags + + If the input, output, or proof profiles specified are not valid + filenames, a PyCMSError will be raised. + + If an error occurs during creation of the transform, a PyCMSError will + be raised. + + If inMode or outMode are not a mode supported by the outputProfile + (or by pyCMS), a PyCMSError will be raised. + + This function builds and returns an ICC transform from the inputProfile + to the outputProfile, but tries to simulate the result that would be + obtained on the proofProfile device using renderingIntent and + proofRenderingIntent to determine what to do with out-of-gamut + colors. This is known as "soft-proofing". It will ONLY work for + converting images that are in inMode to images that are in outMode + color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.). + + Usage of the resulting transform object is exactly the same as with + ImageCms.buildTransform(). + + Proof profiling is generally used when using an output device to get a + good idea of what the final printed/displayed image would look like on + the proofProfile device when it's quicker and easier to use the + output device for judging color. Generally, this means that the + output device is a monitor, or a dye-sub printer (etc.), and the simulated + device is something more expensive, complicated, or time consuming + (making it difficult to make a real print for color judgement purposes). + + Soft-proofing basically functions by adjusting the colors on the + output device to match the colors of the device being simulated. However, + when the simulated device has a much wider gamut than the output + device, you may obtain marginal results. + + """ + + if type(renderingIntent) != type(1) or not (0 <= renderingIntent <=3): + raise PyCMSError("renderingIntent must be an integer between 0 and 3") + + if type(flags) != type(1) or not (0 <= flags <= _MAX_FLAG): + raise PyCMSError("flags must be an integer between 0 and %s" + _MAX_FLAG) + + try: + if not isinstance(inputProfile, ImageCmsProfile): + inputProfile = ImageCmsProfile(inputProfile) + if not isinstance(outputProfile, ImageCmsProfile): + outputProfile = ImageCmsProfile(outputProfile) + if not isinstance(proofProfile, ImageCmsProfile): + proofProfile = ImageCmsProfile(proofProfile) + return ImageCmsTransform(inputProfile, outputProfile, inMode, outMode, renderingIntent, proofProfile, proofRenderingIntent, flags) + except (IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +buildTransformFromOpenProfiles = buildTransform +buildProofTransformFromOpenProfiles = buildProofTransform + +## +# (pyCMS) Applies a transform to a given image. + +def applyTransform(im, transform, inPlace=0): + """ + ImageCms.applyTransform(im, transform, [inPlace]) + + Returns either None, or a new PIL Image object, depending on the value + of inPlace (see below) + + im = a PIL Image object, and im.mode must be the same as the inMode + supported by the transform. + transform = a valid CmsTransform class object + inPlace = BOOL (1 == TRUE, 0 or None == FALSE). If TRUE, im is + modified in place and None is returned, if FALSE, a new Image + object with the transform applied is returned (and im is not + changed). The default is FALSE. + + If im.mode != transform.inMode, a PyCMSError is raised. + + If inPlace == TRUE and transform.inMode != transform.outMode, a + PyCMSError is raised. + + If im.mode, transfer.inMode, or transfer.outMode is not supported by + pyCMSdll or the profiles you used for the transform, a PyCMSError is + raised. + + If an error occurs while the transform is being applied, a PyCMSError + is raised. + + This function applies a pre-calculated transform (from + ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles()) to an + image. The transform can be used for multiple images, saving + considerable calcuation time if doing the same conversion multiple times. + + If you want to modify im in-place instead of receiving a new image as + the return value, set inPlace to TRUE. This can only be done if + transform.inMode and transform.outMode are the same, because we can't + change the mode in-place (the buffer sizes for some modes are + different). The default behavior is to return a new Image object of + the same dimensions in mode transform.outMode. + + """ + + try: + if inPlace: + transform.apply_in_place(im) + imOut = None + else: + imOut = transform.apply(im) + except (TypeError, ValueError), v: + raise PyCMSError(v) + + return imOut + +## +# (pyCMS) Creates a profile. + +def createProfile(colorSpace, colorTemp=-1): + """ + ImageCms.createProfile(colorSpace, [colorTemp]) + + Returns a CmsProfile class object + + colorSpace = string, the color space of the profile you wish to create. + Currently only "LAB", "XYZ", and "sRGB" are supported. + colorTemp = positive integer for the white point for the profile, in + degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for + D50 illuminant if omitted (5000k). colorTemp is ONLY applied to + LAB profiles, and is ignored for XYZ and sRGB. + + If colorSpace not in ["LAB", "XYZ", "sRGB"], a PyCMSError is raised + + If using LAB and colorTemp != a positive integer, a PyCMSError is raised. + + If an error occurs while creating the profile, a PyCMSError is raised. + + Use this function to create common profiles on-the-fly instead of + having to supply a profile on disk and knowing the path to it. It + returns a normal CmsProfile object that can be passed to + ImageCms.buildTransformFromOpenProfiles() to create a transform to apply + to images. + + """ + if colorSpace not in ["LAB", "XYZ", "sRGB"]: + raise PyCMSError("Color space not supported for on-the-fly profile creation (%s)" % colorSpace) + + if colorSpace == "LAB": + if type(colorTemp) == type(5000.0): + colorTemp = int(colorTemp + 0.5) + if type (colorTemp) != type (5000): + raise PyCMSError("Color temperature must be a positive integer, \"%s\" not valid" % colorTemp) + + try: + return core.createProfile(colorSpace, colorTemp) + except (TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the internal product name for the given profile. + +def getProfileName(profile): + """ + ImageCms.getProfileName(profile) + + Returns a string containing the internal name of the profile as stored + in an ICC tag. + + profile = EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised If an error occurs while trying to obtain the + name tag, a PyCMSError is raised. + + Use this function to obtain the INTERNAL name of the profile (stored + in an ICC tag in the profile itself), usually the one used when the + profile was originally created. Sometimes this tag also contains + additional information supplied by the creator. + + """ + try: + # add an extra newline to preserve pyCMS compatibility + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.product_name + "\n" + except (AttributeError, IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the internal product information for the given profile. + +def getProfileInfo(profile): + """ + ImageCms.getProfileInfo(profile) + + Returns a string containing the internal profile information stored in + an ICC tag. + + profile = EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the info tag, a PyCMSError + is raised + + Use this function to obtain the information stored in the profile's + info tag. This often contains details about the profile, and how it + was created, as supplied by the creator. + + """ + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # add an extra newline to preserve pyCMS compatibility + return profile.product_info + "\n" + except (AttributeError, IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Gets the default intent name for the given profile. + +def getDefaultIntent(profile): + """ + ImageCms.getDefaultIntent(profile) + + Returns integer 0-3 specifying the default rendering intent for this + profile. + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + see the pyCMS documentation for details on rendering intents and + what they do. + + profile = EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + + If profile isn't a valid CmsProfile object or filename to a profile, + a PyCMSError is raised. + + If an error occurs while trying to obtain the default intent, a + PyCMSError is raised. + + Use this function to determine the default (and usually best optomized) + rendering intent for this profile. Most profiles support multiple + rendering intents, but are intended mostly for one type of conversion. + If you wish to use a different intent than returned, use + ImageCms.isIntentSupported() to verify it will work first. + """ + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + return profile.profile.rendering_intent + except (AttributeError, IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Checks if a given intent is supported. + +def isIntentSupported(profile, intent, direction): + """ + ImageCms.isIntentSupported(profile, intent, direction) + + Returns 1 if the intent/direction are supported, -1 if they are not. + + profile = EITHER a valid CmsProfile object, OR a string of the + filename of an ICC profile. + intent = integer (0-3) specifying the rendering intent you wish to use + with this profile + INTENT_PERCEPTUAL = 0 (DEFAULT) (ImageCms.INTENT_PERCEPTUAL) + INTENT_RELATIVE_COLORIMETRIC =1 (ImageCms.INTENT_RELATIVE_COLORIMETRIC) + INTENT_SATURATION = 2 (ImageCms.INTENT_SATURATION) + INTENT_ABSOLUTE_COLORIMETRIC =3 (ImageCms.INTENT_ABSOLUTE_COLORIMETRIC) + see the pyCMS documentation for details on rendering intents and + what they do. + direction = integer specifing if the profile is to be used for input, + output, or proof + INPUT = 0 (or use ImageCms.DIRECTION_INPUT) + OUTPUT = 1 (or use ImageCms.DIRECTION_OUTPUT) + PROOF = 2 (or use ImageCms.DIRECTION_PROOF) + + Use this function to verify that you can use your desired + renderingIntent with profile, and that profile can be used for the + input/output/proof profile as you desire. + + Some profiles are created specifically for one "direction", can cannot + be used for others. Some profiles can only be used for certain + rendering intents... so it's best to either verify this before trying + to create a transform with them (using this function), or catch the + potential PyCMSError that will occur if they don't support the modes + you select. + + """ + try: + if not isinstance(profile, ImageCmsProfile): + profile = ImageCmsProfile(profile) + # FIXME: I get different results for the same data w. different + # compilers. Bug in LittleCMS or in the binding? + if profile.profile.is_intent_supported(intent, direction): + return 1 + else: + return -1 + except (AttributeError, IOError, TypeError, ValueError), v: + raise PyCMSError(v) + +## +# (pyCMS) Fetches versions. + +def versions(): + import sys + return ( + VERSION, core.littlecms_version, sys.version.split()[0], Image.VERSION + ) + +# -------------------------------------------------------------------- + +if __name__ == "__main__": + # create a cheap manual from the __doc__ strings for the functions above + + import ImageCms + import string + print __doc__ + + for f in dir(pyCMS): + print "="*80 + print "%s" %f + + try: + exec ("doc = ImageCms.%s.__doc__" %(f)) + if string.find(doc, "pyCMS") >= 0: + # so we don't get the __doc__ string for imported modules + print doc + except AttributeError: + pass diff --git a/PIL/ImageColor.py b/PIL/ImageColor.py new file mode 100644 index 000000000..c3cca46db --- /dev/null +++ b/PIL/ImageColor.py @@ -0,0 +1,263 @@ +# +# The Python Imaging Library +# $Id$ +# +# map CSS3-style colour description strings to RGB +# +# History: +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-15 fl Added RGBA support +# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 +# 2004-07-19 fl Fixed gray/grey spelling issues +# 2009-03-05 fl Fixed rounding error in grayscale calculation +# +# Copyright (c) 2002-2004 by Secret Labs AB +# Copyright (c) 2002-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image +import re, string + +try: + x = int("a", 16) +except TypeError: + # python 1.5.2 doesn't support int(x,b) + str2int = string.atoi +else: + str2int = int + +## +# Convert color string to RGB tuple. +# +# @param color A CSS3-style colour string. +# @return An RGB-tuple. +# @exception ValueError If the color string could not be interpreted +# as an RGB value. + +def getrgb(color): + # FIXME: add RGBA support + try: + rgb = colormap[color] + except KeyError: + try: + # fall back on case-insensitive lookup + rgb = colormap[string.lower(color)] + except KeyError: + rgb = None + # found color in cache + if rgb: + if isinstance(rgb, type(())): + return rgb + colormap[color] = rgb = getrgb(rgb) + return rgb + # check for known string formats + m = re.match("#\w\w\w$", color) + if m: + return ( + str2int(color[1]*2, 16), + str2int(color[2]*2, 16), + str2int(color[3]*2, 16) + ) + m = re.match("#\w\w\w\w\w\w$", color) + if m: + return ( + str2int(color[1:3], 16), + str2int(color[3:5], 16), + str2int(color[5:7], 16) + ) + m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) + if m: + return ( + str2int(m.group(1)), + str2int(m.group(2)), + str2int(m.group(3)) + ) + m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + return ( + int((str2int(m.group(1)) * 255) / 100.0 + 0.5), + int((str2int(m.group(2)) * 255) / 100.0 + 0.5), + int((str2int(m.group(3)) * 255) / 100.0 + 0.5) + ) + m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) + if m: + from colorsys import hls_to_rgb + rgb = hls_to_rgb( + float(m.group(1)) / 360.0, + float(m.group(3)) / 100.0, + float(m.group(2)) / 100.0, + ) + return ( + int(rgb[0] * 255 + 0.5), + int(rgb[1] * 255 + 0.5), + int(rgb[2] * 255 + 0.5) + ) + raise ValueError("unknown color specifier: %r" % color) + +def getcolor(color, mode): + # same as getrgb, but converts the result to the given mode + color = getrgb(color) + if mode == "RGB": + return color + if mode == "RGBA": + r, g, b = color + return r, g, b, 255 + if Image.getmodebase(mode) == "L": + r, g, b = color + return (r*299 + g*587 + b*114)/1000 + return color + +colormap = { + # X11 colour table (from "CSS3 module: Color working draft"), with + # gray/grey spelling issues fixed. This is a superset of HTML 4.0 + # colour names used in CSS 1. + "aliceblue": "#f0f8ff", + "antiquewhite": "#faebd7", + "aqua": "#00ffff", + "aquamarine": "#7fffd4", + "azure": "#f0ffff", + "beige": "#f5f5dc", + "bisque": "#ffe4c4", + "black": "#000000", + "blanchedalmond": "#ffebcd", + "blue": "#0000ff", + "blueviolet": "#8a2be2", + "brown": "#a52a2a", + "burlywood": "#deb887", + "cadetblue": "#5f9ea0", + "chartreuse": "#7fff00", + "chocolate": "#d2691e", + "coral": "#ff7f50", + "cornflowerblue": "#6495ed", + "cornsilk": "#fff8dc", + "crimson": "#dc143c", + "cyan": "#00ffff", + "darkblue": "#00008b", + "darkcyan": "#008b8b", + "darkgoldenrod": "#b8860b", + "darkgray": "#a9a9a9", + "darkgrey": "#a9a9a9", + "darkgreen": "#006400", + "darkkhaki": "#bdb76b", + "darkmagenta": "#8b008b", + "darkolivegreen": "#556b2f", + "darkorange": "#ff8c00", + "darkorchid": "#9932cc", + "darkred": "#8b0000", + "darksalmon": "#e9967a", + "darkseagreen": "#8fbc8f", + "darkslateblue": "#483d8b", + "darkslategray": "#2f4f4f", + "darkslategrey": "#2f4f4f", + "darkturquoise": "#00ced1", + "darkviolet": "#9400d3", + "deeppink": "#ff1493", + "deepskyblue": "#00bfff", + "dimgray": "#696969", + "dimgrey": "#696969", + "dodgerblue": "#1e90ff", + "firebrick": "#b22222", + "floralwhite": "#fffaf0", + "forestgreen": "#228b22", + "fuchsia": "#ff00ff", + "gainsboro": "#dcdcdc", + "ghostwhite": "#f8f8ff", + "gold": "#ffd700", + "goldenrod": "#daa520", + "gray": "#808080", + "grey": "#808080", + "green": "#008000", + "greenyellow": "#adff2f", + "honeydew": "#f0fff0", + "hotpink": "#ff69b4", + "indianred": "#cd5c5c", + "indigo": "#4b0082", + "ivory": "#fffff0", + "khaki": "#f0e68c", + "lavender": "#e6e6fa", + "lavenderblush": "#fff0f5", + "lawngreen": "#7cfc00", + "lemonchiffon": "#fffacd", + "lightblue": "#add8e6", + "lightcoral": "#f08080", + "lightcyan": "#e0ffff", + "lightgoldenrodyellow": "#fafad2", + "lightgreen": "#90ee90", + "lightgray": "#d3d3d3", + "lightgrey": "#d3d3d3", + "lightpink": "#ffb6c1", + "lightsalmon": "#ffa07a", + "lightseagreen": "#20b2aa", + "lightskyblue": "#87cefa", + "lightslategray": "#778899", + "lightslategrey": "#778899", + "lightsteelblue": "#b0c4de", + "lightyellow": "#ffffe0", + "lime": "#00ff00", + "limegreen": "#32cd32", + "linen": "#faf0e6", + "magenta": "#ff00ff", + "maroon": "#800000", + "mediumaquamarine": "#66cdaa", + "mediumblue": "#0000cd", + "mediumorchid": "#ba55d3", + "mediumpurple": "#9370db", + "mediumseagreen": "#3cb371", + "mediumslateblue": "#7b68ee", + "mediumspringgreen": "#00fa9a", + "mediumturquoise": "#48d1cc", + "mediumvioletred": "#c71585", + "midnightblue": "#191970", + "mintcream": "#f5fffa", + "mistyrose": "#ffe4e1", + "moccasin": "#ffe4b5", + "navajowhite": "#ffdead", + "navy": "#000080", + "oldlace": "#fdf5e6", + "olive": "#808000", + "olivedrab": "#6b8e23", + "orange": "#ffa500", + "orangered": "#ff4500", + "orchid": "#da70d6", + "palegoldenrod": "#eee8aa", + "palegreen": "#98fb98", + "paleturquoise": "#afeeee", + "palevioletred": "#db7093", + "papayawhip": "#ffefd5", + "peachpuff": "#ffdab9", + "peru": "#cd853f", + "pink": "#ffc0cb", + "plum": "#dda0dd", + "powderblue": "#b0e0e6", + "purple": "#800080", + "red": "#ff0000", + "rosybrown": "#bc8f8f", + "royalblue": "#4169e1", + "saddlebrown": "#8b4513", + "salmon": "#fa8072", + "sandybrown": "#f4a460", + "seagreen": "#2e8b57", + "seashell": "#fff5ee", + "sienna": "#a0522d", + "silver": "#c0c0c0", + "skyblue": "#87ceeb", + "slateblue": "#6a5acd", + "slategray": "#708090", + "slategrey": "#708090", + "snow": "#fffafa", + "springgreen": "#00ff7f", + "steelblue": "#4682b4", + "tan": "#d2b48c", + "teal": "#008080", + "thistle": "#d8bfd8", + "tomato": "#ff6347", + "turquoise": "#40e0d0", + "violet": "#ee82ee", + "wheat": "#f5deb3", + "white": "#ffffff", + "whitesmoke": "#f5f5f5", + "yellow": "#ffff00", + "yellowgreen": "#9acd32", +} diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py new file mode 100644 index 000000000..5217a7366 --- /dev/null +++ b/PIL/ImageDraw.py @@ -0,0 +1,378 @@ +# +# The Python Imaging Library +# $Id$ +# +# drawing interface operations +# +# History: +# 1996-04-13 fl Created (experimental) +# 1996-08-07 fl Filled polygons, ellipses. +# 1996-08-13 fl Added text support +# 1998-06-28 fl Handle I and F images +# 1998-12-29 fl Added arc; use arc primitive to draw ellipses +# 1999-01-10 fl Added shape stuff (experimental) +# 1999-02-06 fl Added bitmap support +# 1999-02-11 fl Changed all primitives to take options +# 1999-02-20 fl Fixed backwards compatibility +# 2000-10-12 fl Copy on write, when necessary +# 2001-02-18 fl Use default ink for bitmap/text also in fill mode +# 2002-10-24 fl Added support for CSS-style color strings +# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing +# 2002-12-11 fl Refactored low-level drawing API (work in progress) +# 2004-08-26 fl Made Draw() a factory function, added getdraw() support +# 2004-09-04 fl Added width support to line primitive +# 2004-09-10 fl Added font mode handling +# 2006-06-19 fl Added font bearing support (getmask2) +# +# Copyright (c) 1997-2006 by Secret Labs AB +# Copyright (c) 1996-2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageColor + +try: + import warnings +except ImportError: + warnings = None + +## +# A simple 2D drawing interface for PIL images. +#

+# Application code should use the Draw factory, instead of +# directly. + +class ImageDraw: + + ## + # Create a drawing instance. + # + # @param im The image to draw in. + # @param mode Optional mode to use for color values. For RGB + # images, this argument can be RGB or RGBA (to blend the + # drawing into the image). For all other modes, this argument + # must be the same as the image mode. If omitted, the mode + # defaults to the mode of the image. + + def __init__(self, im, mode=None): + im.load() + if im.readonly: + im._copy() # make it writable + blend = 0 + if mode is None: + mode = im.mode + if mode != im.mode: + if mode == "RGBA" and im.mode == "RGB": + blend = 1 + else: + raise ValueError("mode mismatch") + if mode == "P": + self.palette = im.palette + else: + self.palette = None + self.im = im.im + self.draw = Image.core.draw(self.im, blend) + self.mode = mode + if mode in ("I", "F"): + self.ink = self.draw.draw_ink(1, mode) + else: + self.ink = self.draw.draw_ink(-1, mode) + if mode in ("1", "P", "I", "F"): + # FIXME: fix Fill2 to properly support matte for I+F images + self.fontmode = "1" + else: + self.fontmode = "L" # aliasing is okay for other modes + self.fill = 0 + self.font = None + + ## + # Set the default pen color. + + def setink(self, ink): + # compatibility + if warnings: + warnings.warn( + "'setink' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + if Image.isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not Image.isNumberType(ink): + ink = self.palette.getcolor(ink) + self.ink = self.draw.draw_ink(ink, self.mode) + + ## + # Set the default background color. + + def setfill(self, onoff): + # compatibility + if warnings: + warnings.warn( + "'setfill' is deprecated; use keyword arguments instead", + DeprecationWarning, stacklevel=2 + ) + self.fill = onoff + + ## + # Set the default font. + + def setfont(self, font): + # compatibility + self.font = font + + ## + # Get the current default font. + + def getfont(self): + if not self.font: + # FIXME: should add a font repository + import ImageFont + self.font = ImageFont.load_default() + return self.font + + def _getink(self, ink, fill=None): + if ink is None and fill is None: + if self.fill: + fill = self.ink + else: + ink = self.ink + else: + if ink is not None: + if Image.isStringType(ink): + ink = ImageColor.getcolor(ink, self.mode) + if self.palette and not Image.isNumberType(ink): + ink = self.palette.getcolor(ink) + ink = self.draw.draw_ink(ink, self.mode) + if fill is not None: + if Image.isStringType(fill): + fill = ImageColor.getcolor(fill, self.mode) + if self.palette and not Image.isNumberType(fill): + fill = self.palette.getcolor(fill) + fill = self.draw.draw_ink(fill, self.mode) + return ink, fill + + ## + # Draw an arc. + + def arc(self, xy, start, end, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_arc(xy, start, end, ink) + + ## + # Draw a bitmap. + + def bitmap(self, xy, bitmap, fill=None): + bitmap.load() + ink, fill = self._getink(fill) + if ink is None: + ink = fill + if ink is not None: + self.draw.draw_bitmap(xy, bitmap.im, ink) + + ## + # Draw a chord. + + def chord(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_chord(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_chord(xy, start, end, ink, 0) + + ## + # Draw an ellipse. + + def ellipse(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_ellipse(xy, fill, 1) + if ink is not None: + self.draw.draw_ellipse(xy, ink, 0) + + ## + # Draw a line, or a connected sequence of line segments. + + def line(self, xy, fill=None, width=0): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_lines(xy, ink, width) + + ## + # (Experimental) Draw a shape. + + def shape(self, shape, fill=None, outline=None): + # experimental + shape.close() + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_outline(shape, fill, 1) + if ink is not None: + self.draw.draw_outline(shape, ink, 0) + + ## + # Draw a pieslice. + + def pieslice(self, xy, start, end, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_pieslice(xy, start, end, fill, 1) + if ink is not None: + self.draw.draw_pieslice(xy, start, end, ink, 0) + + ## + # Draw one or more individual pixels. + + def point(self, xy, fill=None): + ink, fill = self._getink(fill) + if ink is not None: + self.draw.draw_points(xy, ink) + + ## + # Draw a polygon. + + def polygon(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_polygon(xy, fill, 1) + if ink is not None: + self.draw.draw_polygon(xy, ink, 0) + + ## + # Draw a rectangle. + + def rectangle(self, xy, fill=None, outline=None): + ink, fill = self._getink(outline, fill) + if fill is not None: + self.draw.draw_rectangle(xy, fill, 1) + if ink is not None: + self.draw.draw_rectangle(xy, ink, 0) + + ## + # Draw text. + + def text(self, xy, text, fill=None, font=None, anchor=None): + ink, fill = self._getink(fill) + if font is None: + font = self.getfont() + if ink is None: + ink = fill + if ink is not None: + try: + mask, offset = font.getmask2(text, self.fontmode) + xy = xy[0] + offset[0], xy[1] + offset[1] + except AttributeError: + try: + mask = font.getmask(text, self.fontmode) + except TypeError: + mask = font.getmask(text) + self.draw.draw_bitmap(xy, mask, ink) + + ## + # Get the size of a given string, in pixels. + + def textsize(self, text, font=None): + if font is None: + font = self.getfont() + return font.getsize(text) + +## +# A simple 2D drawing interface for PIL images. +# +# @param im The image to draw in. +# @param mode Optional mode to use for color values. For RGB +# images, this argument can be RGB or RGBA (to blend the +# drawing into the image). For all other modes, this argument +# must be the same as the image mode. If omitted, the mode +# defaults to the mode of the image. + +def Draw(im, mode=None): + try: + return im.getdraw(mode) + except AttributeError: + return ImageDraw(im, mode) + +# experimental access to the outline API +try: + Outline = Image.core.outline +except: + Outline = None + +## +# (Experimental) A more advanced 2D drawing interface for PIL images, +# based on the WCK interface. +# +# @param im The image to draw in. +# @param hints An optional list of hints. +# @return A (drawing context, drawing resource factory) tuple. + +def getdraw(im=None, hints=None): + # FIXME: this needs more work! + # FIXME: come up with a better 'hints' scheme. + handler = None + if not hints or "nicest" in hints: + try: + import _imagingagg + handler = _imagingagg + except ImportError: + pass + if handler is None: + import ImageDraw2 + handler = ImageDraw2 + if im: + im = handler.Draw(im) + return im, handler + +## +# (experimental) Fills a bounded region with a given color. +# +# @param image Target image. +# @param xy Seed position (a 2-item coordinate tuple). +# @param value Fill color. +# @param border Optional border value. If given, the region consists of +# pixels with a color different from the border color. If not given, +# the region consists of pixels having the same color as the seed +# pixel. + +def floodfill(image, xy, value, border=None): + "Fill bounded region." + # based on an implementation by Eric S. Raymond + pixel = image.load() + x, y = xy + try: + background = pixel[x, y] + if background == value: + return # seed point already has fill color + pixel[x, y] = value + except IndexError: + return # seed point outside image + edge = [(x, y)] + if border is None: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p == background: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge + else: + while edge: + newedge = [] + for (x, y) in edge: + for (s, t) in ((x+1, y), (x-1, y), (x, y+1), (x, y-1)): + try: + p = pixel[s, t] + except IndexError: + pass + else: + if p != value and p != border: + pixel[s, t] = value + newedge.append((s, t)) + edge = newedge diff --git a/PIL/ImageDraw2.py b/PIL/ImageDraw2.py new file mode 100644 index 000000000..dbf1a1f88 --- /dev/null +++ b/PIL/ImageDraw2.py @@ -0,0 +1,105 @@ +# +# The Python Imaging Library +# $Id$ +# +# WCK-style drawing interface operations +# +# History: +# 2003-12-07 fl created +# 2005-05-15 fl updated; added to PIL as ImageDraw2 +# 2005-05-15 fl added text support +# 2005-05-20 fl added arc/chord/pieslice support +# +# Copyright (c) 2003-2005 by Secret Labs AB +# Copyright (c) 2003-2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageColor, ImageDraw, ImageFont, ImagePath + +class Pen: + def __init__(self, color, width=1, opacity=255): + self.color = ImageColor.getrgb(color) + self.width = width + +class Brush: + def __init__(self, color, opacity=255): + self.color = ImageColor.getrgb(color) + +class Font: + def __init__(self, color, file, size=12): + # FIXME: add support for bitmap fonts + self.color = ImageColor.getrgb(color) + self.font = ImageFont.truetype(file, size) + +class Draw: + + def __init__(self, image, size=None, color=None): + if not hasattr(image, "im"): + image = Image.new(image, size, color) + self.draw = ImageDraw.Draw(image) + self.image = image + self.transform = None + + def flush(self): + return self.image + + def render(self, op, xy, pen, brush=None): + # handle color arguments + outline = fill = None; width = 1 + if isinstance(pen, Pen): + outline = pen.color + width = pen.width + elif isinstance(brush, Pen): + outline = brush.color + width = brush.width + if isinstance(brush, Brush): + fill = brush.color + elif isinstance(pen, Brush): + fill = pen.color + # handle transformation + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + # render the item + if op == "line": + self.draw.line(xy, fill=outline, width=width) + else: + getattr(self.draw, op)(xy, fill=fill, outline=outline) + + def settransform(self, (xoffset, yoffset)): + self.transform = (1, 0, xoffset, 0, 1, yoffset) + + def arc(self, xy, start, end, *options): + self.render("arc", xy, start, end, *options) + + def chord(self, xy, start, end, *options): + self.render("chord", xy, start, end, *options) + + def ellipse(self, xy, *options): + self.render("ellipse", xy, *options) + + def line(self, xy, *options): + self.render("line", xy, *options) + + def pieslice(self, xy, start, end, *options): + self.render("pieslice", xy, start, end, *options) + + def polygon(self, xy, *options): + self.render("polygon", xy, *options) + + def rectangle(self, xy, *options): + self.render("rectangle", xy, *options) + + def symbol(self, xy, symbol, *options): + raise NotImplementedError("not in this version") + + def text(self, xy, text, font): + if self.transform: + xy = ImagePath.Path(xy) + xy.transform(self.transform) + self.draw.text(xy, text, font=font.font, fill=font.color) + + def textsize(self, text, font): + return self.draw.textsize(text, font=font.font) diff --git a/PIL/ImageEnhance.py b/PIL/ImageEnhance.py new file mode 100644 index 000000000..86a19e650 --- /dev/null +++ b/PIL/ImageEnhance.py @@ -0,0 +1,90 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image enhancement classes +# +# For a background, see "Image Processing By Interpolation and +# Extrapolation", Paul Haeberli and Douglas Voorhies. Available +# at http://www.sgi.com/grafica/interp/index.html +# +# History: +# 1996-03-23 fl Created +# 2009-06-16 fl Fixed mean calculation +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +import Image, ImageFilter, ImageStat + +class _Enhance: + + ## + # Returns an enhanced image. The enhancement factor is a floating + # point value controlling the enhancement. Factor 1.0 always + # returns a copy of the original image, lower factors mean less + # colour (brightness, contrast, etc), and higher values more. + # There are no restrictions on this value. + # + # @param factor Enhancement factor. + # @return An enhanced image. + + def enhance(self, factor): + return Image.blend(self.degenerate, self.image, factor) + +## +# Color enhancement object. +#

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

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

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

+# This class can be used to adjust the sharpness of an image. The +# enhancement factor 0.0 gives a blurred image, 1.0 gives the original +# image, and a factor of 2.0 gives a sharpened image. + +class Sharpness(_Enhance): + "Adjust image sharpness" + def __init__(self, image): + self.image = image + self.degenerate = image.filter(ImageFilter.SMOOTH) diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py new file mode 100644 index 000000000..8a97c1b5b --- /dev/null +++ b/PIL/ImageFile.py @@ -0,0 +1,528 @@ +# +# The Python Imaging Library. +# $Id$ +# +# base class for image file handlers +# +# history: +# 1995-09-09 fl Created +# 1996-03-11 fl Fixed load mechanism. +# 1996-04-15 fl Added pcx/xbm decoders. +# 1996-04-30 fl Added encoders. +# 1996-12-14 fl Added load helpers +# 1997-01-11 fl Use encode_to_file where possible +# 1997-08-27 fl Flush output in _save +# 1998-03-05 fl Use memory mapping for some modes +# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" +# 1999-05-31 fl Added image parser +# 2000-10-12 fl Set readonly flag on memory-mapped images +# 2002-03-20 fl Use better messages for common decoder errors +# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available +# 2003-10-30 fl Added StubImageFile class +# 2004-02-25 fl Made incremental parser more robust +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1995-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image +import traceback, string, os + +MAXBLOCK = 65536 + +SAFEBLOCK = 1024*1024 + +ERRORS = { + -1: "image buffer overrun error", + -2: "decoding error", + -3: "unknown error", + -8: "bad configuration", + -9: "out of memory error" +} + +def raise_ioerror(error): + try: + message = Image.core.getcodecstatus(error) + except AttributeError: + message = ERRORS.get(error) + if not message: + message = "decoder error %d" % error + raise IOError(message + " when reading image file") + +# +# -------------------------------------------------------------------- +# Helpers + +def _tilesort(t1, t2): + # sort on offset + return cmp(t1[2], t2[2]) + +# +# -------------------------------------------------------------------- +# ImageFile base class + +## +# Base class for image file handlers. + +class ImageFile(Image.Image): + "Base class for image file format handlers." + + def __init__(self, fp=None, filename=None): + Image.Image.__init__(self) + + self.tile = None + self.readonly = 1 # until we know better + + self.decoderconfig = () + self.decodermaxblock = MAXBLOCK + + if Image.isStringType(fp): + # filename + self.fp = open(fp, "rb") + self.filename = fp + else: + # stream + self.fp = fp + self.filename = filename + + try: + self._open() + except IndexError, v: # end of data + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError, v + except TypeError, v: # end of data (ord) + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError, v + except KeyError, v: # unsupported mode + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError, v + except EOFError, v: # got header but not the first frame + if Image.DEBUG > 1: + traceback.print_exc() + raise SyntaxError, v + + if not self.mode or self.size[0] <= 0: + raise SyntaxError, "not identified by this driver" + + def draft(self, mode, size): + "Set draft mode" + + pass + + def verify(self): + "Check file integrity" + + # raise exception if something's wrong. must be called + # directly after open, and closes file when finished. + self.fp = None + + def load(self): + "Load image data based on tile list" + + pixel = Image.Image.load(self) + + if self.tile is None: + raise IOError("cannot load this image") + if not self.tile: + return pixel + + self.map = None + + readonly = 0 + + if self.filename and len(self.tile) == 1: + # try memory mapping + d, e, o, a = self.tile[0] + if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES: + try: + if hasattr(Image.core, "map"): + # use built-in mapper + self.map = Image.core.map(self.filename) + self.map.seek(o) + self.im = self.map.readimage( + self.mode, self.size, a[1], a[2] + ) + else: + # use mmap, if possible + import mmap + file = open(self.filename, "r+") + size = os.path.getsize(self.filename) + # FIXME: on Unix, use PROT_READ etc + self.map = mmap.mmap(file.fileno(), size) + self.im = Image.core.map_buffer( + self.map, self.size, d, e, o, a + ) + readonly = 1 + except (AttributeError, EnvironmentError, ImportError): + self.map = None + + self.load_prepare() + + # look for read/seek overrides + try: + read = self.load_read + except AttributeError: + read = self.fp.read + + try: + seek = self.load_seek + except AttributeError: + seek = self.fp.seek + + if not self.map: + + # sort tiles in file order + self.tile.sort(_tilesort) + + try: + # FIXME: This is a hack to handle TIFF's JpegTables tag. + prefix = self.tile_prefix + except AttributeError: + prefix = "" + + for d, e, o, a in self.tile: + d = Image._getdecoder(self.mode, d, a, self.decoderconfig) + seek(o) + try: + d.setimage(self.im, e) + except ValueError: + continue + b = prefix + t = len(b) + while 1: + s = read(self.decodermaxblock) + if not s: + self.tile = [] + raise IOError("image file is truncated (%d bytes not processed)" % len(b)) + b = b + s + n, e = d.decode(b) + if n < 0: + break + b = b[n:] + t = t + n + + self.tile = [] + self.readonly = readonly + + self.fp = None # might be shared + + if not self.map and e < 0: + raise_ioerror(e) + + # post processing + if hasattr(self, "tile_post_rotate"): + # FIXME: This is a hack to handle rotated PCD's + self.im = self.im.rotate(self.tile_post_rotate) + self.size = self.im.size + + self.load_end() + + return Image.Image.load(self) + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.new(self.mode, self.size) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + + def load_end(self): + # may be overridden + pass + + # may be defined for contained formats + # def load_seek(self, pos): + # pass + + # may be defined for blocked formats (e.g. PNG) + # def load_read(self, bytes): + # pass + +## +# Base class for stub image loaders. +#

+# A stub loader is an image loader that can identify files of a +# certain format, but relies on external code to load the file. + +class StubImageFile(ImageFile): + "Base class for stub image loaders." + + def _open(self): + raise NotImplementedError( + "StubImageFile subclass must implement _open" + ) + + def load(self): + loader = self._load() + if loader is None: + raise IOError("cannot find loader for this %s file" % self.format) + image = loader.load(self) + assert image is not None + # become the other object (!) + self.__class__ = image.__class__ + self.__dict__ = image.__dict__ + + ## + # (Hook) Find actual image loader. + + def _load(self): + raise NotImplementedError( + "StubImageFile subclass must implement _load" + ) + +## +# (Internal) Support class for the Parser file. + +class _ParserFile: + # parser support class. + + def __init__(self, data): + self.data = data + self.offset = 0 + + def close(self): + self.data = self.offset = None + + def tell(self): + return self.offset + + def seek(self, offset, whence=0): + if whence == 0: + self.offset = offset + elif whence == 1: + self.offset = self.offset + offset + else: + # force error in Image.open + raise IOError("illegal argument to seek") + + def read(self, bytes=0): + pos = self.offset + if bytes: + data = self.data[pos:pos+bytes] + else: + data = self.data[pos:] + self.offset = pos + len(data) + return data + + def readline(self): + # FIXME: this is slow! + s = "" + while 1: + c = self.read(1) + if not c: + break + s = s + c + if c == "\n": + break + return s + +## +# Incremental image parser. This class implements the standard +# feed/close consumer interface. + +class Parser: + + incremental = None + image = None + data = None + decoder = None + finished = 0 + + ## + # (Consumer) Reset the parser. Note that you can only call this + # method immediately after you've created a parser; parser + # instances cannot be reused. + + def reset(self): + assert self.data is None, "cannot reuse parsers" + + ## + # (Consumer) Feed data to the parser. + # + # @param data A string buffer. + # @exception IOError If the parser failed to parse the image file. + + def feed(self, data): + # collect data + + if self.finished: + return + + if self.data is None: + self.data = data + else: + self.data = self.data + data + + # parse what we have + if self.decoder: + + if self.offset > 0: + # skip header + skip = min(len(self.data), self.offset) + self.data = self.data[skip:] + self.offset = self.offset - skip + if self.offset > 0 or not self.data: + return + + n, e = self.decoder.decode(self.data) + + if n < 0: + # end of stream + self.data = None + self.finished = 1 + if e < 0: + # decoding error + self.image = None + raise_ioerror(e) + else: + # end of image + return + self.data = self.data[n:] + + elif self.image: + + # if we end up here with no decoder, this file cannot + # be incrementally parsed. wait until we've gotten all + # available data + pass + + else: + + # attempt to open this file + try: + try: + fp = _ParserFile(self.data) + im = Image.open(fp) + finally: + fp.close() # explicitly close the virtual file + except IOError: + pass # not enough data + else: + flag = hasattr(im, "load_seek") or hasattr(im, "load_read") + if flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # initialize decoder + im.load_prepare() + d, e, o, a = im.tile[0] + im.tile = [] + self.decoder = Image._getdecoder( + im.mode, d, a, im.decoderconfig + ) + self.decoder.setimage(im.im, e) + + # calculate decoder offset + self.offset = o + if self.offset <= len(self.data): + self.data = self.data[self.offset:] + self.offset = 0 + + self.image = im + + ## + # (Consumer) Close the stream. + # + # @return An image object. + # @exception IOError If the parser failed to parse the image file. + + def close(self): + # finish decoding + if self.decoder: + # get rid of what's left in the buffers + self.feed("") + self.data = self.decoder = None + if not self.finished: + raise IOError("image was incomplete") + if not self.image: + raise IOError("cannot parse this image") + if self.data: + # incremental parsing not possible; reopen the file + # not that we have all data + try: + fp = _ParserFile(self.data) + self.image = Image.open(fp) + finally: + self.image.load() + fp.close() # explicitly close the virtual file + return self.image + +# -------------------------------------------------------------------- + +## +# (Helper) Save image body to file. +# +# @param im Image object. +# @param fp File object. +# @param tile Tile list. + +def _save(im, fp, tile): + "Helper to save image based on tile list" + + im.load() + if not hasattr(im, "encoderconfig"): + im.encoderconfig = () + tile.sort(_tilesort) + # FIXME: make MAXBLOCK a configuration parameter + bufsize = max(MAXBLOCK, im.size[0] * 4) # see RawEncode.c + try: + fh = fp.fileno() + fp.flush() + except AttributeError: + # compress to Python file-compatible object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + while 1: + l, s, d = e.encode(bufsize) + fp.write(d) + if s: + break + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + else: + # slight speedup: compress to real file object + for e, b, o, a in tile: + e = Image._getencoder(im.mode, e, a, im.encoderconfig) + if o > 0: + fp.seek(o, 0) + e.setimage(im.im, b) + s = e.encode_to_file(fh, bufsize) + if s < 0: + raise IOError("encoder error %d when writing image file" % s) + try: + fp.flush() + except: pass + + +## +# Reads large blocks in a safe way. Unlike fp.read(n), this function +# doesn't trust the user. If the requested size is larger than +# SAFEBLOCK, the file is read block by block. +# +# @param fp File handle. Must implement a read method. +# @param size Number of bytes to read. +# @return A string containing up to size bytes of data. + +def _safe_read(fp, size): + if size <= 0: + return "" + if size <= SAFEBLOCK: + return fp.read(size) + data = [] + while size > 0: + block = fp.read(min(size, SAFEBLOCK)) + if not block: + break + data.append(block) + size = size - len(block) + return string.join(data, "") diff --git a/PIL/ImageFileIO.py b/PIL/ImageFileIO.py new file mode 100644 index 000000000..c12a25738 --- /dev/null +++ b/PIL/ImageFileIO.py @@ -0,0 +1,39 @@ +# +# The Python Imaging Library. +# $Id$ +# +# kludge to get basic ImageFileIO functionality +# +# History: +# 1998-08-06 fl Recreated +# +# Copyright (c) Secret Labs AB 1998-2002. +# +# See the README file for information on usage and redistribution. +# + +from StringIO import StringIO + +## +# The ImageFileIO module can be used to read an image from a +# socket, or any other stream device. +#

+# This module is deprecated. New code should use the Parser +# class in the ImageFile module instead. +# +# @see ImageFile#Parser + +class ImageFileIO(StringIO): + + ## + # Adds buffering to a stream file object, in order to + # provide seek and tell methods required + # by the Image.open method. The stream object must + # implement read and close methods. + # + # @param fp Stream file handle. + # @see Image#open + + def __init__(self, fp): + data = fp.read() + StringIO.__init__(self, data) diff --git a/PIL/ImageFilter.py b/PIL/ImageFilter.py new file mode 100644 index 000000000..918bab177 --- /dev/null +++ b/PIL/ImageFilter.py @@ -0,0 +1,289 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard filters +# +# History: +# 1995-11-27 fl Created +# 2002-06-08 fl Added rank and mode filters +# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2002 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +class Filter: + pass + +## +# Convolution filter kernel. + +class Kernel(Filter): + + ## + # Create a convolution kernel. The current version only + # supports 3x3 and 5x5 integer and floating point kernels. + #

+ # In the current version, kernels can only be applied to + # "L" and "RGB" images. + # + # @def __init__(size, kernel, **options) + # @param size Kernel size, given as (width, height). In + # the current version, this must be (3,3) or (5,5). + # @param kernel A sequence containing kernel weights. + # @param **options Optional keyword arguments. + # @keyparam scale Scale factor. If given, the result for each + # pixel is divided by this value. The default is the sum + # of the kernel weights. + # @keyparam offset Offset. If given, this value is added to the + # result, after it has been divided by the scale factor. + + def __init__(self, size, kernel, scale=None, offset=0): + if scale is None: + # default scale is sum of kernel + scale = reduce(lambda a,b: a+b, kernel) + if size[0] * size[1] != len(kernel): + raise ValueError("not enough coefficients in kernel") + self.filterargs = size, scale, offset, kernel + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + return apply(image.filter, self.filterargs) + +class BuiltinFilter(Kernel): + def __init__(self): + pass + +## +# Rank filter. + +class RankFilter(Filter): + name = "Rank" + + ## + # Create a rank filter. The rank filter sorts all pixels in + # a window of the given size, and returns the rank'th value. + # + # @param size The kernel size, in pixels. + # @param rank What pixel value to pick. Use 0 for a min filter, + # size*size/2 for a median filter, size*size-1 for a max filter, + # etc. + + def __init__(self, size, rank): + self.size = size + self.rank = rank + + def filter(self, image): + if image.mode == "P": + raise ValueError("cannot filter palette images") + image = image.expand(self.size/2, self.size/2) + return image.rankfilter(self.size, self.rank) + +## +# Median filter. Picks the median pixel value in a window with the +# given size. + +class MedianFilter(RankFilter): + name = "Median" + + ## + # Create a median filter. + # + # @param size The kernel size, in pixels. + + def __init__(self, size=3): + self.size = size + self.rank = size*size/2 + +## +# Min filter. Picks the lowest pixel value in a window with the given +# size. + +class MinFilter(RankFilter): + name = "Min" + + ## + # Create a min filter. + # + # @param size The kernel size, in pixels. + + def __init__(self, size=3): + self.size = size + self.rank = 0 + +## +# Max filter. Picks the largest pixel value in a window with the +# given size. + +class MaxFilter(RankFilter): + name = "Max" + + ## + # Create a max filter. + # + # @param size The kernel size, in pixels. + + def __init__(self, size=3): + self.size = size + self.rank = size*size-1 + +## +# Mode filter. Picks the most frequent pixel value in a box with the +# given size. Pixel values that occur only once or twice are ignored; +# if no pixel value occurs more than twice, the original pixel value +# is preserved. + +class ModeFilter(Filter): + name = "Mode" + + ## + # Create a mode filter. + # + # @param size The kernel size, in pixels. + + def __init__(self, size=3): + self.size = size + def filter(self, image): + return image.modefilter(self.size) + +## +# Gaussian blur filter. + +class GaussianBlur(Filter): + name = "GaussianBlur" + + def __init__(self, radius=2): + self.radius = 2 + def filter(self, image): + return image.gaussian_blur(self.radius) + +## +# Unsharp mask filter. + +class UnsharpMask(Filter): + name = "UnsharpMask" + + def __init__(self, radius=2, percent=150, threshold=3): + self.radius = 2 + self.percent = percent + self.threshold = threshold + def filter(self, image): + return image.unsharp_mask(self.radius, self.percent, self.threshold) + +## +# Simple blur filter. + +class BLUR(BuiltinFilter): + name = "Blur" + filterargs = (5, 5), 16, 0, ( + 1, 1, 1, 1, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, + 1, 1, 1, 1, 1 + ) + +## +# Simple contour filter. + +class CONTOUR(BuiltinFilter): + name = "Contour" + filterargs = (3, 3), 1, 255, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + +## +# Simple detail filter. + +class DETAIL(BuiltinFilter): + name = "Detail" + filterargs = (3, 3), 6, 0, ( + 0, -1, 0, + -1, 10, -1, + 0, -1, 0 + ) + +## +# Simple edge enhancement filter. + +class EDGE_ENHANCE(BuiltinFilter): + name = "Edge-enhance" + filterargs = (3, 3), 2, 0, ( + -1, -1, -1, + -1, 10, -1, + -1, -1, -1 + ) + +## +# Simple stronger edge enhancement filter. + +class EDGE_ENHANCE_MORE(BuiltinFilter): + name = "Edge-enhance More" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 9, -1, + -1, -1, -1 + ) + +## +# Simple embossing filter. + +class EMBOSS(BuiltinFilter): + name = "Emboss" + filterargs = (3, 3), 1, 128, ( + -1, 0, 0, + 0, 1, 0, + 0, 0, 0 + ) + +## +# Simple edge-finding filter. + +class FIND_EDGES(BuiltinFilter): + name = "Find Edges" + filterargs = (3, 3), 1, 0, ( + -1, -1, -1, + -1, 8, -1, + -1, -1, -1 + ) + +## +# Simple smoothing filter. + +class SMOOTH(BuiltinFilter): + name = "Smooth" + filterargs = (3, 3), 13, 0, ( + 1, 1, 1, + 1, 5, 1, + 1, 1, 1 + ) + +## +# Simple stronger smoothing filter. + +class SMOOTH_MORE(BuiltinFilter): + name = "Smooth More" + filterargs = (5, 5), 100, 0, ( + 1, 1, 1, 1, 1, + 1, 5, 5, 5, 1, + 1, 5, 44, 5, 1, + 1, 5, 5, 5, 1, + 1, 1, 1, 1, 1 + ) + +## +# Simple sharpening filter. + +class SHARPEN(BuiltinFilter): + name = "Sharpen" + filterargs = (3, 3), 16, 0, ( + -2, -2, -2, + -2, 32, -2, + -2, -2, -2 + ) diff --git a/PIL/ImageFont.py b/PIL/ImageFont.py new file mode 100644 index 000000000..3ea2f2f8e --- /dev/null +++ b/PIL/ImageFont.py @@ -0,0 +1,390 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIL raster font management +# +# History: +# 1996-08-07 fl created (experimental) +# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 +# 1999-02-06 fl rewrote most font management stuff in C +# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) +# 2001-02-17 fl added freetype support +# 2001-05-09 fl added TransposedFont wrapper class +# 2002-03-04 fl make sure we have a "L" or "1" font +# 2002-12-04 fl skip non-directory entries in the system path +# 2003-04-29 fl add embedded default font +# 2003-09-27 fl added support for truetype charmap encodings +# +# Todo: +# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) +# +# Copyright (c) 1997-2003 by Secret Labs AB +# Copyright (c) 1996-2003 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image +import os, string, sys + +class _imagingft_not_installed: + # module placeholder + def __getattr__(self, id): + raise ImportError("The _imagingft C module is not installed") + +try: + import _imagingft + core = _imagingft + del _imagingft +except ImportError: + core = _imagingft_not_installed() + +# FIXME: add support for pilfont2 format (see FontFile.py) + +# -------------------------------------------------------------------- +# Font metrics format: +# "PILfont" LF +# fontdescriptor LF +# (optional) key=value... LF +# "DATA" LF +# binary data: 256*10*2 bytes (dx, dy, dstbox, srcbox) +# +# To place a character, cut out srcbox and paste at dstbox, +# relative to the character position. Then move the character +# position according to dx, dy. +# -------------------------------------------------------------------- + +## +# The ImageFont module defines a class with the same name. +# Instances of this class store bitmap fonts, and are used with the +# text method of the ImageDraw class. +#

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

+# Starting with version 1.1.4, PIL can be configured to support +# TrueType and OpenType fonts. For earlier version, TrueType +# support is only available as part of the imToolkit package +# +# @see ImageDraw#ImageDraw.text +# @see pilfont + +class ImageFont: + "PIL font wrapper" + + def _load_pilfont(self, filename): + + file = open(filename, "rb") + + for ext in (".png", ".gif", ".pbm"): + try: + fullname = os.path.splitext(filename)[0] + ext + image = Image.open(fullname) + except: + pass + else: + if image and image.mode in ("1", "L"): + break + else: + raise IOError("cannot find glyph data file") + + self.file = fullname + + return self._load_pilfont_data(file, image) + + def _load_pilfont_data(self, file, image): + + # read PILfont header + if file.readline() != "PILfont\n": + raise SyntaxError("Not a PILfont file") + d = string.split(file.readline(), ";") + self.info = [] # FIXME: should be a dictionary + while True: + s = file.readline() + if not s or s == "DATA\n": + break + self.info.append(s) + + # read PILfont metrics + data = file.read(256*20) + + # check image + if image.mode not in ("1", "L"): + raise TypeError("invalid font image mode") + + image.load() + + self.font = Image.core.font(image.im, data) + + # delegate critical operations to internal type + self.getsize = self.font.getsize + self.getmask = self.font.getmask + +## +# Wrapper for FreeType fonts. Application code should use the +# truetype factory function to create font objects. + +class FreeTypeFont: + "FreeType font wrapper (requires _imagingft service)" + + def __init__(self, file, size, index=0, encoding=""): + # FIXME: use service provider instead + self.font = core.getfont(file, size, index, encoding) + + def getname(self): + return self.font.family, self.font.style + + def getmetrics(self): + return self.font.ascent, self.font.descent + + def getsize(self, text): + return self.font.getsize(text)[0] + + def getmask(self, text, mode=""): + return self.getmask2(text, mode)[0] + + def getmask2(self, text, mode="", fill=Image.core.fill): + size, offset = self.font.getsize(text) + im = fill("L", size, 0) + self.font.render(text, im.id, mode=="1") + return im, offset + +## +# Wrapper that creates a transposed font from any existing font +# object. +# +# @param font A font object. +# @param orientation An optional orientation. If given, this should +# be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM, +# Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270. + +class TransposedFont: + "Wrapper for writing rotated or mirrored text" + + def __init__(self, font, orientation=None): + self.font = font + self.orientation = orientation # any 'transpose' argument, or None + + def getsize(self, text): + w, h = self.font.getsize(text) + if self.orientation in (Image.ROTATE_90, Image.ROTATE_270): + return h, w + return w, h + + def getmask(self, text, mode=""): + im = self.font.getmask(text, mode) + if self.orientation is not None: + return im.transpose(self.orientation) + return im + +## +# Load font file. This function loads a font object from the given +# bitmap font file, and returns the corresponding font object. +# +# @param filename Name of font file. +# @return A font object. +# @exception IOError If the file could not be read. + +def load(filename): + "Load a font file." + f = ImageFont() + f._load_pilfont(filename) + return f + +## +# Load a TrueType or OpenType font file, and create a font object. +# This function loads a font object from the given file, and creates +# a font object for a font of the given size. +#

+# This function requires the _imagingft service. +# +# @param filename A truetype font file. Under Windows, if the file +# is not found in this filename, the loader also looks in Windows +# fonts directory +# @param size The requested size, in points. +# @param index Which font face to load (default is first available face). +# @param encoding Which font encoding to use (default is Unicode). Common +# encodings are "unic" (Unicode), "symb" (Microsoft Symbol), "ADOB" +# (Adobe Standard), "ADBE" (Adobe Expert), and "armn" (Apple Roman). +# See the FreeType documentation for more information. +# @return A font object. +# @exception IOError If the file could not be read. + +def truetype(filename, size, index=0, encoding=""): + "Load a truetype font file." + try: + return FreeTypeFont(filename, size, index, encoding) + except IOError: + if sys.platform == "win32": + # check the windows font repository + # NOTE: must use uppercase WINDIR, to work around bugs in + # 1.5.2's os.environ.get() + windir = os.environ.get("WINDIR") + if windir: + filename = os.path.join(windir, "fonts", filename) + return FreeTypeFont(filename, size, index, encoding) + raise + +## +# Load font file. Same as load, but searches for a bitmap font along +# the Python path. +# +# @param filename Name of font file. +# @return A font object. +# @exception IOError If the file could not be read. +# @see #load + +def load_path(filename): + "Load a font file, searching along the Python path." + for dir in sys.path: + if Image.isDirectory(dir): + try: + return load(os.path.join(dir, filename)) + except IOError: + pass + raise IOError("cannot find font file") + +## +# Load a (probably rather ugly) default font. +# +# @return A font object. + +def load_default(): + "Load a default font." + from StringIO import StringIO + import base64 + f = ImageFont() + f._load_pilfont_data( + # courB08 + StringIO(base64.decodestring(''' +UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAA//8AAQAAAAAAAAABAAEA +BgAAAAH/+gADAAAAAQAAAAMABgAGAAAAAf/6AAT//QADAAAABgADAAYAAAAA//kABQABAAYAAAAL +AAgABgAAAAD/+AAFAAEACwAAABAACQAGAAAAAP/5AAUAAAAQAAAAFQAHAAYAAP////oABQAAABUA +AAAbAAYABgAAAAH/+QAE//wAGwAAAB4AAwAGAAAAAf/5AAQAAQAeAAAAIQAIAAYAAAAB//kABAAB +ACEAAAAkAAgABgAAAAD/+QAE//0AJAAAACgABAAGAAAAAP/6AAX//wAoAAAALQAFAAYAAAAB//8A +BAACAC0AAAAwAAMABgAAAAD//AAF//0AMAAAADUAAQAGAAAAAf//AAMAAAA1AAAANwABAAYAAAAB +//kABQABADcAAAA7AAgABgAAAAD/+QAFAAAAOwAAAEAABwAGAAAAAP/5AAYAAABAAAAARgAHAAYA +AAAA//kABQAAAEYAAABLAAcABgAAAAD/+QAFAAAASwAAAFAABwAGAAAAAP/5AAYAAABQAAAAVgAH +AAYAAAAA//kABQAAAFYAAABbAAcABgAAAAD/+QAFAAAAWwAAAGAABwAGAAAAAP/5AAUAAABgAAAA +ZQAHAAYAAAAA//kABQAAAGUAAABqAAcABgAAAAD/+QAFAAAAagAAAG8ABwAGAAAAAf/8AAMAAABv +AAAAcQAEAAYAAAAA//wAAwACAHEAAAB0AAYABgAAAAD/+gAE//8AdAAAAHgABQAGAAAAAP/7AAT/ +/gB4AAAAfAADAAYAAAAB//oABf//AHwAAACAAAUABgAAAAD/+gAFAAAAgAAAAIUABgAGAAAAAP/5 +AAYAAQCFAAAAiwAIAAYAAP////oABgAAAIsAAACSAAYABgAA////+gAFAAAAkgAAAJgABgAGAAAA +AP/6AAUAAACYAAAAnQAGAAYAAP////oABQAAAJ0AAACjAAYABgAA////+gAFAAAAowAAAKkABgAG +AAD////6AAUAAACpAAAArwAGAAYAAAAA//oABQAAAK8AAAC0AAYABgAA////+gAGAAAAtAAAALsA +BgAGAAAAAP/6AAQAAAC7AAAAvwAGAAYAAP////oABQAAAL8AAADFAAYABgAA////+gAGAAAAxQAA +AMwABgAGAAD////6AAUAAADMAAAA0gAGAAYAAP////oABQAAANIAAADYAAYABgAA////+gAGAAAA +2AAAAN8ABgAGAAAAAP/6AAUAAADfAAAA5AAGAAYAAP////oABQAAAOQAAADqAAYABgAAAAD/+gAF +AAEA6gAAAO8ABwAGAAD////6AAYAAADvAAAA9gAGAAYAAAAA//oABQAAAPYAAAD7AAYABgAA//// ++gAFAAAA+wAAAQEABgAGAAD////6AAYAAAEBAAABCAAGAAYAAP////oABgAAAQgAAAEPAAYABgAA +////+gAGAAABDwAAARYABgAGAAAAAP/6AAYAAAEWAAABHAAGAAYAAP////oABgAAARwAAAEjAAYA +BgAAAAD/+gAFAAABIwAAASgABgAGAAAAAf/5AAQAAQEoAAABKwAIAAYAAAAA//kABAABASsAAAEv +AAgABgAAAAH/+QAEAAEBLwAAATIACAAGAAAAAP/5AAX//AEyAAABNwADAAYAAAAAAAEABgACATcA +AAE9AAEABgAAAAH/+QAE//wBPQAAAUAAAwAGAAAAAP/7AAYAAAFAAAABRgAFAAYAAP////kABQAA +AUYAAAFMAAcABgAAAAD/+wAFAAABTAAAAVEABQAGAAAAAP/5AAYAAAFRAAABVwAHAAYAAAAA//sA +BQAAAVcAAAFcAAUABgAAAAD/+QAFAAABXAAAAWEABwAGAAAAAP/7AAYAAgFhAAABZwAHAAYAAP// +//kABQAAAWcAAAFtAAcABgAAAAD/+QAGAAABbQAAAXMABwAGAAAAAP/5AAQAAgFzAAABdwAJAAYA +AP////kABgAAAXcAAAF+AAcABgAAAAD/+QAGAAABfgAAAYQABwAGAAD////7AAUAAAGEAAABigAF +AAYAAP////sABQAAAYoAAAGQAAUABgAAAAD/+wAFAAABkAAAAZUABQAGAAD////7AAUAAgGVAAAB +mwAHAAYAAAAA//sABgACAZsAAAGhAAcABgAAAAD/+wAGAAABoQAAAacABQAGAAAAAP/7AAYAAAGn +AAABrQAFAAYAAAAA//kABgAAAa0AAAGzAAcABgAA////+wAGAAABswAAAboABQAGAAD////7AAUA +AAG6AAABwAAFAAYAAP////sABgAAAcAAAAHHAAUABgAAAAD/+wAGAAABxwAAAc0ABQAGAAD////7 +AAYAAgHNAAAB1AAHAAYAAAAA//sABQAAAdQAAAHZAAUABgAAAAH/+QAFAAEB2QAAAd0ACAAGAAAA +Av/6AAMAAQHdAAAB3gAHAAYAAAAA//kABAABAd4AAAHiAAgABgAAAAD/+wAF//0B4gAAAecAAgAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAB +//sAAwACAecAAAHpAAcABgAAAAD/+QAFAAEB6QAAAe4ACAAGAAAAAP/5AAYAAAHuAAAB9AAHAAYA +AAAA//oABf//AfQAAAH5AAUABgAAAAD/+QAGAAAB+QAAAf8ABwAGAAAAAv/5AAMAAgH/AAACAAAJ +AAYAAAAA//kABQABAgAAAAIFAAgABgAAAAH/+gAE//sCBQAAAggAAQAGAAAAAP/5AAYAAAIIAAAC +DgAHAAYAAAAB//kABf/+Ag4AAAISAAUABgAA////+wAGAAACEgAAAhkABQAGAAAAAP/7AAX//gIZ +AAACHgADAAYAAAAA//wABf/9Ah4AAAIjAAEABgAAAAD/+QAHAAACIwAAAioABwAGAAAAAP/6AAT/ ++wIqAAACLgABAAYAAAAA//kABP/8Ai4AAAIyAAMABgAAAAD/+gAFAAACMgAAAjcABgAGAAAAAf/5 +AAT//QI3AAACOgAEAAYAAAAB//kABP/9AjoAAAI9AAQABgAAAAL/+QAE//sCPQAAAj8AAgAGAAD/ +///7AAYAAgI/AAACRgAHAAYAAAAA//kABgABAkYAAAJMAAgABgAAAAH//AAD//0CTAAAAk4AAQAG +AAAAAf//AAQAAgJOAAACUQADAAYAAAAB//kABP/9AlEAAAJUAAQABgAAAAH/+QAF//4CVAAAAlgA +BQAGAAD////7AAYAAAJYAAACXwAFAAYAAP////kABgAAAl8AAAJmAAcABgAA////+QAGAAACZgAA +Am0ABwAGAAD////5AAYAAAJtAAACdAAHAAYAAAAA//sABQACAnQAAAJ5AAcABgAA////9wAGAAAC +eQAAAoAACQAGAAD////3AAYAAAKAAAAChwAJAAYAAP////cABgAAAocAAAKOAAkABgAA////9wAG +AAACjgAAApUACQAGAAD////4AAYAAAKVAAACnAAIAAYAAP////cABgAAApwAAAKjAAkABgAA//// ++gAGAAACowAAAqoABgAGAAAAAP/6AAUAAgKqAAACrwAIAAYAAP////cABQAAAq8AAAK1AAkABgAA +////9wAFAAACtQAAArsACQAGAAD////3AAUAAAK7AAACwQAJAAYAAP////gABQAAAsEAAALHAAgA +BgAAAAD/9wAEAAACxwAAAssACQAGAAAAAP/3AAQAAALLAAACzwAJAAYAAAAA//cABAAAAs8AAALT +AAkABgAAAAD/+AAEAAAC0wAAAtcACAAGAAD////6AAUAAALXAAAC3QAGAAYAAP////cABgAAAt0A +AALkAAkABgAAAAD/9wAFAAAC5AAAAukACQAGAAAAAP/3AAUAAALpAAAC7gAJAAYAAAAA//cABQAA +Au4AAALzAAkABgAAAAD/9wAFAAAC8wAAAvgACQAGAAAAAP/4AAUAAAL4AAAC/QAIAAYAAAAA//oA +Bf//Av0AAAMCAAUABgAA////+gAGAAADAgAAAwkABgAGAAD////3AAYAAAMJAAADEAAJAAYAAP// +//cABgAAAxAAAAMXAAkABgAA////9wAGAAADFwAAAx4ACQAGAAD////4AAYAAAAAAAoABwASAAYA +AP////cABgAAAAcACgAOABMABgAA////+gAFAAAADgAKABQAEAAGAAD////6AAYAAAAUAAoAGwAQ +AAYAAAAA//gABgAAABsACgAhABIABgAAAAD/+AAGAAAAIQAKACcAEgAGAAAAAP/4AAYAAAAnAAoA +LQASAAYAAAAA//gABgAAAC0ACgAzABIABgAAAAD/+QAGAAAAMwAKADkAEQAGAAAAAP/3AAYAAAA5 +AAoAPwATAAYAAP////sABQAAAD8ACgBFAA8ABgAAAAD/+wAFAAIARQAKAEoAEQAGAAAAAP/4AAUA +AABKAAoATwASAAYAAAAA//gABQAAAE8ACgBUABIABgAAAAD/+AAFAAAAVAAKAFkAEgAGAAAAAP/5 +AAUAAABZAAoAXgARAAYAAAAA//gABgAAAF4ACgBkABIABgAAAAD/+AAGAAAAZAAKAGoAEgAGAAAA +AP/4AAYAAABqAAoAcAASAAYAAAAA//kABgAAAHAACgB2ABEABgAAAAD/+AAFAAAAdgAKAHsAEgAG +AAD////4AAYAAAB7AAoAggASAAYAAAAA//gABQAAAIIACgCHABIABgAAAAD/+AAFAAAAhwAKAIwA +EgAGAAAAAP/4AAUAAACMAAoAkQASAAYAAAAA//gABQAAAJEACgCWABIABgAAAAD/+QAFAAAAlgAK +AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA +pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG +AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA//// ++QAGAAIAzgAKANUAEw== +''')), Image.open(StringIO(base64.decodestring(''' +iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u +Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9 +M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g +LeNZUworuN1cjTPIzrTX6ofHWeo3v336qPzfEwRmBnHTtf95/fglZK5N0PDgfRTslpGBvz7LFc4F +IUXBWQGjQ5MGCx34EDFPwXiY4YbYxavpnhHFrk14CDAAAAD//wBlAJr/AgKqRooH2gAgPeggvUAA +Bu2WfgPoAwzRAABAAAAAAACQgLz/3Uv4Gv+gX7BJgDeeGP6AAAD1NMDzKHD7ANWr3loYbxsAD791 +NAADfcoIDyP44K/jv4Y63/Z+t98Ovt+ub4T48LAAAAD//wBlAJr/AuplMlADJAAAAGuAphWpqhMx +in0A/fRvAYBABPgBwBUgABBQ/sYAyv9g0bCHgOLoGAAAAAAAREAAwI7nr0ArYpow7aX8//9LaP/9 +SjdavWA8ePHeBIKB//81/83ndznOaXx379wAAAD//wBlAJr/AqDxW+D3AABAAbUh/QMnbQag/gAY +AYDAAACgtgD/gOqAAAB5IA/8AAAk+n9w0AAA8AAAmFRJuPo27ciC0cD5oeW4E7KA/wD3ECMAn2tt +y8PgwH8AfAxFzC0JzeAMtratAsC/ffwAAAD//wBlAJr/BGKAyCAA4AAAAvgeYTAwHd1kmQF5chkG +ABoMIHcL5xVpTfQbUqzlAAAErwAQBgAAEOClA5D9il08AEh/tUzdCBsXkbgACED+woQg8Si9VeqY +lODCn7lmF6NhnAEYgAAA/NMIAAAAAAD//2JgjLZgVGBg5Pv/Tvpc8hwGBjYGJADjHDrAwPzAjv/H +/Wf3PzCwtzcwHmBgYGcwbZz8wHaCAQMDOwMDQ8MCBgYOC3W7mp+f0w+wHOYxO3OG+e376hsMZjk3 +AAAAAP//YmCMY2A4wMAIN5e5gQETPD6AZisDAwMDgzSDAAPjByiHcQMDAwMDg1nOze1lByRu5/47 +c4859311AYNZzg0AAAAA//9iYGDBYihOIIMuwIjGL39/fwffA8b//xv/P2BPtzzHwCBjUQAAAAD/ +/yLFBrIBAAAA//9i1HhcwdhizX7u8NZNzyLbvT97bfrMf/QHI8evOwcSqGUJAAAA//9iYBB81iSw +pEE170Qrg5MIYydHqwdDQRMrAwcVrQAAAAD//2J4x7j9AAMDn8Q/BgYLBoaiAwwMjPdvMDBYM1Tv +oJodAAAAAP//Yqo/83+dxePWlxl3npsel9lvLfPcqlE9725C+acfVLMEAAAA//9i+s9gwCoaaGMR +evta/58PTEWzr21hufPjA8N+qlnBwAAAAAD//2JiWLci5v1+HmFXDqcnULE/MxgYGBj+f6CaJQAA +AAD//2Ji2FrkY3iYpYC5qDeGgeEMAwPDvwQBBoYvcTwOVLMEAAAA//9isDBgkP///0EOg9z35v// +Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR +w7IkEbzhVQAAAABJRU5ErkJggg== +''')))) + return f + +if __name__ == "__main__": + # create font data chunk for embedding + import base64, os, sys + font = "../Images/courB08" + print " f._load_pilfont_data(" + print " # %s" % os.path.basename(font) + print " StringIO(base64.decodestring('''" + base64.encode(open(font + ".pil", "rb"), sys.stdout) + print "''')), Image.open(StringIO(base64.decodestring('''" + base64.encode(open(font + ".pbm", "rb"), sys.stdout) + print "'''))))" diff --git a/PIL/ImageGL.py b/PIL/ImageGL.py new file mode 100644 index 000000000..5c58b6ca9 --- /dev/null +++ b/PIL/ImageGL.py @@ -0,0 +1,28 @@ +# +# The Python Imaging Library. +# $Id$ +# +# OpenGL pixmap/texture interface (requires imToolkit OpenGL extensions) +# +# History: +# 2003-09-13 fl Added +# +# Copyright (c) Secret Labs AB 2003. +# +# See the README file for information on usage and redistribution. +# + +## +# OpenGL pixmap/texture interface (requires imToolkit OpenGL +# extensions.) +## + +import _imaginggl + +## +# Texture factory. + +class TextureFactory: + pass # overwritten by the _imaginggl module + +from _imaginggl import * diff --git a/PIL/ImageGrab.py b/PIL/ImageGrab.py new file mode 100644 index 000000000..9bcd804d3 --- /dev/null +++ b/PIL/ImageGrab.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library +# $Id$ +# +# screen grabber (windows only) +# +# History: +# 2001-04-26 fl created +# 2001-09-17 fl use builtin driver, if present +# 2002-11-19 fl added grabclipboard support +# +# Copyright (c) 2001-2002 by Secret Labs AB +# Copyright (c) 2001-2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image + +## +# (New in 1.1.3) The ImageGrab module can be used to copy +# the contents of the screen to a PIL image memory. +#

+# The current version works on Windows only.

+# +# @since 1.1.3 +## + +try: + # built-in driver (1.1.3 and later) + grabber = Image.core.grabscreen +except AttributeError: + # stand-alone driver (pil plus) + import _grabscreen + grabber = _grabscreen.grab + +## +# (New in 1.1.3) Take a snapshot of the screen. The pixels inside the +# bounding box are returned as an "RGB" image. If the bounding box is +# omitted, the entire screen is copied. +# +# @param bbox What region to copy. Default is the entire screen. +# @return An image +# @since 1.1.3 + +def grab(bbox=None): + size, data = grabber() + im = Image.fromstring( + "RGB", size, data, + # RGB, 32-bit line padding, origo in lower left corner + "raw", "BGR", (size[0]*3 + 3) & -4, -1 + ) + if bbox: + im = im.crop(bbox) + return im + +## +# (New in 1.1.4) Take a snapshot of the clipboard image, if any. +# +# @return An image, a list of filenames, or None if the clipboard does +# not contain image data or filenames. Note that if a list is +# returned, the filenames may not represent image files. +# @since 1.1.4 + +def grabclipboard(): + debug = 0 # temporary interface + data = Image.core.grabclipboard(debug) + if Image.isStringType(data): + import BmpImagePlugin, StringIO + return BmpImagePlugin.DibImageFile(StringIO.StringIO(data)) + return data diff --git a/PIL/ImageMath.py b/PIL/ImageMath.py new file mode 100644 index 000000000..56c42a45a --- /dev/null +++ b/PIL/ImageMath.py @@ -0,0 +1,207 @@ +# +# The Python Imaging Library +# $Id$ +# +# a simple math add-on for the Python Imaging Library +# +# History: +# 1999-02-15 fl Original PIL Plus release +# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 +# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 +# +# Copyright (c) 1999-2005 by Secret Labs AB +# Copyright (c) 2005 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image +import _imagingmath + +VERBOSE = 0 + +def _isconstant(v): + return isinstance(v, type(0)) or isinstance(v, type(0.0)) + +class _Operand: + # wraps an image operand, providing standard operators + + def __init__(self, im): + self.im = im + + def __fixup(self, im1): + # convert image to suitable mode + if isinstance(im1, _Operand): + # argument was an image. + if im1.im.mode in ("1", "L"): + return im1.im.convert("I") + elif im1.im.mode in ("I", "F"): + return im1.im + else: + raise ValueError, "unsupported mode: %s" % im1.im.mode + else: + # argument was a constant + if _isconstant(im1) and self.im.mode in ("1", "L", "I"): + return Image.new("I", self.im.size, im1) + else: + return Image.new("F", self.im.size, im1) + + def apply(self, op, im1, im2=None, mode=None): + im1 = self.__fixup(im1) + if im2 is None: + # unary operation + out = Image.new(mode or im1.mode, im1.size, None) + im1.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError, "bad operand type for '%s'" % op + _imagingmath.unop(op, out.im.id, im1.im.id) + else: + # binary operation + im2 = self.__fixup(im2) + if im1.mode != im2.mode: + # convert both arguments to floating point + if im1.mode != "F": im1 = im1.convert("F") + if im2.mode != "F": im2 = im2.convert("F") + if im1.mode != im2.mode: + raise ValueError, "mode mismatch" + if im1.size != im2.size: + # crop both arguments to a common size + size = (min(im1.size[0], im2.size[0]), + min(im1.size[1], im2.size[1])) + if im1.size != size: im1 = im1.crop((0, 0) + size) + if im2.size != size: im2 = im2.crop((0, 0) + size) + out = Image.new(mode or im1.mode, size, None) + else: + out = Image.new(mode or im1.mode, im1.size, None) + im1.load(); im2.load() + try: + op = getattr(_imagingmath, op+"_"+im1.mode) + except AttributeError: + raise TypeError, "bad operand type for '%s'" % op + _imagingmath.binop(op, out.im.id, im1.im.id, im2.im.id) + return _Operand(out) + + # unary operators + def __nonzero__(self): + # an image is "true" if it contains at least one non-zero pixel + return self.im.getbbox() is not None + def __abs__(self): + return self.apply("abs", self) + def __pos__(self): + return self + def __neg__(self): + return self.apply("neg", self) + + # binary operators + def __add__(self, other): + return self.apply("add", self, other) + def __radd__(self, other): + return self.apply("add", other, self) + def __sub__(self, other): + return self.apply("sub", self, other) + def __rsub__(self, other): + return self.apply("sub", other, self) + def __mul__(self, other): + return self.apply("mul", self, other) + def __rmul__(self, other): + return self.apply("mul", other, self) + def __div__(self, other): + return self.apply("div", self, other) + def __rdiv__(self, other): + return self.apply("div", other, self) + def __mod__(self, other): + return self.apply("mod", self, other) + def __rmod__(self, other): + return self.apply("mod", other, self) + def __pow__(self, other): + return self.apply("pow", self, other) + def __rpow__(self, other): + return self.apply("pow", other, self) + + # bitwise + def __invert__(self): + return self.apply("invert", self) + def __and__(self, other): + return self.apply("and", self, other) + def __rand__(self, other): + return self.apply("and", other, self) + def __or__(self, other): + return self.apply("or", self, other) + def __ror__(self, other): + return self.apply("or", other, self) + def __xor__(self, other): + return self.apply("xor", self, other) + def __rxor__(self, other): + return self.apply("xor", other, self) + def __lshift__(self, other): + return self.apply("lshift", self, other) + def __rshift__(self, other): + return self.apply("rshift", self, other) + + # logical + def __eq__(self, other): + return self.apply("eq", self, other) + def __ne__(self, other): + return self.apply("ne", self, other) + def __lt__(self, other): + return self.apply("lt", self, other) + def __le__(self, other): + return self.apply("le", self, other) + def __gt__(self, other): + return self.apply("gt", self, other) + def __ge__(self, other): + return self.apply("ge", self, other) + +# conversions +def imagemath_int(self): + return _Operand(self.im.convert("I")) +def imagemath_float(self): + return _Operand(self.im.convert("F")) + +# logical +def imagemath_equal(self, other): + return self.apply("eq", self, other, mode="I") +def imagemath_notequal(self, other): + return self.apply("ne", self, other, mode="I") + +def imagemath_min(self, other): + return self.apply("min", self, other) +def imagemath_max(self, other): + return self.apply("max", self, other) + +def imagemath_convert(self, mode): + return _Operand(self.im.convert(mode)) + +ops = {} +for k, v in globals().items(): + if k[:10] == "imagemath_": + ops[k[10:]] = v + +## +# Evaluates an image expression. +# +# @param expression A string containing a Python-style expression. +# @keyparam options Values to add to the evaluation context. You +# can either use a dictionary, or one or more keyword arguments. +# @return The evaluated expression. This is usually an image object, +# but can also be an integer, a floating point value, or a pixel +# tuple, depending on the expression. + +def eval(expression, _dict={}, **kw): + + # build execution namespace + args = ops.copy() + args.update(_dict) + args.update(kw) + for k, v in args.items(): + if hasattr(v, "im"): + args[k] = _Operand(v) + + import __builtin__ + out =__builtin__.eval(expression, args) + try: + return out.im + except AttributeError: + return out diff --git a/PIL/ImageMode.py b/PIL/ImageMode.py new file mode 100644 index 000000000..1d5df1c6d --- /dev/null +++ b/PIL/ImageMode.py @@ -0,0 +1,50 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard mode descriptors +# +# History: +# 2006-03-20 fl Added +# +# Copyright (c) 2006 by Secret Labs AB. +# Copyright (c) 2006 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# mode descriptor cache +_modes = {} + +## +# Wrapper for mode strings. + +class ModeDescriptor: + + def __init__(self, mode, bands, basemode, basetype): + self.mode = mode + self.bands = bands + self.basemode = basemode + self.basetype = basetype + + def __str__(self): + return self.mode + +## +# Gets a mode descriptor for the given mode. + +def getmode(mode): + if not _modes: + # initialize mode cache + import Image + # core modes + for m, (basemode, basetype, bands) in Image._MODEINFO.items(): + _modes[m] = ModeDescriptor(m, bands, basemode, basetype) + # extra experimental modes + _modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L") + _modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") + # mapping modes + _modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") + _modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") + _modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") + return _modes[mode] diff --git a/PIL/ImageOps.py b/PIL/ImageOps.py new file mode 100644 index 000000000..b51d78e25 --- /dev/null +++ b/PIL/ImageOps.py @@ -0,0 +1,439 @@ +# +# The Python Imaging Library. +# $Id$ +# +# standard image operations +# +# History: +# 2001-10-20 fl Created +# 2001-10-23 fl Added autocontrast operator +# 2001-12-18 fl Added Kevin's fit operator +# 2004-03-14 fl Fixed potential division by zero in equalize +# 2005-05-05 fl Fixed equalize for low number of values +# +# Copyright (c) 2001-2004 by Secret Labs AB +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image +import operator + +## +# (New in 1.1.3) The ImageOps module contains a number of +# 'ready-made' image processing operations. This module is somewhat +# experimental, and most operators only work on L and RGB images. +# +# @since 1.1.3 +## + +# +# helpers + +def _border(border): + if type(border) is type(()): + if len(border) == 2: + left, top = right, bottom = border + elif len(border) == 4: + left, top, right, bottom = border + else: + left = top = right = bottom = border + return left, top, right, bottom + +def _color(color, mode): + if Image.isStringType(color): + import ImageColor + color = ImageColor.getcolor(color, mode) + return color + +def _lut(image, lut): + if image.mode == "P": + # FIXME: apply to lookup table, not image data + raise NotImplementedError("mode P support coming soon") + elif image.mode in ("L", "RGB"): + if image.mode == "RGB" and len(lut) == 256: + lut = lut + lut + lut + return image.point(lut) + else: + raise IOError, "not supported for this image mode" + +# +# actions + +## +# Maximize (normalize) image contrast. This function calculates a +# histogram of the input image, removes cutoff percent of the +# lightest and darkest pixels from the histogram, and remaps the image +# so that the darkest pixel becomes black (0), and the lightest +# becomes white (255). +# +# @param image The image to process. +# @param cutoff How many percent to cut off from the histogram. +# @param ignore The background pixel value (use None for no background). +# @return An image. + +def autocontrast(image, cutoff=0, ignore=None): + "Maximize image contrast, based on histogram" + histogram = image.histogram() + lut = [] + for layer in range(0, len(histogram), 256): + h = histogram[layer:layer+256] + if ignore is not None: + # get rid of outliers + try: + h[ignore] = 0 + except TypeError: + # assume sequence + for ix in ignore: + h[ix] = 0 + if cutoff: + # cut off pixels from both ends of the histogram + # get number of pixels + n = 0 + for ix in range(256): + n = n + h[ix] + # remove cutoff% pixels from the low end + cut = n * cutoff / 100 + for lo in range(256): + if cut > h[lo]: + cut = cut - h[lo] + h[lo] = 0 + else: + h[lo] = h[lo] - cut + cut = 0 + if cut <= 0: + break + # remove cutoff% samples from the hi end + cut = n * cutoff / 100 + for hi in range(255, -1, -1): + if cut > h[hi]: + cut = cut - h[hi] + h[hi] = 0 + else: + h[hi] = h[hi] - cut + cut = 0 + if cut <= 0: + break + # find lowest/highest samples after preprocessing + for lo in range(256): + if h[lo]: + break + for hi in range(255, -1, -1): + if h[hi]: + break + if hi <= lo: + # don't bother + lut.extend(range(256)) + else: + scale = 255.0 / (hi - lo) + offset = -lo * scale + for ix in range(256): + ix = int(ix * scale + offset) + if ix < 0: + ix = 0 + elif ix > 255: + ix = 255 + lut.append(ix) + return _lut(image, lut) + +## +# Colorize grayscale image. The black and white +# arguments should be RGB tuples; this function calculates a colour +# wedge mapping all black pixels in the source image to the first +# colour, and all white pixels to the second colour. +# +# @param image The image to colourize. +# @param black The colour to use for black input pixels. +# @param white The colour to use for white input pixels. +# @return An image. + +def colorize(image, black, white): + "Colorize a grayscale image" + assert image.mode == "L" + black = _color(black, "RGB") + white = _color(white, "RGB") + red = []; green = []; blue = [] + for i in range(256): + red.append(black[0]+i*(white[0]-black[0])/255) + green.append(black[1]+i*(white[1]-black[1])/255) + blue.append(black[2]+i*(white[2]-black[2])/255) + image = image.convert("RGB") + return _lut(image, red + green + blue) + +## +# Remove border from image. The same amount of pixels are removed +# from all four sides. This function works on all image modes. +# +# @param image The image to crop. +# @param border The number of pixels to remove. +# @return An image. +# @see Image#Image.crop + +def crop(image, border=0): + "Crop border off image" + left, top, right, bottom = _border(border) + return image.crop( + (left, top, image.size[0]-right, image.size[1]-bottom) + ) + +## +# Deform the image. +# +# @param image The image to deform. +# @param deformer A deformer object. Any object that implements a +# getmesh method can be used. +# @param resample What resampling filter to use. +# @return An image. + +def deform(image, deformer, resample=Image.BILINEAR): + "Deform image using the given deformer" + return image.transform( + image.size, Image.MESH, deformer.getmesh(image), resample + ) + +## +# Equalize the image histogram. This function applies a non-linear +# mapping to the input image, in order to create a uniform +# distribution of grayscale values in the output image. +# +# @param image The image to equalize. +# @param mask An optional mask. If given, only the pixels selected by +# the mask are included in the analysis. +# @return An image. + +def equalize(image, mask=None): + "Equalize image histogram" + if image.mode == "P": + image = image.convert("RGB") + h = image.histogram(mask) + lut = [] + for b in range(0, len(h), 256): + histo = filter(None, h[b:b+256]) + if len(histo) <= 1: + lut.extend(range(256)) + else: + step = (reduce(operator.add, histo) - histo[-1]) / 255 + if not step: + lut.extend(range(256)) + else: + n = step / 2 + for i in range(256): + lut.append(n / step) + n = n + h[i+b] + return _lut(image, lut) + +## +# Add border to the image +# +# @param image The image to expand. +# @param border Border width, in pixels. +# @param fill Pixel fill value (a colour value). Default is 0 (black). +# @return An image. + +def expand(image, border=0, fill=0): + "Add border to image" + left, top, right, bottom = _border(border) + width = left + image.size[0] + right + height = top + image.size[1] + bottom + out = Image.new(image.mode, (width, height), _color(fill, image.mode)) + out.paste(image, (left, top)) + return out + +## +# Returns a sized and cropped version of the image, cropped to the +# requested aspect ratio and size. +#

+# The fit function was contributed by Kevin Cazabon. +# +# @param size The requested output size in pixels, given as a +# (width, height) tuple. +# @param method What resampling method to use. Default is Image.NEAREST. +# @param bleed Remove a border around the outside of the image (from all +# four edges. The value is a decimal percentage (use 0.01 for one +# percent). The default value is 0 (no border). +# @param centering Control the cropping position. Use (0.5, 0.5) for +# center cropping (e.g. if cropping the width, take 50% off of the +# left side, and therefore 50% off the right side). (0.0, 0.0) +# will crop from the top left corner (i.e. if cropping the width, +# take all of the crop off of the right side, and if cropping the +# height, take all of it off the bottom). (1.0, 0.0) will crop +# from the bottom left corner, etc. (i.e. if cropping the width, +# take all of the crop off the left side, and if cropping the height +# take none from the top, and therefore all off the bottom). +# @return An image. + +def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)): + """ + This method returns a sized and cropped version of the image, + cropped to the aspect ratio and size that you request. + """ + + # by Kevin Cazabon, Feb 17/2000 + # kevin@cazabon.com + # http://www.cazabon.com + + # ensure inputs are valid + if type(centering) != type([]): + centering = [centering[0], centering[1]] + + if centering[0] > 1.0 or centering[0] < 0.0: + centering [0] = 0.50 + if centering[1] > 1.0 or centering[1] < 0.0: + centering[1] = 0.50 + + if bleed > 0.49999 or bleed < 0.0: + bleed = 0.0 + + # calculate the area to use for resizing and cropping, subtracting + # the 'bleed' around the edges + + # number of pixels to trim off on Top and Bottom, Left and Right + bleedPixels = ( + int((float(bleed) * float(image.size[0])) + 0.5), + int((float(bleed) * float(image.size[1])) + 0.5) + ) + + liveArea = ( + bleedPixels[0], bleedPixels[1], image.size[0] - bleedPixels[0] - 1, + image.size[1] - bleedPixels[1] - 1 + ) + + liveSize = (liveArea[2] - liveArea[0], liveArea[3] - liveArea[1]) + + # calculate the aspect ratio of the liveArea + liveAreaAspectRatio = float(liveSize[0])/float(liveSize[1]) + + # calculate the aspect ratio of the output image + aspectRatio = float(size[0]) / float(size[1]) + + # figure out if the sides or top/bottom will be cropped off + if liveAreaAspectRatio >= aspectRatio: + # liveArea is wider than what's needed, crop the sides + cropWidth = int((aspectRatio * float(liveSize[1])) + 0.5) + cropHeight = liveSize[1] + else: + # liveArea is taller than what's needed, crop the top and bottom + cropWidth = liveSize[0] + cropHeight = int((float(liveSize[0])/aspectRatio) + 0.5) + + # make the crop + leftSide = int(liveArea[0] + (float(liveSize[0]-cropWidth) * centering[0])) + if leftSide < 0: + leftSide = 0 + topSide = int(liveArea[1] + (float(liveSize[1]-cropHeight) * centering[1])) + if topSide < 0: + topSide = 0 + + out = image.crop( + (leftSide, topSide, leftSide + cropWidth, topSide + cropHeight) + ) + + # resize the image and return it + return out.resize(size, method) + +## +# Flip the image vertically (top to bottom). +# +# @param image The image to flip. +# @return An image. + +def flip(image): + "Flip image vertically" + return image.transpose(Image.FLIP_TOP_BOTTOM) + +## +# Convert the image to grayscale. +# +# @param image The image to convert. +# @return An image. + +def grayscale(image): + "Convert to grayscale" + return image.convert("L") + +## +# Invert (negate) the image. +# +# @param image The image to invert. +# @return An image. + +def invert(image): + "Invert image (negate)" + lut = [] + for i in range(256): + lut.append(255-i) + return _lut(image, lut) + +## +# Flip image horizontally (left to right). +# +# @param image The image to mirror. +# @return An image. + +def mirror(image): + "Flip image horizontally" + return image.transpose(Image.FLIP_LEFT_RIGHT) + +## +# Reduce the number of bits for each colour channel. +# +# @param image The image to posterize. +# @param bits The number of bits to keep for each channel (1-8). +# @return An image. + +def posterize(image, bits): + "Reduce the number of bits per color channel" + lut = [] + mask = ~(2**(8-bits)-1) + for i in range(256): + lut.append(i & mask) + return _lut(image, lut) + +## +# Invert all pixel values above a threshold. +# +# @param image The image to posterize. +# @param threshold All pixels above this greyscale level are inverted. +# @return An image. + +def solarize(image, threshold=128): + "Invert all values above threshold" + lut = [] + for i in range(256): + if i < threshold: + lut.append(i) + else: + lut.append(255-i) + return _lut(image, lut) + +# -------------------------------------------------------------------- +# PIL USM components, from Kevin Cazabon. + +def gaussian_blur(im, radius=None): + """ PIL_usm.gblur(im, [radius])""" + + if radius is None: + radius = 5.0 + + im.load() + + return im.im.gaussian_blur(radius) + +gblur = gaussian_blur + +def unsharp_mask(im, radius=None, percent=None, threshold=None): + """ PIL_usm.usm(im, [radius, percent, threshold])""" + + if radius is None: + radius = 5.0 + if percent is None: + percent = 150 + if threshold is None: + threshold = 3 + + im.load() + + return im.im.unsharp_mask(radius, percent, threshold) + +usm = unsharp_mask diff --git a/PIL/ImagePalette.py b/PIL/ImagePalette.py new file mode 100644 index 000000000..6efee2998 --- /dev/null +++ b/PIL/ImagePalette.py @@ -0,0 +1,184 @@ +# +# The Python Imaging Library. +# $Id$ +# +# image palette object +# +# History: +# 1996-03-11 fl Rewritten. +# 1997-01-03 fl Up and running. +# 1997-08-23 fl Added load hack +# 2001-04-16 fl Fixed randint shadow bug in random() +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import array +import Image, ImageColor + +## +# Colour palette wrapper for palette mapped images. + +class ImagePalette: + "Colour palette for palette mapped images" + + def __init__(self, mode = "RGB", palette = None): + self.mode = mode + self.rawmode = None # if set, palette contains raw data + self.palette = palette or range(256)*len(self.mode) + self.colors = {} + self.dirty = None + if len(self.mode)*256 != len(self.palette): + raise ValueError, "wrong palette size" + + def getdata(self): + # experimental: get palette contents in format suitable + # for the low-level im.putpalette primitive + if self.rawmode: + return self.rawmode, self.palette + return self.mode + ";L", self.tostring() + + def tostring(self): + # experimental: convert palette to string + if self.rawmode: + raise ValueError("palette contains raw palette data") + if Image.isStringType(self.palette): + return self.palette + return array.array("B", self.palette).tostring() + + def getcolor(self, color): + # experimental: given an rgb tuple, allocate palette entry + if self.rawmode: + raise ValueError("palette contains raw palette data") + if Image.isTupleType(color): + try: + return self.colors[color] + except KeyError: + # allocate new color slot + if Image.isStringType(self.palette): + self.palette = map(int, self.palette) + index = len(self.colors) + if index >= 256: + raise ValueError("cannot allocate more than 256 colors") + self.colors[color] = index + self.palette[index] = color[0] + self.palette[index+256] = color[1] + self.palette[index+512] = color[2] + self.dirty = 1 + return index + else: + raise ValueError("unknown color specifier: %r" % color) + + def save(self, fp): + # (experimental) save palette to text file + if self.rawmode: + raise ValueError("palette contains raw palette data") + if type(fp) == type(""): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write("# Mode: %s\n" % self.mode) + for i in range(256): + fp.write("%d" % i) + for j in range(i, len(self.palette), 256): + fp.write(" %d" % self.palette[j]) + fp.write("\n") + fp.close() + +# -------------------------------------------------------------------- +# Internal + +def raw(rawmode, data): + palette = ImagePalette() + palette.rawmode = rawmode + palette.palette = data + palette.dirty = 1 + return palette + +# -------------------------------------------------------------------- +# Factories + +def _make_linear_lut(black, white): + lut = [] + if black == 0: + for i in range(256): + lut.append(white*i/255) + else: + raise NotImplementedError # FIXME + return lut + +def _make_gamma_lut(exp, mode="RGB"): + lut = [] + for i in range(256): + lut.append(int(((i / 255.0) ** exp) * 255.0 + 0.5)) + return lut + +def new(mode, data): + return Image.core.new_palette(mode, data) + +def negative(mode="RGB"): + palette = range(256) + palette.reverse() + return ImagePalette(mode, palette * len(mode)) + +def random(mode="RGB"): + from random import randint + palette = [] + for i in range(256*len(mode)): + palette.append(randint(0, 255)) + return ImagePalette(mode, palette) + +def sepia(white="#fff0c0"): + r, g, b = ImageColor.getrgb(white) + r = _make_linear_lut(0, r) + g = _make_linear_lut(0, g) + b = _make_linear_lut(0, b) + return ImagePalette("RGB", r + g + b) + +def wedge(mode="RGB"): + return ImagePalette(mode, range(256) * len(mode)) + +def load(filename): + + # FIXME: supports GIMP gradients only + + fp = open(filename, "rb") + + lut = None + + if not lut: + try: + import GimpPaletteFile + fp.seek(0) + p = GimpPaletteFile.GimpPaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + pass + + if not lut: + try: + import GimpGradientFile + fp.seek(0) + p = GimpGradientFile.GimpGradientFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + pass + + if not lut: + try: + import PaletteFile + fp.seek(0) + p = PaletteFile.PaletteFile(fp) + lut = p.getpalette() + except (SyntaxError, ValueError): + pass + + if not lut: + raise IOError, "cannot load palette" + + return lut # data, rawmode + + +# add some psuedocolour palettes as well diff --git a/PIL/ImagePath.py b/PIL/ImagePath.py new file mode 100644 index 000000000..721fd9449 --- /dev/null +++ b/PIL/ImagePath.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library +# $Id$ +# +# path interface +# +# History: +# 1996-11-04 fl Created +# 2002-04-14 fl Added documentation stub class +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +import Image + +## +# Path wrapper. + +class Path: + + ## + # Creates a path object. + # + # @param xy Sequence. The sequence can contain 2-tuples [(x, y), ...] + # or a flat list of numbers [x, y, ...]. + + def __init__(self, xy): + pass + + ## + # Compacts the path, by removing points that are close to each + # other. This method modifies the path in place. + + def compact(self, distance=2): + pass + + ## + # Gets the bounding box. + + def getbbox(self): + pass + + ## + # Maps the path through a function. + + def map(self, function): + pass + + ## + # Converts the path to Python list. + # + # @param flat By default, this function returns a list of 2-tuples + # [(x, y), ...]. If this argument is true, it returns a flat + # list [x, y, ...] instead. + # @return A list of coordinates. + + def tolist(self, flat=0): + pass + + ## + # Transforms the path. + + def transform(self, matrix): + pass + + +# override with C implementation +Path = Image.core.path diff --git a/PIL/ImageQt.py b/PIL/ImageQt.py new file mode 100644 index 000000000..50ee07d6c --- /dev/null +++ b/PIL/ImageQt.py @@ -0,0 +1,84 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a simple Qt image interface. +# +# history: +# 2006-06-03 fl: created +# 2006-06-04 fl: inherit from QImage instead of wrapping it +# 2006-06-05 fl: removed toimage helper; move string support to ImageQt +# +# Copyright (c) 2006 by Secret Labs AB +# Copyright (c) 2006 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image + +from PyQt4.QtGui import QImage, qRgb + +## +# (Internal) Turns an RGB color into a Qt compatible color integer. + +def rgb(r, g, b): + # use qRgb to pack the colors, and then turn the resulting long + # into a negative integer with the same bitpattern. + return (qRgb(r, g, b) & 0xffffff) - 0x1000000 + +## +# An PIL image wrapper for Qt. This is a subclass of PyQt4's QImage +# class. +# +# @param im A PIL Image object, or a file name (given either as Python +# string or a PyQt string object). + +class ImageQt(QImage): + + def __init__(self, im): + + data = None + colortable = None + + # handle filename, if given instead of image name + if hasattr(im, "toUtf8"): + # FIXME - is this really the best way to do this? + im = unicode(im.toUtf8(), "utf-8") + if Image.isStringType(im): + im = Image.open(im) + + if im.mode == "1": + format = QImage.Format_Mono + elif im.mode == "L": + format = QImage.Format_Indexed8 + colortable = [] + for i in range(256): + colortable.append(rgb(i, i, i)) + elif im.mode == "P": + format = QImage.Format_Indexed8 + colortable = [] + palette = im.getpalette() + for i in range(0, len(palette), 3): + colortable.append(rgb(*palette[i:i+3])) + elif im.mode == "RGB": + data = im.tostring("raw", "BGRX") + format = QImage.Format_RGB32 + elif im.mode == "RGBA": + try: + data = im.tostring("raw", "BGRA") + except SystemError: + # workaround for earlier versions + r, g, b, a = im.split() + im = Image.merge("RGBA", (b, g, r, a)) + format = QImage.Format_ARGB32 + else: + raise ValueError("unsupported image mode %r" % im.mode) + + # must keep a reference, or Qt will crash! + self.__data = data or im.tostring() + + QImage.__init__(self, self.__data, im.size[0], im.size[1], format) + + if colortable: + self.setColorTable(colortable) diff --git a/PIL/ImageSequence.py b/PIL/ImageSequence.py new file mode 100644 index 000000000..e94ca0b1e --- /dev/null +++ b/PIL/ImageSequence.py @@ -0,0 +1,38 @@ +# +# The Python Imaging Library. +# $Id$ +# +# sequence support classes +# +# history: +# 1997-02-20 fl Created +# +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# This class implements an iterator object that can be used to loop +# over an image sequence. + +class Iterator: + + ## + # Create an iterator. + # + # @param im An image object. + + def __init__(self, im): + if not hasattr(im, "seek"): + raise AttributeError("im must have seek method") + self.im = im + + def __getitem__(self, ix): + try: + if ix: + self.im.seek(ix) + return self.im + except EOFError: + raise IndexError # end of sequence diff --git a/PIL/ImageShow.py b/PIL/ImageShow.py new file mode 100644 index 000000000..05270f440 --- /dev/null +++ b/PIL/ImageShow.py @@ -0,0 +1,163 @@ +# +# The Python Imaging Library. +# $Id$ +# +# im.show() drivers +# +# History: +# 2008-04-06 fl Created +# +# Copyright (c) Secret Labs AB 2008. +# +# See the README file for information on usage and redistribution. +# + +import Image +import os, sys + +_viewers = [] + +def register(viewer, order=1): + try: + if issubclass(viewer, Viewer): + viewer = viewer() + except TypeError: + pass # raised if viewer wasn't a class + if order > 0: + _viewers.append(viewer) + elif order < 0: + _viewers.insert(0, viewer) + +## +# Displays a given image. +# +# @param image An image object. +# @param title Optional title. Not all viewers can display the title. +# @param **options Additional viewer options. +# @return True if a suitable viewer was found, false otherwise. + +def show(image, title=None, **options): + for viewer in _viewers: + if viewer.show(image, title=title, **options): + return 1 + return 0 + +## +# Base class for viewers. + +class Viewer: + + # main api + + def show(self, image, **options): + + # save temporary image to disk + if image.mode[:4] == "I;16": + # @PIL88 @PIL101 + # "I;16" isn't an 'official' mode, but we still want to + # provide a simple way to show 16-bit images. + base = "L" + # FIXME: auto-contrast if max() > 255? + else: + base = Image.getmodebase(image.mode) + if base != image.mode and image.mode != "1": + image = image.convert(base) + + self.show_image(image, **options) + + # hook methods + + format = None + + def get_format(self, image): + # return format name, or None to save as PGM/PPM + return self.format + + def get_command(self, file, **options): + raise NotImplementedError + + def save_image(self, image): + # save to temporary file, and return filename + return image._dump(format=self.get_format(image)) + + def show_image(self, image, **options): + # display given image + return self.show_file(self.save_image(image), **options) + + def show_file(self, file, **options): + # display given file + os.system(self.get_command(file, **options)) + return 1 + +# -------------------------------------------------------------------- + +if sys.platform == "win32": + + class WindowsViewer(Viewer): + format = "BMP" + def get_command(self, file, **options): + return "start /wait %s && del /f %s" % (file, file) + + register(WindowsViewer) + +elif sys.platform == "darwin": + + class MacViewer(Viewer): + format = "BMP" + def get_command(self, file, **options): + # on darwin open returns immediately resulting in the temp + # file removal while app is opening + command = "open -a /Applications/Preview.app" + command = "(%s %s; sleep 20; rm -f %s)&" % (command, file, file) + return command + + register(MacViewer) + +else: + + # unixoids + + def which(executable): + path = os.environ.get("PATH") + if not path: + return None + for dirname in path.split(os.pathsep): + filename = os.path.join(dirname, executable) + if os.path.isfile(filename): + # FIXME: make sure it's executable + return filename + return None + + class UnixViewer(Viewer): + def show_file(self, file, **options): + command, executable = self.get_command_ex(file, **options) + command = "(%s %s; rm -f %s)&" % (command, file, file) + os.system(command) + return 1 + + # implementations + + class DisplayViewer(UnixViewer): + def get_command_ex(self, file, **options): + command = executable = "display" + return command, executable + + if which("display"): + register(DisplayViewer) + + class XVViewer(UnixViewer): + def get_command_ex(self, file, title=None, **options): + # note: xv is pretty outdated. most modern systems have + # imagemagick's display command instead. + command = executable = "xv" + if title: + # FIXME: do full escaping + command = command + " -name \"%s\"" % title + return command, executable + + if which("xv"): + register(XVViewer) + +if __name__ == "__main__": + # usage: python ImageShow.py imagefile [title] + print show(Image.open(sys.argv[1]), *sys.argv[2:]) diff --git a/PIL/ImageStat.py b/PIL/ImageStat.py new file mode 100644 index 000000000..9ebdab030 --- /dev/null +++ b/PIL/ImageStat.py @@ -0,0 +1,164 @@ +# +# The Python Imaging Library. +# $Id$ +# +# global image statistics +# +# History: +# 1996-04-05 fl Created +# 1997-05-21 fl Added mask; added rms, var, stddev attributes +# 1997-08-05 fl Added median +# 1998-07-05 hk Fixed integer overflow error +# +# Notes: +# This class shows how to implement delayed evaluation of attributes. +# To get a certain value, simply access the corresponding attribute. +# The __getattr__ dispatcher takes care of the rest. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996-97. +# +# See the README file for information on usage and redistribution. +# + +import Image +import operator, math + +## +# The ImageStat module calculates global statistics for an +# image, or a region of an image. +## + +## +# Calculate statistics for the given image. If a mask is included, +# only the regions covered by that mask are included in the +# statistics. + +class Stat: + "Get image or feature statistics" + + ## + # Create a statistics object. + # + # @def __init__(image, mask=None) + # @param image A PIL image, or a precalculate histogram. + # @param mask An optional mask. + + def __init__(self, image_or_list, mask = None): + try: + if mask: + self.h = image_or_list.histogram(mask) + else: + self.h = image_or_list.histogram() + except AttributeError: + self.h = image_or_list # assume it to be a histogram list + if type(self.h) != type([]): + raise TypeError, "first argument must be image or list" + self.bands = range(len(self.h) / 256) + + def __getattr__(self, id): + "Calculate missing attribute" + if id[:4] == "_get": + raise AttributeError, id + # calculate missing attribute + v = getattr(self, "_get" + id)() + setattr(self, id, v) + return v + + def _getextrema(self): + "Get min/max values for each band in the image" + + def minmax(histogram): + n = 255 + x = 0 + for i in range(256): + if histogram[i]: + n = min(n, i) + x = max(x, i) + return n, x # returns (255, 0) if there's no data in the histogram + + v = [] + for i in range(0, len(self.h), 256): + v.append(minmax(self.h[i:])) + return v + + def _getcount(self): + "Get total number of pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + v.append(reduce(operator.add, self.h[i:i+256])) + return v + + def _getsum(self): + "Get sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum = 0.0 + for j in range(256): + sum = sum + j * self.h[i+j] + v.append(sum) + return v + + def _getsum2(self): + "Get squared sum of all pixels in each layer" + + v = [] + for i in range(0, len(self.h), 256): + sum2 = 0.0 + for j in range(256): + sum2 = sum2 + (j ** 2) * float(self.h[i+j]) + v.append(sum2) + return v + + def _getmean(self): + "Get average pixel level for each layer" + + v = [] + for i in self.bands: + v.append(self.sum[i] / self.count[i]) + return v + + def _getmedian(self): + "Get median pixel level for each layer" + + v = [] + for i in self.bands: + s = 0 + l = self.count[i]/2 + b = i * 256 + for j in range(256): + s = s + self.h[b+j] + if s > l: + break + v.append(j) + return v + + def _getrms(self): + "Get RMS for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.sum2[i] / self.count[i])) + return v + + + def _getvar(self): + "Get variance for each layer" + + v = [] + for i in self.bands: + n = self.count[i] + v.append((self.sum2[i]-(self.sum[i]**2.0)/n)/n) + return v + + def _getstddev(self): + "Get standard deviation for each layer" + + v = [] + for i in self.bands: + v.append(math.sqrt(self.var[i])) + return v + +Global = Stat # compatibility diff --git a/PIL/ImageTk.py b/PIL/ImageTk.py new file mode 100644 index 000000000..8618139a5 --- /dev/null +++ b/PIL/ImageTk.py @@ -0,0 +1,296 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Tk display interface +# +# History: +# 96-04-08 fl Created +# 96-09-06 fl Added getimage method +# 96-11-01 fl Rewritten, removed image attribute and crop method +# 97-05-09 fl Use PyImagingPaste method instead of image type +# 97-05-12 fl Minor tweaks to match the IFUNC95 interface +# 97-05-17 fl Support the "pilbitmap" booster patch +# 97-06-05 fl Added file= and data= argument to image constructors +# 98-03-09 fl Added width and height methods to Image classes +# 98-07-02 fl Use default mode for "P" images without palette attribute +# 98-07-02 fl Explicitly destroy Tkinter image objects +# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) +# 99-07-26 fl Automatically hook into Tkinter (if possible) +# 99-08-15 fl Hook uses _imagingtk instead of _imaging +# +# Copyright (c) 1997-1999 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Tkinter, Image + +## +# The ImageTk module contains support to create and modify +# Tkinter BitmapImage and PhotoImage objects. +#

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

+ # @def __init__(image=None, size=None, **options) + # @param image Either a PIL image, or a mode string. If a + # mode string is used, a size must also be given. + # @param size If the first argument is a mode string, this + # defines the size of the image. + # @keyparam file A filename to load the image from (using + # Image.open(file)). + # @keyparam data An 8-bit string containing image data (as + # loaded from an image file). + + def __init__(self, image=None, size=None, **kw): + + # Tk compatibility: file or data + if image is None: + if kw.has_key("file"): + image = Image.open(kw["file"]) + del kw["file"] + elif kw.has_key("data"): + from StringIO import StringIO + image = Image.open(StringIO(kw["data"])) + del kw["data"] + + if hasattr(image, "mode") and hasattr(image, "size"): + # got an image instead of a mode + mode = image.mode + if mode == "P": + # palette mapped data + image.load() + try: + mode = image.palette.mode + except AttributeError: + mode = "RGB" # default + size = image.size + kw["width"], kw["height"] = size + else: + mode = image + image = None + + if mode not in ["1", "L", "RGB", "RGBA"]: + mode = Image.getmodebase(mode) + + self.__mode = mode + self.__size = size + self.__photo = apply(Tkinter.PhotoImage, (), kw) + self.tk = self.__photo.tk + if image: + self.paste(image) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + ## + # Get the Tkinter photo image identifier. This method is + # automatically called by Tkinter whenever a PhotoImage object is + # passed to a Tkinter method. + # + # @return A Tkinter photo image identifier (a string). + + def __str__(self): + return str(self.__photo) + + ## + # Get the width of the image. + # + # @return The width, in pixels. + + def width(self): + return self.__size[0] + + ## + # Get the height of the image. + # + # @return The height, in pixels. + + def height(self): + return self.__size[1] + + ## + # Paste a PIL image into the photo image. Note that this can + # be very slow if the photo image is displayed. + # + # @param im A PIL image. The size must match the target region. + # If the mode does not match, the image is converted to the + # mode of the bitmap image. + # @param box A 4-tuple defining the left, upper, right, and + # lower pixel coordinate. If None is given instead of a + # tuple, all of the image is assumed. + + def paste(self, im, box=None): + + # convert to blittable + im.load() + image = im.im + if image.isblock() and im.mode == self.__mode: + block = image + else: + block = image.new_block(self.__mode, im.size) + image.convert2(block, image) # convert directly between buffers + + tk = self.__photo.tk + + try: + tk.call("PyImagingPhoto", self.__photo, block.id) + except Tkinter.TclError, v: + # activate Tkinter hook + try: + import _imagingtk + try: + _imagingtk.tkinit(tk.interpaddr(), 1) + except AttributeError: + _imagingtk.tkinit(id(tk), 0) + tk.call("PyImagingPhoto", self.__photo, block.id) + except (ImportError, AttributeError, Tkinter.TclError): + raise # configuration problem; cannot attach to Tkinter + +# -------------------------------------------------------------------- +# BitmapImage + +## +# Create a Tkinter-compatible bitmap image. This can be used +# everywhere Tkinter expects an image object. + +class BitmapImage: + + ## + # Create a Tkinter-compatible bitmap image. + #

+ # The given image must have mode "1". Pixels having value 0 are + # treated as transparent. Options, if any, are passed on to + # Tkinter. The most commonly used option is foreground, + # which is used to specify the colour for the non-transparent + # parts. See the Tkinter documentation for information on how to + # specify colours. + # + # @def __init__(image=None, **options) + # @param image A PIL image. + + def __init__(self, image=None, **kw): + + # Tk compatibility: file or data + if image is None: + if kw.has_key("file"): + image = Image.open(kw["file"]) + del kw["file"] + elif kw.has_key("data"): + from StringIO import StringIO + image = Image.open(StringIO(kw["data"])) + del kw["data"] + + self.__mode = image.mode + self.__size = image.size + + if _pilbitmap_check(): + # fast way (requires the pilbitmap booster patch) + image.load() + kw["data"] = "PIL:%d" % image.im.id + self.__im = image # must keep a reference + else: + # slow but safe way + kw["data"] = image.tobitmap() + self.__photo = apply(Tkinter.BitmapImage, (), kw) + + def __del__(self): + name = self.__photo.name + self.__photo.name = None + try: + self.__photo.tk.call("image", "delete", name) + except: + pass # ignore internal errors + + ## + # Get the width of the image. + # + # @return The width, in pixels. + + def width(self): + return self.__size[0] + + ## + # Get the height of the image. + # + # @return The height, in pixels. + + def height(self): + return self.__size[1] + + ## + # Get the Tkinter bitmap image identifier. This method is + # automatically called by Tkinter whenever a BitmapImage object + # is passed to a Tkinter method. + # + # @return A Tkinter bitmap image identifier (a string). + + def __str__(self): + return str(self.__photo) + +## +# Copies the contents of a PhotoImage to a PIL image memory. + +def getimage(photo): + photo.tk.call("PyImagingPhotoGet", photo) + +# -------------------------------------------------------------------- +# Helper for the Image.show method. + +def _show(image, title): + + class UI(Tkinter.Label): + def __init__(self, master, im): + if im.mode == "1": + self.image = BitmapImage(im, foreground="white", master=master) + else: + self.image = PhotoImage(im, master=master) + Tkinter.Label.__init__(self, master, image=self.image, + bg="black", bd=0) + + if not Tkinter._default_root: + raise IOError, "tkinter not initialized" + top = Tkinter.Toplevel() + if title: + top.title(title) + UI(top, image).pack() diff --git a/PIL/ImageTransform.py b/PIL/ImageTransform.py new file mode 100644 index 000000000..cc323d3b9 --- /dev/null +++ b/PIL/ImageTransform.py @@ -0,0 +1,95 @@ +# +# The Python Imaging Library. +# $Id$ +# +# transform wrappers +# +# History: +# 2002-04-08 fl Created +# +# Copyright (c) 2002 by Secret Labs AB +# Copyright (c) 2002 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +import Image + +class Transform(Image.ImageTransformHandler): + def __init__(self, data): + self.data = data + def getdata(self): + return self.method, self.data + def transform(self, size, image, **options): + # can be overridden + method, data = self.getdata() + return image.transform(size, method, data, **options) + +## +# Define an affine image transform. +#

+# This function takes a 6-tuple (a, b, c, d, e, f) which +# contain the first two rows from an affine transform matrix. For +# each pixel (x, y) in the output image, the new value is +# taken from a position (a x + b y + c, +# d x + e y + f) in the input image, rounded to +# nearest pixel. +#

+# This function can be used to scale, translate, rotate, and shear the +# original image. +# +# @def AffineTransform(matrix) +# @param matrix A 6-tuple (a, b, c, d, e, f) containing +# the first two rows from an affine transform matrix. +# @see Image#Image.transform + +class AffineTransform(Transform): + method = Image.AFFINE + +## +# Define a transform to extract a subregion from an image. +#

+# Maps a rectangle (defined by two corners) from the image to a +# rectangle of the given size. The resulting image will contain +# data sampled from between the corners, such that (x0, y0) +# in the input image will end up at (0,0) in the output image, +# and (x1, y1) at size. +#

+# This method can be used to crop, stretch, shrink, or mirror an +# arbitrary rectangle in the current image. It is slightly slower than +# crop, but about as fast as a corresponding resize +# operation. +# +# @def ExtentTransform(bbox) +# @param bbox A 4-tuple (x0, y0, x1, y1) which specifies +# two points in the input image's coordinate system. +# @see Image#Image.transform + +class ExtentTransform(Transform): + method = Image.EXTENT + +## +# Define an quad image transform. +#

+# Maps a quadrilateral (a region defined by four corners) from the +# image to a rectangle of the given size. +# +# @def QuadTransform(xy) +# @param xy An 8-tuple (x0, y0, x1, y1, x2, y2, y3, y3) which +# contain the upper left, lower left, lower right, and upper right +# corner of the source quadrilateral. +# @see Image#Image.transform + +class QuadTransform(Transform): + method = Image.QUAD + +## +# Define an mesh image transform. A mesh transform consists of one +# or more individual quad transforms. +# +# @def MeshTransform(data) +# @param data A list of (bbox, quad) tuples. +# @see Image#Image.transform + +class MeshTransform(Transform): + method = Image.MESH diff --git a/PIL/ImageWin.py b/PIL/ImageWin.py new file mode 100644 index 000000000..f98725f74 --- /dev/null +++ b/PIL/ImageWin.py @@ -0,0 +1,215 @@ +# +# The Python Imaging Library. +# $Id$ +# +# a Windows DIB display interface +# +# History: +# 1996-05-20 fl Created +# 1996-09-20 fl Fixed subregion exposure +# 1997-09-21 fl Added draw primitive (for tzPrint) +# 2003-05-21 fl Added experimental Window/ImageWindow classes +# 2003-09-05 fl Added fromstring/tostring methods +# +# Copyright (c) Secret Labs AB 1997-2003. +# Copyright (c) Fredrik Lundh 1996-2003. +# +# See the README file for information on usage and redistribution. +# + +import Image + +## +# The ImageWin module contains support to create and display +# images under Windows 95/98, NT, 2000 and later. + +class HDC: + def __init__(self, dc): + self.dc = dc + def __int__(self): + return self.dc + +class HWND: + def __init__(self, wnd): + self.wnd = wnd + def __int__(self): + return self.wnd + +## +# Create a Windows bitmap with the given mode and size. The mode can +# be one of "1", "L", "P", or "RGB". +# +# If the display requires a palette, this constructor creates a +# suitable palette and associates it with the image. For an "L" image, +# 128 greylevels are allocated. For an "RGB" image, a 6x6x6 colour +# cube is used, together with 20 greylevels. +# +# To make sure that palettes work properly under Windows, you must +# call the palette method upon certain events from Windows. + +class Dib: + + ## + # Create Windows bitmap. + # + # @param image Either a PIL image, or a mode string. If a + # mode string is used, a size must also be given. The + # mode can be one of "1", "L", "P", or "RGB". + # @param size If the first argument is a mode string, this + # defines the size of the image. + + def __init__(self, image, size=None): + if hasattr(image, "mode") and hasattr(image, "size"): + mode = image.mode + size = image.size + else: + mode = image + image = None + if mode not in ["1", "L", "P", "RGB"]: + mode = Image.getmodebase(mode) + self.image = Image.core.display(mode, size) + self.mode = mode + self.size = size + if image: + self.paste(image) + + ## + # Copy the bitmap contents to a device context. + # + # @param handle Device context (HDC), cast to a Python integer, + # or a HDC or HWND instance. In PythonWin, you can use the + # GetHandleAttrib method of the CDC class to get + # a suitable handle. + + def expose(self, handle): + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.expose(dc) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.expose(handle) + return result + + def draw(self, handle, dst, src=None): + if not src: + src = (0,0) + self.size + if isinstance(handle, HWND): + dc = self.image.getdc(handle) + try: + result = self.image.draw(dc, dst, src) + finally: + self.image.releasedc(handle, dc) + else: + result = self.image.draw(handle, dst, src) + return result + + ## + # Installs the palette associated with the image in the + # given device context. + #

+ # This method should be called upon QUERYNEWPALETTE + # and PALETTECHANGED events from Windows. If this + # method returns a non-zero value, one or more display + # palette entries were changed, and the image should be + # redrawn. + # + # @param handle Device context (HDC), cast to a Python integer, + # or an HDC or HWND instance. + # @return A true value if one or more entries were changed + # (this indicates that the image should be redrawn). + + def query_palette(self, handle): + if isinstance(handle, HWND): + handle = self.image.getdc(handle) + try: + result = self.image.query_palette(handle) + finally: + self.image.releasedc(handle, handle) + else: + result = self.image.query_palette(handle) + return result + + ## + # Paste a PIL image into the bitmap image. + # + # @param im A PIL image. The size must match the target region. + # If the mode does not match, the image is converted to the + # mode of the bitmap image. + # @param box A 4-tuple defining the left, upper, right, and + # lower pixel coordinate. If None is given instead of a + # tuple, all of the image is assumed. + + def paste(self, im, box=None): + im.load() + if self.mode != im.mode: + im = im.convert(self.mode) + if box: + self.image.paste(im.im, box) + else: + self.image.paste(im.im) + + ## + # Load display memory contents from string buffer. + # + # @param buffer A string buffer containing display data (usually + # data returned from tostring) + + def fromstring(self, buffer): + return self.image.fromstring(buffer) + + ## + # Copy display memory contents to string buffer. + # + # @return A string buffer containing display data. + + def tostring(self): + return self.image.tostring() + + +## +# Create a Window with the given title size. + +class Window: + + def __init__(self, title="PIL", width=None, height=None): + self.hwnd = Image.core.createwindow( + title, self.__dispatcher, width or 0, height or 0 + ) + + def __dispatcher(self, action, *args): + return apply(getattr(self, "ui_handle_" + action), args) + + def ui_handle_clear(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_damage(self, x0, y0, x1, y1): + pass + + def ui_handle_destroy(self): + pass + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + pass + + def ui_handle_resize(self, width, height): + pass + + def mainloop(self): + Image.core.eventloop() + +## +# Create an image window which displays the given image. + +class ImageWindow(Window): + + def __init__(self, image, title="PIL"): + if not isinstance(image, Dib): + image = Dib(image) + self.image = image + width, height = image.size + Window.__init__(self, title, width=width, height=height) + + def ui_handle_repair(self, dc, x0, y0, x1, y1): + self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/PIL/ImtImagePlugin.py b/PIL/ImtImagePlugin.py new file mode 100644 index 000000000..bf5611b8a --- /dev/null +++ b/PIL/ImtImagePlugin.py @@ -0,0 +1,93 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IM Tools support for PIL +# +# history: +# 1996-05-27 fl Created (read 8-bit images only) +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import re + +import Image, ImageFile + +# +# -------------------------------------------------------------------- + +field = re.compile(r"([a-z]*) ([^ \r\n]*)") + +## +# Image plugin for IM Tools images. + +class ImtImageFile(ImageFile.ImageFile): + + format = "IMT" + format_description = "IM Tools" + + def _open(self): + + # Quick rejection: if there's not a LF among the first + # 100 bytes, this is (probably) not a text header. + + if not "\n" in self.fp.read(100): + raise SyntaxError, "not an IM file" + self.fp.seek(0) + + xsize = ysize = 0 + + while 1: + + s = self.fp.read(1) + if not s: + break + + if s == chr(12): + + # image data begins + self.tile = [("raw", (0,0)+self.size, + self.fp.tell(), + (self.mode, 0, 1))] + + break + + else: + + # read key/value pair + # FIXME: dangerous, may read whole file + s = s + self.fp.readline() + if len(s) == 1 or len(s) > 100: + break + if s[0] == "*": + continue # comment + + m = field.match(s) + if not m: + break + k, v = m.group(1,2) + if k == "width": + xsize = int(v) + self.size = xsize, ysize + elif k == "height": + ysize = int(v) + self.size = xsize, ysize + elif k == "pixel" and v == "n8": + self.mode = "L" + + +# +# -------------------------------------------------------------------- + +Image.register_open("IMT", ImtImageFile) + +# +# no extension registered (".im" is simply too common) diff --git a/PIL/IptcImagePlugin.py b/PIL/IptcImagePlugin.py new file mode 100644 index 000000000..acea5d18b --- /dev/null +++ b/PIL/IptcImagePlugin.py @@ -0,0 +1,288 @@ +# +# The Python Imaging Library. +# $Id$ +# +# IPTC/NAA file handling +# +# history: +# 1995-10-01 fl Created +# 1998-03-09 fl Cleaned up and added to PIL +# 2002-06-18 fl Added getiptcinfo helper +# +# Copyright (c) Secret Labs AB 1997-2002. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + + +import Image, ImageFile +import os, tempfile + + +COMPRESSION = { + 1: "raw", + 5: "jpeg" +} + +PAD = chr(0) * 4 + +# +# Helpers + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + +def i(c): + return i32((PAD + c)[-4:]) + +def dump(c): + for i in c: + print "%02x" % ord(i), + print + +## +# Image plugin for IPTC/NAA datastreams. To read IPTC/NAA fields +# from TIFF and JPEG files, use the getiptcinfo function. + +class IptcImageFile(ImageFile.ImageFile): + + format = "IPTC" + format_description = "IPTC/NAA" + + def getint(self, key): + return i(self.info[key]) + + def field(self): + # + # get a IPTC field header + s = self.fp.read(5) + if not len(s): + return None, 0 + + tag = ord(s[1]), ord(s[2]) + + # syntax + if ord(s[0]) != 0x1C or tag[0] < 1 or tag[0] > 9: + raise SyntaxError, "invalid IPTC/NAA file" + + # field size + size = ord(s[3]) + if size > 132: + raise IOError, "illegal field length in IPTC/NAA file" + elif size == 128: + size = 0 + elif size > 128: + size = i(self.fp.read(size-128)) + else: + size = i16(s[3:]) + + return tag, size + + def _is_raw(self, offset, size): + # + # check if the file can be mapped + + # DISABLED: the following only slows things down... + return 0 + + self.fp.seek(offset) + t, sz = self.field() + if sz != size[0]: + return 0 + y = 1 + while 1: + self.fp.seek(sz, 1) + t, s = self.field() + if t != (8, 10): + break + if s != sz: + return 0 + y = y + 1 + return y == size[1] + + def _open(self): + + # load descriptive fields + while 1: + offset = self.fp.tell() + tag, size = self.field() + if not tag or tag == (8,10): + break + if size: + tagdata = self.fp.read(size) + else: + tagdata = None + if tag in self.info.keys(): + if isinstance(self.info[tag], list): + self.info[tag].append(tagdata) + else: + self.info[tag] = [self.info[tag], tagdata] + else: + self.info[tag] = tagdata + + # print tag, self.info[tag] + + # mode + layers = ord(self.info[(3,60)][0]) + component = ord(self.info[(3,60)][1]) + if self.info.has_key((3,65)): + id = ord(self.info[(3,65)][0])-1 + else: + id = 0 + if layers == 1 and not component: + self.mode = "L" + elif layers == 3 and component: + self.mode = "RGB"[id] + elif layers == 4 and component: + self.mode = "CMYK"[id] + + # size + self.size = self.getint((3,20)), self.getint((3,30)) + + # compression + try: + compression = COMPRESSION[self.getint((3,120))] + except KeyError: + raise IOError, "Unknown IPTC image compression" + + # tile + if tag == (8,10): + if compression == "raw" and self._is_raw(offset, self.size): + self.tile = [(compression, (offset, size + 5, -1), + (0, 0, self.size[0], self.size[1]))] + else: + self.tile = [("iptc", (compression, offset), + (0, 0, self.size[0], self.size[1]))] + + def load(self): + + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + type, tile, box = self.tile[0] + + encoding, offset = tile + + self.fp.seek(offset) + + # Copy image data to temporary file + outfile = tempfile.mktemp() + o = open(outfile, "wb") + if encoding == "raw": + # To simplify access to the extracted file, + # prepend a PPM header + o.write("P5\n%d %d\n255\n" % self.size) + while 1: + type, size = self.field() + if type != (8, 10): + break + while size > 0: + s = self.fp.read(min(size, 8192)) + if not s: + break + o.write(s) + size = size - len(s) + o.close() + + try: + try: + # fast + self.im = Image.core.open_ppm(outfile) + except: + # slightly slower + im = Image.open(outfile) + im.load() + self.im = im.im + finally: + try: os.unlink(outfile) + except: pass + + +Image.register_open("IPTC", IptcImageFile) + +Image.register_extension("IPTC", ".iim") + +## +# Get IPTC information from TIFF, JPEG, or IPTC file. +# +# @param im An image containing IPTC data. +# @return A dictionary containing IPTC information, or None if +# no IPTC information block was found. + +def getiptcinfo(im): + + import TiffImagePlugin, JpegImagePlugin + import StringIO + + data = None + + if isinstance(im, IptcImageFile): + # return info dictionary right away + return im.info + + elif isinstance(im, JpegImagePlugin.JpegImageFile): + # extract the IPTC/NAA resource + try: + app = im.app["APP13"] + if app[:14] == "Photoshop 3.0\x00": + app = app[14:] + # parse the image resource block + offset = 0 + while app[offset:offset+4] == "8BIM": + offset = offset + 4 + # resource code + code = JpegImagePlugin.i16(app, offset) + offset = offset + 2 + # resource name (usually empty) + name_len = ord(app[offset]) + name = app[offset+1:offset+1+name_len] + offset = 1 + offset + name_len + if offset & 1: + offset = offset + 1 + # resource data block + size = JpegImagePlugin.i32(app, offset) + offset = offset + 4 + if code == 0x0404: + # 0x0404 contains IPTC/NAA data + data = app[offset:offset+size] + break + offset = offset + size + if offset & 1: + offset = offset + 1 + except (AttributeError, KeyError): + pass + + elif isinstance(im, TiffImagePlugin.TiffImageFile): + # get raw data from the IPTC/NAA tag (PhotoShop tags the data + # as 4-byte integers, so we cannot use the get method...) + try: + type, data = im.tag.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] + except (AttributeError, KeyError): + pass + + if data is None: + return None # no properties + + # create an IptcImagePlugin object without initializing it + class FakeImage: + pass + im = FakeImage() + im.__class__ = IptcImageFile + + # parse the IPTC information chunk + im.info = {} + im.fp = StringIO.StringIO(data) + + try: + im._open() + except (IndexError, KeyError): + pass # expected failure + + return im.info diff --git a/PIL/JpegImagePlugin.py b/PIL/JpegImagePlugin.py new file mode 100644 index 000000000..933abf3c2 --- /dev/null +++ b/PIL/JpegImagePlugin.py @@ -0,0 +1,492 @@ +# +# The Python Imaging Library. +# $Id$ +# +# JPEG (JFIF) file handling +# +# See "Digital Compression and Coding of Continous-Tone Still Images, +# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) +# +# History: +# 1995-09-09 fl Created +# 1995-09-13 fl Added full parser +# 1996-03-25 fl Added hack to use the IJG command line utilities +# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug +# 1996-05-28 fl Added draft support, JFIF version (0.1) +# 1996-12-30 fl Added encoder options, added progression property (0.2) +# 1997-08-27 fl Save mode 1 images as BW (0.3) +# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) +# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) +# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) +# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) +# 2003-04-25 fl Added experimental EXIF decoder (0.5) +# 2003-06-06 fl Added experimental EXIF GPSinfo decoder +# 2003-09-13 fl Extract COM markers +# 2009-09-06 fl Added icc_profile support (from Florian Hoech) +# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) +# 2009-03-08 fl Added subsampling support (from Justin Huff). +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import array, struct +import string +import Image, ImageFile + +def i16(c,o=0): + return ord(c[o+1]) + (ord(c[o])<<8) + +def i32(c,o=0): + return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) + +# +# Parser + +def Skip(self, marker): + n = i16(self.fp.read(2))-2 + ImageFile._safe_read(self.fp, n) + +def APP(self, marker): + # + # Application marker. Store these in the APP dictionary. + # Also look for well-known application markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + app = "APP%d" % (marker&15) + + self.app[app] = s # compatibility + self.applist.append((app, s)) + + if marker == 0xFFE0 and s[:4] == "JFIF": + # extract JFIF information + self.info["jfif"] = version = i16(s, 5) # version + self.info["jfif_version"] = divmod(version, 256) + # extract JFIF properties + try: + jfif_unit = ord(s[7]) + jfif_density = i16(s, 8), i16(s, 10) + except: + pass + else: + if jfif_unit == 1: + self.info["dpi"] = jfif_density + self.info["jfif_unit"] = jfif_unit + self.info["jfif_density"] = jfif_density + elif marker == 0xFFE1 and s[:5] == "Exif\0": + # extract Exif information (incomplete) + self.info["exif"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:5] == "FPXR\0": + # extract FlashPix information (incomplete) + self.info["flashpix"] = s # FIXME: value will change + elif marker == 0xFFE2 and s[:12] == "ICC_PROFILE\0": + # Since an ICC profile can be larger than the maximum size of + # a JPEG marker (64K), we need provisions to split it into + # multiple markers. The format defined by the ICC specifies + # one or more APP2 markers containing the following data: + # Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + # Marker sequence number 1, 2, etc (1 byte) + # Number of markers Total of APP2's used (1 byte) + # Profile data (remainder of APP2 data) + # Decoders should use the marker sequence numbers to + # reassemble the profile, rather than assuming that the APP2 + # markers appear in the correct sequence. + self.icclist.append(s) + elif marker == 0xFFEE and s[:5] == "Adobe": + self.info["adobe"] = i16(s, 5) + # extract Adobe custom properties + try: + adobe_transform = ord(s[1]) + except: + pass + else: + self.info["adobe_transform"] = adobe_transform + +def COM(self, marker): + # + # Comment marker. Store these in the APP dictionary. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + + self.app["COM"] = s # compatibility + self.applist.append(("COM", s)) + +def SOF(self, marker): + # + # Start of frame marker. Defines the size and mode of the + # image. JPEG is colour blind, so we use some simple + # heuristics to map the number of layers to an appropriate + # mode. Note that this could be made a bit brighter, by + # looking for JFIF and Adobe APP markers. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + self.size = i16(s[3:]), i16(s[1:]) + + self.bits = ord(s[0]) + if self.bits != 8: + raise SyntaxError("cannot handle %d-bit layers" % self.bits) + + self.layers = ord(s[5]) + if self.layers == 1: + self.mode = "L" + elif self.layers == 3: + self.mode = "RGB" + elif self.layers == 4: + self.mode = "CMYK" + else: + raise SyntaxError("cannot handle %d-layer images" % self.layers) + + if marker in [0xFFC2, 0xFFC6, 0xFFCA, 0xFFCE]: + self.info["progressive"] = self.info["progression"] = 1 + + if self.icclist: + # fixup icc profile + self.icclist.sort() # sort by sequence number + if ord(self.icclist[0][13]) == len(self.icclist): + profile = [] + for p in self.icclist: + profile.append(p[14:]) + icc_profile = string.join(profile, "") + else: + icc_profile = None # wrong number of fragments + self.info["icc_profile"] = icc_profile + self.icclist = None + + for i in range(6, len(s), 3): + t = s[i:i+3] + # 4-tuples: id, vsamp, hsamp, qtable + self.layer.append((t[0], ord(t[1])/16, ord(t[1])&15, ord(t[2]))) + +def DQT(self, marker): + # + # Define quantization table. Support baseline 8-bit tables + # only. Note that there might be more than one table in + # each marker. + + # FIXME: The quantization tables can be used to estimate the + # compression quality. + + n = i16(self.fp.read(2))-2 + s = ImageFile._safe_read(self.fp, n) + while len(s): + if len(s) < 65: + raise SyntaxError("bad quantization table marker") + v = ord(s[0]) + if v/16 == 0: + self.quantization[v&15] = array.array("b", s[1:65]) + s = s[65:] + else: + return # FIXME: add code to read 16-bit tables! + # raise SyntaxError, "bad quantization table element size" + + +# +# JPEG marker table + +MARKER = { + 0xFFC0: ("SOF0", "Baseline DCT", SOF), + 0xFFC1: ("SOF1", "Extended Sequential DCT", SOF), + 0xFFC2: ("SOF2", "Progressive DCT", SOF), + 0xFFC3: ("SOF3", "Spatial lossless", SOF), + 0xFFC4: ("DHT", "Define Huffman table", Skip), + 0xFFC5: ("SOF5", "Differential sequential DCT", SOF), + 0xFFC6: ("SOF6", "Differential progressive DCT", SOF), + 0xFFC7: ("SOF7", "Differential spatial", SOF), + 0xFFC8: ("JPG", "Extension", None), + 0xFFC9: ("SOF9", "Extended sequential DCT (AC)", SOF), + 0xFFCA: ("SOF10", "Progressive DCT (AC)", SOF), + 0xFFCB: ("SOF11", "Spatial lossless DCT (AC)", SOF), + 0xFFCC: ("DAC", "Define arithmetic coding conditioning", Skip), + 0xFFCD: ("SOF13", "Differential sequential DCT (AC)", SOF), + 0xFFCE: ("SOF14", "Differential progressive DCT (AC)", SOF), + 0xFFCF: ("SOF15", "Differential spatial (AC)", SOF), + 0xFFD0: ("RST0", "Restart 0", None), + 0xFFD1: ("RST1", "Restart 1", None), + 0xFFD2: ("RST2", "Restart 2", None), + 0xFFD3: ("RST3", "Restart 3", None), + 0xFFD4: ("RST4", "Restart 4", None), + 0xFFD5: ("RST5", "Restart 5", None), + 0xFFD6: ("RST6", "Restart 6", None), + 0xFFD7: ("RST7", "Restart 7", None), + 0xFFD8: ("SOI", "Start of image", None), + 0xFFD9: ("EOI", "End of image", None), + 0xFFDA: ("SOS", "Start of scan", Skip), + 0xFFDB: ("DQT", "Define quantization table", DQT), + 0xFFDC: ("DNL", "Define number of lines", Skip), + 0xFFDD: ("DRI", "Define restart interval", Skip), + 0xFFDE: ("DHP", "Define hierarchical progression", SOF), + 0xFFDF: ("EXP", "Expand reference component", Skip), + 0xFFE0: ("APP0", "Application segment 0", APP), + 0xFFE1: ("APP1", "Application segment 1", APP), + 0xFFE2: ("APP2", "Application segment 2", APP), + 0xFFE3: ("APP3", "Application segment 3", APP), + 0xFFE4: ("APP4", "Application segment 4", APP), + 0xFFE5: ("APP5", "Application segment 5", APP), + 0xFFE6: ("APP6", "Application segment 6", APP), + 0xFFE7: ("APP7", "Application segment 7", APP), + 0xFFE8: ("APP8", "Application segment 8", APP), + 0xFFE9: ("APP9", "Application segment 9", APP), + 0xFFEA: ("APP10", "Application segment 10", APP), + 0xFFEB: ("APP11", "Application segment 11", APP), + 0xFFEC: ("APP12", "Application segment 12", APP), + 0xFFED: ("APP13", "Application segment 13", APP), + 0xFFEE: ("APP14", "Application segment 14", APP), + 0xFFEF: ("APP15", "Application segment 15", APP), + 0xFFF0: ("JPG0", "Extension 0", None), + 0xFFF1: ("JPG1", "Extension 1", None), + 0xFFF2: ("JPG2", "Extension 2", None), + 0xFFF3: ("JPG3", "Extension 3", None), + 0xFFF4: ("JPG4", "Extension 4", None), + 0xFFF5: ("JPG5", "Extension 5", None), + 0xFFF6: ("JPG6", "Extension 6", None), + 0xFFF7: ("JPG7", "Extension 7", None), + 0xFFF8: ("JPG8", "Extension 8", None), + 0xFFF9: ("JPG9", "Extension 9", None), + 0xFFFA: ("JPG10", "Extension 10", None), + 0xFFFB: ("JPG11", "Extension 11", None), + 0xFFFC: ("JPG12", "Extension 12", None), + 0xFFFD: ("JPG13", "Extension 13", None), + 0xFFFE: ("COM", "Comment", COM) +} + + +def _accept(prefix): + return prefix[0] == "\377" + +## +# Image plugin for JPEG and JFIF images. + +class JpegImageFile(ImageFile.ImageFile): + + format = "JPEG" + format_description = "JPEG (ISO 10918)" + + def _open(self): + + s = self.fp.read(1) + + if ord(s[0]) != 255: + raise SyntaxError("not a JPEG file") + + # Create attributes + self.bits = self.layers = 0 + + # JPEG specifics (internal) + self.layer = [] + self.huffman_dc = {} + self.huffman_ac = {} + self.quantization = {} + self.app = {} # compatibility + self.applist = [] + self.icclist = [] + + while 1: + + s = s + self.fp.read(1) + + i = i16(s) + + if MARKER.has_key(i): + name, description, handler = MARKER[i] + # print hex(i), name, description + if handler is not None: + handler(self, i) + if i == 0xFFDA: # start of scan + rawmode = self.mode + if self.mode == "CMYK": + rawmode = "CMYK;I" # assume adobe conventions + self.tile = [("jpeg", (0,0) + self.size, 0, (rawmode, ""))] + # self.__offset = self.fp.tell() + break + s = self.fp.read(1) + elif i == 0 or i == 65535: + # padded marker or junk; move on + s = "\xff" + else: + raise SyntaxError("no marker found") + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + scale = 0 + + if a[0] == "RGB" and mode in ["L", "YCbCr"]: + self.mode = mode + a = mode, "" + + if size: + scale = max(self.size[0] / size[0], self.size[1] / size[1]) + for s in [8, 4, 2, 1]: + if scale >= s: + break + e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] + self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + scale = s + + self.tile = [(d, e, o, a)] + self.decoderconfig = (scale, 1) + + return self + + def load_djpeg(self): + + # ALTERNATIVE: handle JPEGs via the IJG command line utilities + + import tempfile, os + file = tempfile.mktemp() + os.system("djpeg %s >%s" % (self.filename, file)) + + try: + self.im = Image.core.open_ppm(file) + finally: + try: os.unlink(file) + except: pass + + self.mode = self.im.mode + self.size = self.im.size + + self.tile = [] + + def _getexif(self): + # Extract EXIF information. This method is highly experimental, + # and is likely to be replaced with something better in a future + # version. + import TiffImagePlugin, StringIO + def fixup(value): + if len(value) == 1: + return value[0] + return value + # The EXIF record consists of a TIFF file embedded in a JPEG + # application marker (!). + try: + data = self.info["exif"] + except KeyError: + return None + file = StringIO.StringIO(data[6:]) + head = file.read(8) + exif = {} + # process dictionary + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = fixup(value) + # get exif extension + try: + file.seek(exif[0x8769]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + for key, value in info.items(): + exif[key] = fixup(value) + # get gpsinfo extension + try: + file.seek(exif[0x8825]) + except KeyError: + pass + else: + info = TiffImagePlugin.ImageFileDirectory(head) + info.load(file) + exif[0x8825] = gps = {} + for key, value in info.items(): + gps[key] = fixup(value) + return exif + +# -------------------------------------------------------------------- +# stuff to save JPEG files + +RAWMODE = { + "1": "L", + "L": "L", + "RGB": "RGB", + "RGBA": "RGB", + "RGBX": "RGB", + "CMYK": "CMYK;I", # assume adobe conventions + "YCbCr": "YCbCr", +} + +def _save(im, fp, filename): + + try: + rawmode = RAWMODE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as JPEG" % im.mode) + + info = im.encoderinfo + + dpi = info.get("dpi", (0, 0)) + + subsampling = info.get("subsampling", -1) + if subsampling == "4:4:4": + subsampling = 0 + elif subsampling == "4:2:2": + subsampling = 1 + elif subsampling == "4:1:1": + subsampling = 2 + + extra = "" + + icc_profile = info.get("icc_profile") + if icc_profile: + ICC_OVERHEAD_LEN = 14 + MAX_BYTES_IN_MARKER = 65533 + MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN + markers = [] + while icc_profile: + markers.append(icc_profile[:MAX_DATA_BYTES_IN_MARKER]) + icc_profile = icc_profile[MAX_DATA_BYTES_IN_MARKER:] + i = 1 + for marker in markers: + size = struct.pack(">H", 2 + ICC_OVERHEAD_LEN + len(marker)) + extra = extra + ("\xFF\xE2" + size + "ICC_PROFILE\0" + chr(i) + chr(len(markers)) + marker) + i = i + 1 + + # get keyword arguments + im.encoderconfig = ( + info.get("quality", 0), + # "progressive" is the official name, but older documentation + # says "progression" + # FIXME: issue a warning if the wrong form is used (post-1.1.7) + info.has_key("progressive") or info.has_key("progression"), + info.get("smooth", 0), + info.has_key("optimize"), + info.get("streamtype", 0), + dpi[0], dpi[1], + subsampling, + extra, + ) + + ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)]) + +def _save_cjpeg(im, fp, filename): + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + import os + file = im._dump() + os.system("cjpeg %s >%s" % (file, filename)) + try: os.unlink(file) + except: pass + +# -------------------------------------------------------------------q- +# Registry stuff + +Image.register_open("JPEG", JpegImageFile, _accept) +Image.register_save("JPEG", _save) + +Image.register_extension("JPEG", ".jfif") +Image.register_extension("JPEG", ".jpe") +Image.register_extension("JPEG", ".jpg") +Image.register_extension("JPEG", ".jpeg") + +Image.register_mime("JPEG", "image/jpeg") diff --git a/PIL/McIdasImagePlugin.py b/PIL/McIdasImagePlugin.py new file mode 100644 index 000000000..62ee52cf8 --- /dev/null +++ b/PIL/McIdasImagePlugin.py @@ -0,0 +1,70 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Basic McIdas support for PIL +# +# History: +# 1997-05-05 fl Created (8-bit images only) +# 2009-03-08 fl Added 16/32-bit support. +# +# Thanks to Richard Jones and Craig Swank for specs and samples. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +import struct +import Image, ImageFile + +def _accept(s): + return s[:8] == "\x00\x00\x00\x00\x00\x00\x00\x04" + +## +# Image plugin for McIdas area images. + +class McIdasImageFile(ImageFile.ImageFile): + + format = "MCIDAS" + format_description = "McIdas area file" + + def _open(self): + + # parse area file directory + s = self.fp.read(256) + if not _accept(s) or len(s) != 256: + raise SyntaxError("not an McIdas area file") + + self.area_descriptor_raw = s + self.area_descriptor = w = [0] + list(struct.unpack("!64i", s)) + + # get mode + if w[11] == 1: + mode = rawmode = "L" + elif w[11] == 2: + # FIXME: add memory map support + mode = "I"; rawmode = "I;16B" + elif w[11] == 4: + # FIXME: add memory map support + mode = "I"; rawmode = "I;32B" + else: + raise SyntaxError("unsupported McIdas format") + + self.mode = mode + self.size = w[10], w[9] + + offset = w[34] + w[15] + stride = w[15] + w[10]*w[11]*w[14] + + self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] + +# -------------------------------------------------------------------- +# registry + +Image.register_open("MCIDAS", McIdasImageFile, _accept) + +# no default extension diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py new file mode 100644 index 000000000..b1a3bba79 --- /dev/null +++ b/PIL/MicImagePlugin.py @@ -0,0 +1,95 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Microsoft Image Composer support for PIL +# +# Notes: +# uses TiffImagePlugin.py to read the actual image streams +# +# History: +# 97-01-20 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +import Image, TiffImagePlugin +from OleFileIO import * + + +# +# -------------------------------------------------------------------- + + +def _accept(prefix): + return prefix[:8] == MAGIC + +## +# Image plugin for Microsoft's Image Composer file format. + +class MicImageFile(TiffImagePlugin.TiffImageFile): + + format = "MIC" + format_description = "Microsoft Image Composer" + + def _open(self): + + # read the OLE directory and see if this is a likely + # to be a Microsoft Image Composer file + + try: + self.ole = OleFileIO(self.fp) + except IOError: + raise SyntaxError, "not an MIC file; invalid OLE file" + + # find ACI subfiles with Image members (maybe not the + # best way to identify MIC files, but what the... ;-) + + self.images = [] + for file in self.ole.listdir(): + if file[1:] and file[0][-4:] == ".ACI" and file[1] == "Image": + self.images.append(file) + + # if we didn't find any images, this is probably not + # an MIC file. + if not self.images: + raise SyntaxError, "not an MIC file; no image entries" + + self.__fp = self.fp + self.frame = 0 + + if len(self.images) > 1: + self.category = Image.CONTAINER + + self.seek(0) + + def seek(self, frame): + + try: + filename = self.images[frame] + except IndexError: + raise EOFError, "no such frame" + + self.fp = self.ole.openstream(filename) + + TiffImagePlugin.TiffImageFile._open(self) + + self.frame = frame + + def tell(self): + + return self.frame + +# +# -------------------------------------------------------------------- + +Image.register_open("MIC", MicImageFile, _accept) + +Image.register_extension("MIC", ".mic") diff --git a/PIL/MpegImagePlugin.py b/PIL/MpegImagePlugin.py new file mode 100644 index 000000000..c02edee82 --- /dev/null +++ b/PIL/MpegImagePlugin.py @@ -0,0 +1,82 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MPEG file handling +# +# History: +# 95-09-09 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +import Image, ImageFile + +# +# Bitstream parser + +class BitStream: + + def __init__(self, fp): + self.fp = fp + self.bits = 0 + self.bitbuffer = 0 + + def next(self): + return ord(self.fp.read(1)) + + def peek(self, bits): + while self.bits < bits: + c = self.next() + if c < 0: + self.bits = 0 + continue + self.bitbuffer = (self.bitbuffer << 8) + c + self.bits = self.bits + 8 + return self.bitbuffer >> (self.bits - bits) & (1L << bits) - 1 + + def skip(self, bits): + while self.bits < bits: + self.bitbuffer = (self.bitbuffer << 8) + ord(self.fp.read(1)) + self.bits = self.bits + 8 + self.bits = self.bits - bits + + def read(self, bits): + v = self.peek(bits) + self.bits = self.bits - bits + return v + +## +# Image plugin for MPEG streams. This plugin can identify a stream, +# but it cannot read it. + +class MpegImageFile(ImageFile.ImageFile): + + format = "MPEG" + format_description = "MPEG" + + def _open(self): + + s = BitStream(self.fp) + + if s.read(32) != 0x1B3: + raise SyntaxError, "not an MPEG file" + + self.mode = "RGB" + self.size = s.read(12), s.read(12) + + +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open("MPEG", MpegImageFile) + +Image.register_extension("MPEG", ".mpg") +Image.register_extension("MPEG", ".mpeg") + +Image.register_mime("MPEG", "video/mpeg") diff --git a/PIL/MspImagePlugin.py b/PIL/MspImagePlugin.py new file mode 100644 index 000000000..9dac36b47 --- /dev/null +++ b/PIL/MspImagePlugin.py @@ -0,0 +1,103 @@ +# +# The Python Imaging Library. +# $Id$ +# +# MSP file handling +# +# This is the format used by the Paint program in Windows 1 and 2. +# +# History: +# 95-09-05 fl Created +# 97-01-03 fl Read/write MSP images +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + +import Image, ImageFile + + +# +# read MSP files + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def _accept(prefix): + return prefix[:4] in ["DanM", "LinS"] + +## +# Image plugin for Windows MSP images. This plugin supports both +# uncompressed (Windows 1.0). + +class MspImageFile(ImageFile.ImageFile): + + format = "MSP" + format_description = "Windows Paint" + + def _open(self): + + # Header + s = self.fp.read(32) + if s[:4] not in ["DanM", "LinS"]: + raise SyntaxError, "not an MSP file" + + # Header checksum + sum = 0 + for i in range(0, 32, 2): + sum = sum ^ i16(s[i:i+2]) + if sum != 0: + raise SyntaxError, "bad MSP checksum" + + self.mode = "1" + self.size = i16(s[4:]), i16(s[6:]) + + if s[:4] == "DanM": + self.tile = [("raw", (0,0)+self.size, 32, ("1", 0, 1))] + else: + self.tile = [("msp", (0,0)+self.size, 32+2*self.size[1], None)] + +# +# write MSP files (uncompressed only) + +def o16(i): + return chr(i&255) + chr(i>>8&255) + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError, "cannot write mode %s as MSP" % im.mode + + # create MSP header + header = [0] * 16 + + header[0], header[1] = i16("Da"), i16("nM") # version 1 + header[2], header[3] = im.size + header[4], header[5] = 1, 1 + header[6], header[7] = 1, 1 + header[8], header[9] = im.size + + sum = 0 + for h in header: + sum = sum ^ h + header[12] = sum # FIXME: is this the right field? + + # header + for h in header: + fp.write(o16(h)) + + # image body + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 32, ("1", 0, 1))]) + +# +# registry + +Image.register_open("MSP", MspImageFile, _accept) +Image.register_save("MSP", _save) + +Image.register_extension("MSP", ".msp") diff --git a/PIL/OleFileIO.py b/PIL/OleFileIO.py new file mode 100644 index 000000000..36e598a9b --- /dev/null +++ b/PIL/OleFileIO.py @@ -0,0 +1,528 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# stuff to deal with OLE2 Structured Storage files. this module is +# used by PIL to read Image Composer and FlashPix files, but can also +# be used to read other files of this type. +# +# History: +# 1997-01-20 fl Created +# 1997-01-22 fl Fixed 64-bit portability quirk +# 2003-09-09 fl Fixed typo in OleFileIO.loadfat (noted by Daniel Haertle) +# 2004-02-29 fl Changed long hex constants to signed integers +# +# Notes: +# FIXME: sort out sign problem (eliminate long hex constants) +# FIXME: change filename to use "a/b/c" instead of ["a", "b", "c"] +# FIXME: provide a glob mechanism function (using fnmatchcase) +# +# Literature: +# +# "FlashPix Format Specification, Appendix A", Kodak and Microsoft, +# September 1996. +# +# Quotes: +# +# "If this document and functionality of the Software conflict, +# the actual functionality of the Software represents the correct +# functionality" -- Microsoft, in the OLE format specification +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +import string, StringIO + + +def i16(c, o = 0): + return ord(c[o])+(ord(c[o+1])<<8) + +def i32(c, o = 0): + return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) + + +MAGIC = '\320\317\021\340\241\261\032\341' + +# +# -------------------------------------------------------------------- +# property types + +VT_EMPTY=0; VT_NULL=1; VT_I2=2; VT_I4=3; VT_R4=4; VT_R8=5; VT_CY=6; +VT_DATE=7; VT_BSTR=8; VT_DISPATCH=9; VT_ERROR=10; VT_BOOL=11; +VT_VARIANT=12; VT_UNKNOWN=13; VT_DECIMAL=14; VT_I1=16; VT_UI1=17; +VT_UI2=18; VT_UI4=19; VT_I8=20; VT_UI8=21; VT_INT=22; VT_UINT=23; +VT_VOID=24; VT_HRESULT=25; VT_PTR=26; VT_SAFEARRAY=27; VT_CARRAY=28; +VT_USERDEFINED=29; VT_LPSTR=30; VT_LPWSTR=31; VT_FILETIME=64; +VT_BLOB=65; VT_STREAM=66; VT_STORAGE=67; VT_STREAMED_OBJECT=68; +VT_STORED_OBJECT=69; VT_BLOB_OBJECT=70; VT_CF=71; VT_CLSID=72; +VT_VECTOR=0x1000; + +# map property id to name (for debugging purposes) + +VT = {} +for k, v in vars().items(): + if k[:3] == "VT_": + VT[v] = k + +# +# -------------------------------------------------------------------- +# Some common document types (root.clsid fields) + +WORD_CLSID = "00020900-0000-0000-C000-000000000046" + + +# +# -------------------------------------------------------------------- + +class _OleStream(StringIO.StringIO): + + """OLE2 Stream + + Returns a read-only file object which can be used to read + the contents of a OLE stream. To open a stream, use the + openstream method in the OleFile class. + + This function can be used with either ordinary streams, + or ministreams, depending on the offset, sectorsize, and + fat table arguments. + """ + + # FIXME: should store the list of sects obtained by following + # the fat chain, and load new sectors on demand instead of + # loading it all in one go. + + def __init__(self, fp, sect, size, offset, sectorsize, fat): + + data = [] + + while sect != -2: # 0xFFFFFFFEL: + fp.seek(offset + sectorsize * sect) + data.append(fp.read(sectorsize)) + sect = fat[sect] + + data = string.join(data, "") + + # print len(data), size + + StringIO.StringIO.__init__(self, data[:size]) + +# +# -------------------------------------------------------------------- + +# FIXME: should add a counter in here to avoid looping forever +# if the tree is broken. + +class _OleDirectoryEntry: + + """OLE2 Directory Entry + + Encapsulates a stream directory entry. Note that the + constructor builds a tree of all subentries, so we only + have to call it with the root object. + """ + + def __init__(self, sidlist, sid): + + # store directory parameters. the caller provides + # a complete list of directory entries, as read from + # the directory stream. + + name, type, sect, size, sids, clsid = sidlist[sid] + + self.sid = sid + self.name = name + self.type = type # 1=storage 2=stream + self.sect = sect + self.size = size + self.clsid = clsid + + # process child nodes, if any + + self.kids = [] + + sid = sidlist[sid][4][2] + + if sid != -1: + + # the directory entries are organized as a red-black tree. + # the following piece of code does an ordered traversal of + # such a tree (at least that's what I hope ;-) + + stack = [self.sid] + + # start at leftmost position + + left, right, child = sidlist[sid][4] + + while left != -1: # 0xFFFFFFFFL: + stack.append(sid) + sid = left + left, right, child = sidlist[sid][4] + + while sid != self.sid: + + self.kids.append(_OleDirectoryEntry(sidlist, sid)) + + # try to move right + left, right, child = sidlist[sid][4] + if right != -1: # 0xFFFFFFFFL: + # and then back to the left + sid = right + while 1: + left, right, child = sidlist[sid][4] + if left == -1: # 0xFFFFFFFFL: + break + stack.append(sid) + sid = left + else: + # couldn't move right; move up instead + while 1: + ptr = stack[-1] + del stack[-1] + left, right, child = sidlist[ptr][4] + if right != sid: + break + sid = right + left, right, child = sidlist[sid][4] + if right != ptr: + sid = ptr + + # in the OLE file, entries are sorted on (length, name). + # for convenience, we sort them on name instead. + + self.kids.sort() + + def __cmp__(self, other): + "Compare entries by name" + + return cmp(self.name, other.name) + + def dump(self, tab = 0): + "Dump this entry, and all its subentries (for debug purposes only)" + + TYPES = ["(invalid)", "(storage)", "(stream)", "(lockbytes)", + "(property)", "(root)"] + + print " "*tab + repr(self.name), TYPES[self.type], + if self.type in (2, 5): + print self.size, "bytes", + print + if self.type in (1, 5) and self.clsid: + print " "*tab + "{%s}" % self.clsid + + for kid in self.kids: + kid.dump(tab + 2) + +# +# -------------------------------------------------------------------- + +## +# This class encapsulates the interface to an OLE 2 structured +# storage file. Use the {@link listdir} and {@link openstream} +# methods to access the contents of this file. + +class OleFileIO: + """OLE container object + + This class encapsulates the interface to an OLE 2 structured + storage file. Use the listdir and openstream methods to access + the contents of this file. + + Object names are given as a list of strings, one for each subentry + level. The root entry should be omitted. For example, the following + code extracts all image streams from a Microsoft Image Composer file: + + ole = OleFileIO("fan.mic") + + for entry in ole.listdir(): + if entry[1:2] == "Image": + fin = ole.openstream(entry) + fout = open(entry[0:1], "wb") + while 1: + s = fin.read(8192) + if not s: + break + fout.write(s) + + You can use the viewer application provided with the Python Imaging + Library to view the resulting files (which happens to be standard + TIFF files). + """ + + def __init__(self, filename = None): + + if filename: + self.open(filename) + + ## + # Open an OLE2 file. + + def open(self, filename): + """Open an OLE2 file""" + + if type(filename) == type(""): + self.fp = open(filename, "rb") + else: + self.fp = filename + + header = self.fp.read(512) + + if len(header) != 512 or header[:8] != MAGIC: + raise IOError, "not an OLE2 structured storage file" + + # file clsid (probably never used, so we don't store it) + clsid = self._clsid(header[8:24]) + + # FIXME: could check version and byte order fields + + self.sectorsize = 1 << i16(header, 30) + self.minisectorsize = 1 << i16(header, 32) + + self.minisectorcutoff = i32(header, 56) + + # Load file allocation tables + self.loadfat(header) + + # Load direcory. This sets both the sidlist (ordered by id) + # and the root (ordered by hierarchy) members. + self.loaddirectory(i32(header, 48)) + + self.ministream = None + self.minifatsect = i32(header, 60) + + def loadfat(self, header): + # Load the FAT table. The header contains a sector numbers + # for the first 109 FAT sectors. Additional sectors are + # described by DIF blocks (FIXME: not yet implemented) + + sect = header[76:512] + fat = [] + for i in range(0, len(sect), 4): + ix = i32(sect, i) + if ix == -2 or ix == -1: # ix == 0xFFFFFFFEL or ix == 0xFFFFFFFFL: + break + s = self.getsect(ix) + fat = fat + map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + self.fat = fat + + def loadminifat(self): + # Load the MINIFAT table. This is stored in a standard sub- + # stream, pointed to by a header field. + + s = self._open(self.minifatsect).read() + + self.minifat = map(lambda i, s=s: i32(s, i), range(0, len(s), 4)) + + def getsect(self, sect): + # Read given sector + + self.fp.seek(512 + self.sectorsize * sect) + return self.fp.read(self.sectorsize) + + def _unicode(self, s): + # Map unicode string to Latin 1 + + # FIXME: some day, Python will provide an official way to handle + # Unicode strings, but until then, this will have to do... + return filter(ord, s) + + def loaddirectory(self, sect): + # Load the directory. The directory is stored in a standard + # substream, independent of its size. + + # read directory stream + fp = self._open(sect) + + # create list of sid entries + self.sidlist = [] + while 1: + entry = fp.read(128) + if not entry: + break + type = ord(entry[66]) + name = self._unicode(entry[0:0+i16(entry, 64)]) + ptrs = i32(entry, 68), i32(entry, 72), i32(entry, 76) + sect, size = i32(entry, 116), i32(entry, 120) + clsid = self._clsid(entry[80:96]) + self.sidlist.append((name, type, sect, size, ptrs, clsid)) + + # create hierarchical list of directory entries + self.root = _OleDirectoryEntry(self.sidlist, 0) + + def dumpdirectory(self): + # Dump directory (for debugging only) + + self.root.dump() + + def _clsid(self, clsid): + if clsid == "\0" * len(clsid): + return "" + return (("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % + ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + + tuple(map(ord, clsid[8:16])))) + + def _list(self, files, prefix, node): + # listdir helper + + prefix = prefix + [node.name] + for entry in node.kids: + if entry.kids: + self._list(files, prefix, entry) + else: + files.append(prefix[1:] + [entry.name]) + + def _find(self, filename): + # openstream helper + + node = self.root + for name in filename: + for kid in node.kids: + if kid.name == name: + break + else: + raise IOError, "file not found" + node = kid + return node.sid + + def _open(self, start, size = 0x7FFFFFFF): + # openstream helper. + + if size < self.minisectorcutoff: + # ministream object + if not self.ministream: + self.loadminifat() + self.ministream = self._open(self.sidlist[0][2]) + return _OleStream(self.ministream, start, size, 0, + self.minisectorsize, self.minifat) + + # standard stream + return _OleStream(self.fp, start, size, 512, + self.sectorsize, self.fat) + + ## + # Returns a list of streams stored in this file. + + def listdir(self): + """Return a list of streams stored in this file""" + + files = [] + self._list(files, [], self.root) + return files + + ## + # Opens a stream as a read-only file object. + + def openstream(self, filename): + """Open a stream as a read-only file object""" + + slot = self._find(filename) + name, type, sect, size, sids, clsid = self.sidlist[slot] + if type != 2: + raise IOError, "this file is not a stream" + return self._open(sect, size) + + ## + # Gets a list of properties described in substream. + + def getproperties(self, filename): + """Return properties described in substream""" + + fp = self.openstream(filename) + + data = {} + + # header + s = fp.read(28) + clsid = self._clsid(s[8:24]) + + # format id + s = fp.read(20) + fmtid = self._clsid(s[:16]) + fp.seek(i32(s, 16)) + + # get section + s = "****" + fp.read(i32(fp.read(4))-4) + + for i in range(i32(s, 4)): + + id = i32(s, 8+i*8) + offset = i32(s, 12+i*8) + type = i32(s, offset) + + # test for common types first (should perhaps use + # a dictionary instead?) + + if type == VT_I2: + value = i16(s, offset+4) + if value >= 32768: + value = value - 65536 + elif type == VT_UI2: + value = i16(s, offset+4) + elif type in (VT_I4, VT_ERROR): + value = i32(s, offset+4) + elif type == VT_UI4: + value = i32(s, offset+4) # FIXME + elif type in (VT_BSTR, VT_LPSTR): + count = i32(s, offset+4) + value = s[offset+8:offset+8+count-1] + elif type == VT_BLOB: + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + elif type == VT_LPWSTR: + count = i32(s, offset+4) + value = self._unicode(s[offset+8:offset+8+count*2]) + elif type == VT_FILETIME: + value = long(i32(s, offset+4)) + (long(i32(s, offset+8))<<32) + # FIXME: this is a 64-bit int: "number of 100ns periods + # since Jan 1,1601". Should map this to Python time + value = value / 10000000L # seconds + elif type == VT_UI1: + value = ord(s[offset+4]) + elif type == VT_CLSID: + value = self._clsid(s[offset+4:offset+20]) + elif type == VT_CF: + count = i32(s, offset+4) + value = s[offset+8:offset+8+count] + else: + value = None # everything else yields "None" + + # FIXME: add support for VT_VECTOR + + #print "%08x" % id, repr(value), + #print "(%s)" % VT[i32(s, offset) & 0xFFF] + + data[id] = value + + return data + +# +# -------------------------------------------------------------------- +# This script can be used to dump the directory of any OLE2 structured +# storage file. + +if __name__ == "__main__": + + import sys + + for file in sys.argv[1:]: + try: + ole = OleFileIO(file) + print "-" * 68 + print file + print "-" * 68 + ole.dumpdirectory() + for file in ole.listdir(): + if file[-1][0] == "\005": + print file + props = ole.getproperties(file) + props = props.items() + props.sort() + for k, v in props: + print " ", k, v + except IOError, v: + print "***", "cannot read", file, "-", v diff --git a/PIL/PSDraw.py b/PIL/PSDraw.py new file mode 100644 index 000000000..7309e17b3 --- /dev/null +++ b/PIL/PSDraw.py @@ -0,0 +1,199 @@ +# +# The Python Imaging Library +# $Id$ +# +# simple postscript graphics interface +# +# History: +# 1996-04-20 fl Created +# 1999-01-10 fl Added gsave/grestore to image method +# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) +# +# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import EpsImagePlugin +import string + +## +# Simple Postscript graphics interface. + +class PSDraw: + + def __init__(self, fp=None): + if not fp: + import sys + fp = sys.stdout + self.fp = fp + + def begin_document(self, id = None): + "Write Postscript DSC header" + # FIXME: incomplete + self.fp.write("%!PS-Adobe-3.0\n" + "save\n" + "/showpage { } def\n" + "%%EndComments\n" + "%%BeginDocument\n") + #self.fp.write(ERROR_PS) # debugging! + self.fp.write(EDROFF_PS) + self.fp.write(VDI_PS) + self.fp.write("%%EndProlog\n") + self.isofont = {} + + def end_document(self): + "Write Postscript DSC footer" + self.fp.write("%%EndDocument\n" + "restore showpage\n" + "%%End\n") + if hasattr(self.fp, "flush"): + self.fp.flush() + + def setfont(self, font, size): + if not self.isofont.has_key(font): + # reencode font + self.fp.write("/PSDraw-%s ISOLatin1Encoding /%s E\n" %\ + (font, font)) + self.isofont[font] = 1 + # rough + self.fp.write("/F0 %d /PSDraw-%s F\n" % (size, font)) + + def setink(self, ink): + print "*** NOT YET IMPLEMENTED ***" + + def line(self, xy0, xy1): + xy = xy0 + xy1 + self.fp.write("%d %d %d %d Vl\n" % xy) + + def rectangle(self, box): + self.fp.write("%d %d M %d %d 0 Vr\n" % box) + + def text(self, xy, text): + text = string.joinfields(string.splitfields(text, "("), "\\(") + text = string.joinfields(string.splitfields(text, ")"), "\\)") + xy = xy + (text,) + self.fp.write("%d %d M (%s) S\n" % xy) + + def image(self, box, im, dpi = None): + "Write an PIL image" + # default resolution depends on mode + if not dpi: + if im.mode == "1": + dpi = 200 # fax + else: + dpi = 100 # greyscale + # image size (on paper) + x = float(im.size[0] * 72) / dpi + y = float(im.size[1] * 72) / dpi + # max allowed size + xmax = float(box[2] - box[0]) + ymax = float(box[3] - box[1]) + if x > xmax: + y = y * xmax / x; x = xmax + if y > ymax: + x = x * ymax / y; y = ymax + dx = (xmax - x) / 2 + box[0] + dy = (ymax - y) / 2 + box[1] + self.fp.write("gsave\n%f %f translate\n" % (dx, dy)) + if (x, y) != im.size: + # EpsImagePlugin._save prints the image at (0,0,xsize,ysize) + sx = x / im.size[0] + sy = y / im.size[1] + self.fp.write("%f %f scale\n" % (sx, sy)) + EpsImagePlugin._save(im, self.fp, None, 0) + self.fp.write("\ngrestore\n") + +# -------------------------------------------------------------------- +# Postscript driver + +# +# EDROFF.PS -- Postscript driver for Edroff 2 +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +EDROFF_PS = """\ +/S { show } bind def +/P { moveto show } bind def +/M { moveto } bind def +/X { 0 rmoveto } bind def +/Y { 0 exch rmoveto } bind def +/E { findfont + dup maxlength dict begin + { + 1 index /FID ne { def } { pop pop } ifelse + } forall + /Encoding exch def + dup /FontName exch def + currentdict end definefont pop +} bind def +/F { findfont exch scalefont dup setfont + [ exch /setfont cvx ] cvx bind def +} bind def +""" + +# +# VDI.PS -- Postscript driver for VDI meta commands +# +# History: +# 94-01-25 fl: created (edroff 2.04) +# +# Copyright (c) Fredrik Lundh 1994. +# + +VDI_PS = """\ +/Vm { moveto } bind def +/Va { newpath arcn stroke } bind def +/Vl { moveto lineto stroke } bind def +/Vc { newpath 0 360 arc closepath } bind def +/Vr { exch dup 0 rlineto + exch dup neg 0 exch rlineto + exch neg 0 rlineto + 0 exch rlineto + 100 div setgray fill 0 setgray } bind def +/Tm matrix def +/Ve { Tm currentmatrix pop + translate scale newpath 0 0 .5 0 360 arc closepath + Tm setmatrix +} bind def +/Vf { currentgray exch setgray fill setgray } bind def +""" + +# +# ERROR.PS -- Error handler +# +# History: +# 89-11-21 fl: created (pslist 1.10) +# + +ERROR_PS = """\ +/landscape false def +/errorBUF 200 string def +/errorNL { currentpoint 10 sub exch pop 72 exch moveto } def +errordict begin /handleerror { + initmatrix /Courier findfont 10 scalefont setfont + newpath 72 720 moveto $error begin /newerror false def + (PostScript Error) show errorNL errorNL + (Error: ) show + /errorname load errorBUF cvs show errorNL errorNL + (Command: ) show + /command load dup type /stringtype ne { errorBUF cvs } if show + errorNL errorNL + (VMstatus: ) show + vmstatus errorBUF cvs show ( bytes available, ) show + errorBUF cvs show ( bytes used at level ) show + errorBUF cvs show errorNL errorNL + (Operand stargck: ) show errorNL /ostargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall errorNL + (Execution stargck: ) show errorNL /estargck load { + dup type /stringtype ne { errorBUF cvs } if 72 0 rmoveto show errorNL + } forall + end showpage +} def end +""" diff --git a/PIL/PaletteFile.py b/PIL/PaletteFile.py new file mode 100644 index 000000000..3bbd91327 --- /dev/null +++ b/PIL/PaletteFile.py @@ -0,0 +1,55 @@ +# +# Python Imaging Library +# $Id$ +# +# stuff to read simple, teragon-style palette files +# +# History: +# 97-08-23 fl Created +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +import string + +## +# File handler for Teragon-style palette files. + +class PaletteFile: + + rawmode = "RGB" + + def __init__(self, fp): + + self.palette = map(lambda i: (i, i, i), range(256)) + + while 1: + + s = fp.readline() + + if not s: + break + if s[0] == "#": + continue + if len(s) > 100: + raise SyntaxError, "bad palette file" + + v = map(int, string.split(s)) + try: + [i, r, g, b] = v + except ValueError: + [i, r] = v + g = b = r + + if 0 <= i <= 255: + self.palette[i] = chr(r) + chr(g) + chr(b) + + self.palette = string.join(self.palette, "") + + + def getpalette(self): + + return self.palette, self.rawmode diff --git a/PIL/PalmImagePlugin.py b/PIL/PalmImagePlugin.py new file mode 100644 index 000000000..785023130 --- /dev/null +++ b/PIL/PalmImagePlugin.py @@ -0,0 +1,225 @@ +# +# The Python Imaging Library. +# $Id$ +# + +## +# Image plugin for Palm pixmap images (output only). +## + +__version__ = "1.0" + +import Image, ImageFile + +_Palm8BitColormapValues = ( + ( 255, 255, 255 ), ( 255, 204, 255 ), ( 255, 153, 255 ), ( 255, 102, 255 ), + ( 255, 51, 255 ), ( 255, 0, 255 ), ( 255, 255, 204 ), ( 255, 204, 204 ), + ( 255, 153, 204 ), ( 255, 102, 204 ), ( 255, 51, 204 ), ( 255, 0, 204 ), + ( 255, 255, 153 ), ( 255, 204, 153 ), ( 255, 153, 153 ), ( 255, 102, 153 ), + ( 255, 51, 153 ), ( 255, 0, 153 ), ( 204, 255, 255 ), ( 204, 204, 255 ), + ( 204, 153, 255 ), ( 204, 102, 255 ), ( 204, 51, 255 ), ( 204, 0, 255 ), + ( 204, 255, 204 ), ( 204, 204, 204 ), ( 204, 153, 204 ), ( 204, 102, 204 ), + ( 204, 51, 204 ), ( 204, 0, 204 ), ( 204, 255, 153 ), ( 204, 204, 153 ), + ( 204, 153, 153 ), ( 204, 102, 153 ), ( 204, 51, 153 ), ( 204, 0, 153 ), + ( 153, 255, 255 ), ( 153, 204, 255 ), ( 153, 153, 255 ), ( 153, 102, 255 ), + ( 153, 51, 255 ), ( 153, 0, 255 ), ( 153, 255, 204 ), ( 153, 204, 204 ), + ( 153, 153, 204 ), ( 153, 102, 204 ), ( 153, 51, 204 ), ( 153, 0, 204 ), + ( 153, 255, 153 ), ( 153, 204, 153 ), ( 153, 153, 153 ), ( 153, 102, 153 ), + ( 153, 51, 153 ), ( 153, 0, 153 ), ( 102, 255, 255 ), ( 102, 204, 255 ), + ( 102, 153, 255 ), ( 102, 102, 255 ), ( 102, 51, 255 ), ( 102, 0, 255 ), + ( 102, 255, 204 ), ( 102, 204, 204 ), ( 102, 153, 204 ), ( 102, 102, 204 ), + ( 102, 51, 204 ), ( 102, 0, 204 ), ( 102, 255, 153 ), ( 102, 204, 153 ), + ( 102, 153, 153 ), ( 102, 102, 153 ), ( 102, 51, 153 ), ( 102, 0, 153 ), + ( 51, 255, 255 ), ( 51, 204, 255 ), ( 51, 153, 255 ), ( 51, 102, 255 ), + ( 51, 51, 255 ), ( 51, 0, 255 ), ( 51, 255, 204 ), ( 51, 204, 204 ), + ( 51, 153, 204 ), ( 51, 102, 204 ), ( 51, 51, 204 ), ( 51, 0, 204 ), + ( 51, 255, 153 ), ( 51, 204, 153 ), ( 51, 153, 153 ), ( 51, 102, 153 ), + ( 51, 51, 153 ), ( 51, 0, 153 ), ( 0, 255, 255 ), ( 0, 204, 255 ), + ( 0, 153, 255 ), ( 0, 102, 255 ), ( 0, 51, 255 ), ( 0, 0, 255 ), + ( 0, 255, 204 ), ( 0, 204, 204 ), ( 0, 153, 204 ), ( 0, 102, 204 ), + ( 0, 51, 204 ), ( 0, 0, 204 ), ( 0, 255, 153 ), ( 0, 204, 153 ), + ( 0, 153, 153 ), ( 0, 102, 153 ), ( 0, 51, 153 ), ( 0, 0, 153 ), + ( 255, 255, 102 ), ( 255, 204, 102 ), ( 255, 153, 102 ), ( 255, 102, 102 ), + ( 255, 51, 102 ), ( 255, 0, 102 ), ( 255, 255, 51 ), ( 255, 204, 51 ), + ( 255, 153, 51 ), ( 255, 102, 51 ), ( 255, 51, 51 ), ( 255, 0, 51 ), + ( 255, 255, 0 ), ( 255, 204, 0 ), ( 255, 153, 0 ), ( 255, 102, 0 ), + ( 255, 51, 0 ), ( 255, 0, 0 ), ( 204, 255, 102 ), ( 204, 204, 102 ), + ( 204, 153, 102 ), ( 204, 102, 102 ), ( 204, 51, 102 ), ( 204, 0, 102 ), + ( 204, 255, 51 ), ( 204, 204, 51 ), ( 204, 153, 51 ), ( 204, 102, 51 ), + ( 204, 51, 51 ), ( 204, 0, 51 ), ( 204, 255, 0 ), ( 204, 204, 0 ), + ( 204, 153, 0 ), ( 204, 102, 0 ), ( 204, 51, 0 ), ( 204, 0, 0 ), + ( 153, 255, 102 ), ( 153, 204, 102 ), ( 153, 153, 102 ), ( 153, 102, 102 ), + ( 153, 51, 102 ), ( 153, 0, 102 ), ( 153, 255, 51 ), ( 153, 204, 51 ), + ( 153, 153, 51 ), ( 153, 102, 51 ), ( 153, 51, 51 ), ( 153, 0, 51 ), + ( 153, 255, 0 ), ( 153, 204, 0 ), ( 153, 153, 0 ), ( 153, 102, 0 ), + ( 153, 51, 0 ), ( 153, 0, 0 ), ( 102, 255, 102 ), ( 102, 204, 102 ), + ( 102, 153, 102 ), ( 102, 102, 102 ), ( 102, 51, 102 ), ( 102, 0, 102 ), + ( 102, 255, 51 ), ( 102, 204, 51 ), ( 102, 153, 51 ), ( 102, 102, 51 ), + ( 102, 51, 51 ), ( 102, 0, 51 ), ( 102, 255, 0 ), ( 102, 204, 0 ), + ( 102, 153, 0 ), ( 102, 102, 0 ), ( 102, 51, 0 ), ( 102, 0, 0 ), + ( 51, 255, 102 ), ( 51, 204, 102 ), ( 51, 153, 102 ), ( 51, 102, 102 ), + ( 51, 51, 102 ), ( 51, 0, 102 ), ( 51, 255, 51 ), ( 51, 204, 51 ), + ( 51, 153, 51 ), ( 51, 102, 51 ), ( 51, 51, 51 ), ( 51, 0, 51 ), + ( 51, 255, 0 ), ( 51, 204, 0 ), ( 51, 153, 0 ), ( 51, 102, 0 ), + ( 51, 51, 0 ), ( 51, 0, 0 ), ( 0, 255, 102 ), ( 0, 204, 102 ), + ( 0, 153, 102 ), ( 0, 102, 102 ), ( 0, 51, 102 ), ( 0, 0, 102 ), + ( 0, 255, 51 ), ( 0, 204, 51 ), ( 0, 153, 51 ), ( 0, 102, 51 ), + ( 0, 51, 51 ), ( 0, 0, 51 ), ( 0, 255, 0 ), ( 0, 204, 0 ), + ( 0, 153, 0 ), ( 0, 102, 0 ), ( 0, 51, 0 ), ( 17, 17, 17 ), + ( 34, 34, 34 ), ( 68, 68, 68 ), ( 85, 85, 85 ), ( 119, 119, 119 ), + ( 136, 136, 136 ), ( 170, 170, 170 ), ( 187, 187, 187 ), ( 221, 221, 221 ), + ( 238, 238, 238 ), ( 192, 192, 192 ), ( 128, 0, 0 ), ( 128, 0, 128 ), + ( 0, 128, 0 ), ( 0, 128, 128 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), + ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 ), ( 0, 0, 0 )) + +# so build a prototype image to be used for palette resampling +def build_prototype_image(): + image = Image.new("L", (1,len(_Palm8BitColormapValues),)) + image.putdata(range(len(_Palm8BitColormapValues))) + palettedata = () + for i in range(len(_Palm8BitColormapValues)): + palettedata = palettedata + _Palm8BitColormapValues[i] + for i in range(256 - len(_Palm8BitColormapValues)): + palettedata = palettedata + (0, 0, 0) + image.putpalette(palettedata) + return image + +Palm8BitColormapImage = build_prototype_image() + +# OK, we now have in Palm8BitColormapImage, a "P"-mode image with the right palette +# +# -------------------------------------------------------------------- + +_FLAGS = { + "custom-colormap": 0x4000, + "is-compressed": 0x8000, + "has-transparent": 0x2000, + } + +_COMPRESSION_TYPES = { + "none": 0xFF, + "rle": 0x01, + "scanline": 0x00, + } + +def o16b(i): + return chr(i>>8&255) + chr(i&255) + +# +# -------------------------------------------------------------------- + +## +# (Internal) Image save plugin for the Palm format. + +def _save(im, fp, filename, check=0): + + if im.mode == "P": + + # we assume this is a color Palm image with the standard colormap, + # unless the "info" dict has a "custom-colormap" field + + rawmode = "P" + bpp = 8 + version = 1 + + elif im.mode == "L" and im.encoderinfo.has_key("bpp") and im.encoderinfo["bpp"] in (1, 2, 4): + + # this is 8-bit grayscale, so we shift it to get the high-order bits, and invert it because + # Palm does greyscale from white (0) to black (1) + bpp = im.encoderinfo["bpp"] + im = im.point(lambda x, shift=8-bpp, maxval=(1 << bpp)-1: maxval - (x >> shift)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "L" and im.info.has_key("bpp") and im.info["bpp"] in (1, 2, 4): + + # here we assume that even though the inherent mode is 8-bit grayscale, only + # the lower bpp bits are significant. We invert them to match the Palm. + bpp = im.info["bpp"] + im = im.point(lambda x, maxval=(1 << bpp)-1: maxval - (x & maxval)) + # we ignore the palette here + im.mode = "P" + rawmode = "P;" + str(bpp) + version = 1 + + elif im.mode == "1": + + # monochrome -- write it inverted, as is the Palm standard + rawmode = "1;I" + bpp = 1 + version = 0 + + else: + + raise IOError, "cannot write mode %s as Palm" % im.mode + + if check: + return check + + # + # make sure image data is available + im.load() + + # write header + + cols = im.size[0] + rows = im.size[1] + + rowbytes = ((cols + (16/bpp - 1)) / (16 / bpp)) * 2; + transparent_index = 0 + compression_type = _COMPRESSION_TYPES["none"] + + flags = 0; + if im.mode == "P" and im.info.has_key("custom-colormap"): + flags = flags & _FLAGS["custom-colormap"] + colormapsize = 4 * 256 + 2; + colormapmode = im.palette.mode + colormap = im.getdata().getpalette() + else: + colormapsize = 0 + + if im.info.has_key("offset"): + offset = (rowbytes * rows + 16 + 3 + colormapsize) / 4; + else: + offset = 0 + + fp.write(o16b(cols) + o16b(rows) + o16b(rowbytes) + o16b(flags)) + fp.write(chr(bpp)) + fp.write(chr(version)) + fp.write(o16b(offset)) + fp.write(chr(transparent_index)) + fp.write(chr(compression_type)) + fp.write(o16b(0)) # reserved by Palm + + # now write colormap if necessary + + if colormapsize > 0: + fp.write(o16b(256)) + for i in range(256): + fp.write(chr(i)) + if colormapmode == 'RGB': + fp.write(chr(colormap[3 * i]) + chr(colormap[3 * i + 1]) + chr(colormap[3 * i + 2])) + elif colormapmode == 'RGBA': + fp.write(chr(colormap[4 * i]) + chr(colormap[4 * i + 1]) + chr(colormap[4 * i + 2])) + + # now convert data to raw form + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, rowbytes, 1))]) + + fp.flush() + + +# +# -------------------------------------------------------------------- + +Image.register_save("Palm", _save) + +Image.register_extension("Palm", ".palm") + +Image.register_mime("Palm", "image/palm") diff --git a/PIL/PcdImagePlugin.py b/PIL/PcdImagePlugin.py new file mode 100644 index 000000000..07bd27e9a --- /dev/null +++ b/PIL/PcdImagePlugin.py @@ -0,0 +1,76 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCD file handling +# +# History: +# 96-05-10 fl Created +# 96-05-27 fl Added draft mode (128x192, 256x384) +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.1" + + +import Image, ImageFile + +## +# Image plugin for PhotoCD images. This plugin only reads the 768x512 +# image from the file; higher resolutions are encoded in a proprietary +# encoding. + +class PcdImageFile(ImageFile.ImageFile): + + format = "PCD" + format_description = "Kodak PhotoCD" + + def _open(self): + + # rough + self.fp.seek(2048) + s = self.fp.read(2048) + + if s[:4] != "PCD_": + raise SyntaxError, "not a PCD file" + + orientation = ord(s[1538]) & 3 + if orientation == 1: + self.tile_post_rotate = 90 # hack + elif orientation == 3: + self.tile_post_rotate = -90 + + self.mode = "RGB" + self.size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [("pcd", (0,0)+self.size, 96*2048, None)] + + def draft(self, mode, size): + + if len(self.tile) != 1: + return + + d, e, o, a = self.tile[0] + + if size: + scale = max(self.size[0] / size[0], self.size[1] / size[1]) + for s, o in [(4,0*2048), (2,0*2048), (1,96*2048)]: + if scale >= s: + break + # e = e[0], e[1], (e[2]-e[0]+s-1)/s+e[0], (e[3]-e[1]+s-1)/s+e[1] + # self.size = ((self.size[0]+s-1)/s, (self.size[1]+s-1)/s) + + self.tile = [(d, e, o, a)] + + return self + +# +# registry + +Image.register_open("PCD", PcdImageFile) + +Image.register_extension("PCD", ".pcd") diff --git a/PIL/PcfFontFile.py b/PIL/PcfFontFile.py new file mode 100644 index 000000000..bec39e3a0 --- /dev/null +++ b/PIL/PcfFontFile.py @@ -0,0 +1,256 @@ +# +# THIS IS WORK IN PROGRESS +# +# The Python Imaging Library +# $Id$ +# +# portable compiled font file parser +# +# history: +# 1997-08-19 fl created +# 2003-09-13 fl fixed loading of unicode fonts +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1997-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +import Image +import FontFile + +import string + +# -------------------------------------------------------------------- +# declarations + +PCF_MAGIC = 0x70636601 # "\x01fcp" + +PCF_PROPERTIES = (1<<0) +PCF_ACCELERATORS = (1<<1) +PCF_METRICS = (1<<2) +PCF_BITMAPS = (1<<3) +PCF_INK_METRICS = (1<<4) +PCF_BDF_ENCODINGS = (1<<5) +PCF_SWIDTHS = (1<<6) +PCF_GLYPH_NAMES = (1<<7) +PCF_BDF_ACCELERATORS = (1<<8) + +BYTES_PER_ROW = [ + lambda bits: ((bits+7) >> 3), + lambda bits: ((bits+15) >> 3) & ~1, + lambda bits: ((bits+31) >> 3) & ~3, + lambda bits: ((bits+63) >> 3) & ~7, +] + + +def l16(c): + return ord(c[0]) + (ord(c[1])<<8) +def l32(c): + return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) + +def b16(c): + return ord(c[1]) + (ord(c[0])<<8) +def b32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + +def sz(s, o): + return s[o:string.index(s, "\0", o)] + +## +# Font file plugin for the X11 PCF format. + +class PcfFontFile(FontFile.FontFile): + + name = "name" + + def __init__(self, fp): + + magic = l32(fp.read(4)) + if magic != PCF_MAGIC: + raise SyntaxError, "not a PCF file" + + FontFile.FontFile.__init__(self) + + count = l32(fp.read(4)) + self.toc = {} + for i in range(count): + type = l32(fp.read(4)) + self.toc[type] = l32(fp.read(4)), l32(fp.read(4)), l32(fp.read(4)) + + self.fp = fp + + self.info = self._load_properties() + + metrics = self._load_metrics() + bitmaps = self._load_bitmaps(metrics) + encoding = self._load_encoding() + + # + # create glyph structure + + for ch in range(256): + ix = encoding[ch] + if ix is not None: + x, y, l, r, w, a, d, f = metrics[ix] + glyph = (w, 0), (l, d-y, x+l, d), (0, 0, x, y), bitmaps[ix] + self.glyph[ch] = glyph + + def _getformat(self, tag): + + format, size, offset = self.toc[tag] + + fp = self.fp + fp.seek(offset) + + format = l32(fp.read(4)) + + if format & 4: + i16, i32 = b16, b32 + else: + i16, i32 = l16, l32 + + return fp, format, i16, i32 + + def _load_properties(self): + + # + # font properties + + properties = {} + + fp, format, i16, i32 = self._getformat(PCF_PROPERTIES) + + nprops = i32(fp.read(4)) + + # read property description + p = [] + for i in range(nprops): + p.append((i32(fp.read(4)), ord(fp.read(1)), i32(fp.read(4)))) + if nprops & 3: + fp.seek(4 - (nprops & 3), 1) # pad + + data = fp.read(i32(fp.read(4))) + + for k, s, v in p: + k = sz(data, k) + if s: + v = sz(data, v) + properties[k] = v + + return properties + + def _load_metrics(self): + + # + # font metrics + + metrics = [] + + fp, format, i16, i32 = self._getformat(PCF_METRICS) + + append = metrics.append + + if (format & 0xff00) == 0x100: + + # "compressed" metrics + for i in range(i16(fp.read(2))): + left = ord(fp.read(1)) - 128 + right = ord(fp.read(1)) - 128 + width = ord(fp.read(1)) - 128 + ascent = ord(fp.read(1)) - 128 + descent = ord(fp.read(1)) - 128 + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, 0) + ) + + else: + + # "jumbo" metrics + for i in range(i32(fp.read(4))): + left = i16(fp.read(2)) + right = i16(fp.read(2)) + width = i16(fp.read(2)) + ascent = i16(fp.read(2)) + descent = i16(fp.read(2)) + attributes = i16(fp.read(2)) + xsize = right - left + ysize = ascent + descent + append( + (xsize, ysize, left, right, width, + ascent, descent, attributes) + ) + + return metrics + + def _load_bitmaps(self, metrics): + + # + # bitmap data + + bitmaps = [] + + fp, format, i16, i32 = self._getformat(PCF_BITMAPS) + + nbitmaps = i32(fp.read(4)) + + if nbitmaps != len(metrics): + raise IOError, "Wrong number of bitmaps" + + offsets = [] + for i in range(nbitmaps): + offsets.append(i32(fp.read(4))) + + bitmapSizes = [] + for i in range(4): + bitmapSizes.append(i32(fp.read(4))) + + byteorder = format & 4 # non-zero => MSB + bitorder = format & 8 # non-zero => MSB + padindex = format & 3 + + bitmapsize = bitmapSizes[padindex] + offsets.append(bitmapsize) + + data = fp.read(bitmapsize) + + pad = BYTES_PER_ROW[padindex] + mode = "1;R" + if bitorder: + mode = "1" + + for i in range(nbitmaps): + x, y, l, r, w, a, d, f = metrics[i] + b, e = offsets[i], offsets[i+1] + bitmaps.append( + Image.fromstring("1", (x, y), data[b:e], "raw", mode, pad(x)) + ) + + return bitmaps + + def _load_encoding(self): + + # map character code to bitmap index + encoding = [None] * 256 + + fp, format, i16, i32 = self._getformat(PCF_BDF_ENCODINGS) + + firstCol, lastCol = i16(fp.read(2)), i16(fp.read(2)) + firstRow, lastRow = i16(fp.read(2)), i16(fp.read(2)) + + default = i16(fp.read(2)) + + nencoding = (lastCol - firstCol + 1) * (lastRow - firstRow + 1) + + for i in range(nencoding): + encodingOffset = i16(fp.read(2)) + if encodingOffset != 0xFFFF: + try: + encoding[i+firstCol] = encodingOffset + except IndexError: + break # only load ISO-8859-1 glyphs + + return encoding diff --git a/PIL/PcxImagePlugin.py b/PIL/PcxImagePlugin.py new file mode 100644 index 000000000..15012d51c --- /dev/null +++ b/PIL/PcxImagePlugin.py @@ -0,0 +1,167 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PCX file handling +# +# This format was originally used by ZSoft's popular PaintBrush +# program for the IBM PC. It is also supported by many MS-DOS and +# Windows applications, including the Windows PaintBrush program in +# Windows 3. +# +# history: +# 1995-09-01 fl Created +# 1996-05-20 fl Fixed RGB support +# 1997-01-03 fl Fixed 2-bit and 4-bit support +# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) +# 1999-02-07 fl Added write support +# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust +# 2002-07-30 fl Seek from to current position, not beginning of file +# 2003-06-03 fl Extract DPI settings (info["dpi"]) +# +# Copyright (c) 1997-2003 by Secret Labs AB. +# Copyright (c) 1995-2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import Image, ImageFile, ImagePalette + +def i16(c,o): + return ord(c[o]) + (ord(c[o+1])<<8) + +def _accept(prefix): + return ord(prefix[0]) == 10 and ord(prefix[1]) in [0, 2, 3, 5] + +## +# Image plugin for Paintbrush images. + +class PcxImageFile(ImageFile.ImageFile): + + format = "PCX" + format_description = "Paintbrush" + + def _open(self): + + # header + s = self.fp.read(128) + if not _accept(s): + raise SyntaxError, "not a PCX file" + + # image + bbox = i16(s,4), i16(s,6), i16(s,8)+1, i16(s,10)+1 + if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]: + raise SyntaxError, "bad PCX image size" + + # format + version = ord(s[1]) + bits = ord(s[3]) + planes = ord(s[65]) + stride = i16(s,66) + + self.info["dpi"] = i16(s,12), i16(s,14) + + if bits == 1 and planes == 1: + mode = rawmode = "1" + + elif bits == 1 and planes in (2, 4): + mode = "P" + rawmode = "P;%dL" % planes + self.palette = ImagePalette.raw("RGB", s[16:64]) + + elif version == 5 and bits == 8 and planes == 1: + mode = rawmode = "L" + # FIXME: hey, this doesn't work with the incremental loader !!! + self.fp.seek(-769, 2) + s = self.fp.read(769) + if len(s) == 769 and ord(s[0]) == 12: + # check if the palette is linear greyscale + for i in range(256): + if s[i*3+1:i*3+4] != chr(i)*3: + mode = rawmode = "P" + break + if mode == "P": + self.palette = ImagePalette.raw("RGB", s[1:]) + self.fp.seek(128) + + elif version == 5 and bits == 8 and planes == 3: + mode = "RGB" + rawmode = "RGB;L" + + else: + raise IOError, "unknown PCX mode" + + self.mode = mode + self.size = bbox[2]-bbox[0], bbox[3]-bbox[1] + + bbox = (0, 0) + self.size + + self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))] + +# -------------------------------------------------------------------- +# save PCX files + +SAVE = { + # mode: (version, bits, planes, raw mode) + "1": (2, 1, 1, "1"), + "L": (5, 8, 1, "L"), + "P": (5, 8, 1, "P"), + "RGB": (5, 8, 3, "RGB;L"), +} + +def o16(i): + return chr(i&255) + chr(i>>8&255) + +def _save(im, fp, filename, check=0): + + try: + version, bits, planes, rawmode = SAVE[im.mode] + except KeyError: + raise ValueError, "Cannot save %s images as PCX" % im.mode + + if check: + return check + + # bytes per plane + stride = (im.size[0] * bits + 7) / 8 + + # under windows, we could determine the current screen size with + # "Image.core.display_mode()[1]", but I think that's overkill... + + screen = im.size + + dpi = 100, 100 + + # PCX header + fp.write( + chr(10) + chr(version) + chr(1) + chr(bits) + o16(0) + + o16(0) + o16(im.size[0]-1) + o16(im.size[1]-1) + o16(dpi[0]) + + o16(dpi[1]) + chr(0)*24 + chr(255)*24 + chr(0) + chr(planes) + + o16(stride) + o16(1) + o16(screen[0]) + o16(screen[1]) + + chr(0)*54 + ) + + assert fp.tell() == 128 + + ImageFile._save(im, fp, [("pcx", (0,0)+im.size, 0, + (rawmode, bits*planes))]) + + if im.mode == "P": + # colour palette + fp.write(chr(12)) + fp.write(im.im.getpalette("RGB", "RGB")) # 768 bytes + elif im.mode == "L": + # greyscale palette + fp.write(chr(12)) + for i in range(256): + fp.write(chr(i)*3) + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PCX", PcxImageFile, _accept) +Image.register_save("PCX", _save) + +Image.register_extension("PCX", ".pcx") diff --git a/PIL/PdfImagePlugin.py b/PIL/PdfImagePlugin.py new file mode 100644 index 000000000..4f957765c --- /dev/null +++ b/PIL/PdfImagePlugin.py @@ -0,0 +1,211 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PDF (Acrobat) file handling +# +# History: +# 1996-07-16 fl Created +# 1997-01-18 fl Fixed header +# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. +# 2004-02-24 fl Fixes for 1 and P images. +# +# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. +# Copyright (c) 1996-1997 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +## +# Image plugin for PDF images (output only). +## + +__version__ = "0.4" + +import Image, ImageFile +import StringIO + + +# +# -------------------------------------------------------------------- + +# object ids: +# 1. catalogue +# 2. pages +# 3. image +# 4. page +# 5. page contents + +def _obj(fp, obj, **dict): + fp.write("%d 0 obj\n" % obj) + if dict: + fp.write("<<\n") + for k, v in dict.items(): + if v is not None: + fp.write("/%s %s\n" % (k, v)) + fp.write(">>\n") + +def _endobj(fp): + fp.write("endobj\n") + +## +# (Internal) Image save plugin for the PDF format. + +def _save(im, fp, filename): + resolution = im.encoderinfo.get("resolution", 72.0) + + # + # make sure image data is available + im.load() + + xref = [0]*(5+1) # placeholders + + fp.write("%PDF-1.2\n") + fp.write("% created by PIL PDF driver " + __version__ + "\n") + + # + # Get image characteristics + + width, height = im.size + + # FIXME: Should replace ASCIIHexDecode with RunLengthDecode (packbits) + # or LZWDecode (tiff/lzw compression). Note that PDF 1.2 also supports + # Flatedecode (zip compression). + + bits = 8 + params = None + + if im.mode == "1": + filter = "/ASCIIHexDecode" + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + bits = 1 + elif im.mode == "L": + filter = "/DCTDecode" + # params = "<< /Predictor 15 /Columns %d >>" % (width-2) + colorspace = "/DeviceGray" + procset = "/ImageB" # grayscale + elif im.mode == "P": + filter = "/ASCIIHexDecode" + colorspace = "[ /Indexed /DeviceRGB 255 <" + palette = im.im.getpalette("RGB") + for i in range(256): + r = ord(palette[i*3]) + g = ord(palette[i*3+1]) + b = ord(palette[i*3+2]) + colorspace = colorspace + "%02x%02x%02x " % (r, g, b) + colorspace = colorspace + "> ]" + procset = "/ImageI" # indexed color + elif im.mode == "RGB": + filter = "/DCTDecode" + colorspace = "/DeviceRGB" + procset = "/ImageC" # color images + elif im.mode == "CMYK": + filter = "/DCTDecode" + colorspace = "/DeviceCMYK" + procset = "/ImageC" # color images + else: + raise ValueError("cannot save mode %s" % im.mode) + + # + # catalogue + + xref[1] = fp.tell() + _obj(fp, 1, Type = "/Catalog", + Pages = "2 0 R") + _endobj(fp) + + # + # pages + + xref[2] = fp.tell() + _obj(fp, 2, Type = "/Pages", + Count = 1, + Kids = "[4 0 R]") + _endobj(fp) + + # + # image + + op = StringIO.StringIO() + + if filter == "/ASCIIHexDecode": + if bits == 1: + # FIXME: the hex encoder doesn't support packed 1-bit + # images; do things the hard way... + data = im.tostring("raw", "1") + im = Image.new("L", (len(data), 1), None) + im.putdata(data) + ImageFile._save(im, op, [("hex", (0,0)+im.size, 0, im.mode)]) + elif filter == "/DCTDecode": + ImageFile._save(im, op, [("jpeg", (0,0)+im.size, 0, im.mode)]) + elif filter == "/FlateDecode": + ImageFile._save(im, op, [("zip", (0,0)+im.size, 0, im.mode)]) + elif filter == "/RunLengthDecode": + ImageFile._save(im, op, [("packbits", (0,0)+im.size, 0, im.mode)]) + else: + raise ValueError("unsupported PDF filter (%s)" % filter) + + xref[3] = fp.tell() + _obj(fp, 3, Type = "/XObject", + Subtype = "/Image", + Width = width, # * 72.0 / resolution, + Height = height, # * 72.0 / resolution, + Length = len(op.getvalue()), + Filter = filter, + BitsPerComponent = bits, + DecodeParams = params, + ColorSpace = colorspace) + + fp.write("stream\n") + fp.write(op.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # page + + xref[4] = fp.tell() + _obj(fp, 4) + fp.write("<<\n/Type /Page\n/Parent 2 0 R\n"\ + "/Resources <<\n/ProcSet [ /PDF %s ]\n"\ + "/XObject << /image 3 0 R >>\n>>\n"\ + "/MediaBox [ 0 0 %d %d ]\n/Contents 5 0 R\n>>\n" %\ + (procset, int(width * 72.0 /resolution) , int(height * 72.0 / resolution))) + _endobj(fp) + + # + # page contents + + op = StringIO.StringIO() + + op.write("q %d 0 0 %d 0 0 cm /image Do Q\n" % (int(width * 72.0 / resolution), int(height * 72.0 / resolution))) + + xref[5] = fp.tell() + _obj(fp, 5, Length = len(op.getvalue())) + + fp.write("stream\n") + fp.write(op.getvalue()) + fp.write("\nendstream\n") + + _endobj(fp) + + # + # trailer + startxref = fp.tell() + fp.write("xref\n0 %d\n0000000000 65535 f \n" % len(xref)) + for x in xref[1:]: + fp.write("%010d 00000 n \n" % x) + fp.write("trailer\n<<\n/Size %d\n/Root 1 0 R\n>>\n" % len(xref)) + fp.write("startxref\n%d\n%%%%EOF\n" % startxref) + fp.flush() + +# +# -------------------------------------------------------------------- + +Image.register_save("PDF", _save) + +Image.register_extension("PDF", ".pdf") + +Image.register_mime("PDF", "application/pdf") diff --git a/PIL/PixarImagePlugin.py b/PIL/PixarImagePlugin.py new file mode 100644 index 000000000..d4c597b61 --- /dev/null +++ b/PIL/PixarImagePlugin.py @@ -0,0 +1,71 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PIXAR raster support for PIL +# +# history: +# 97-01-29 fl Created +# +# notes: +# This is incomplete; it is based on a few samples created with +# Photoshop 2.5 and 3.0, and a summary description provided by +# Greg Coats . Hopefully, "L" and +# "RGBA" support will be added in future versions. +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.1" + +import Image, ImageFile + +# +# helpers + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def i32(c): + return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) + +## +# Image plugin for PIXAR raster images. + +class PixarImageFile(ImageFile.ImageFile): + + format = "PIXAR" + format_description = "PIXAR raster image" + + def _open(self): + + # assuming a 4-byte magic label (FIXME: add "_accept" hook) + s = self.fp.read(4) + if s != "\200\350\000\000": + raise SyntaxError, "not a PIXAR file" + + # read rest of header + s = s + self.fp.read(508) + + self.size = i16(s[418:420]), i16(s[416:418]) + + # get channel/depth descriptions + mode = i16(s[424:426]), i16(s[426:428]) + + if mode == (14, 2): + self.mode = "RGB" + # FIXME: to be continued... + + # create tile descriptor (assuming "dumped") + self.tile = [("raw", (0,0)+self.size, 1024, (self.mode, 0, 1))] + +# +# -------------------------------------------------------------------- + +Image.register_open("PIXAR", PixarImageFile) + +# +# FIXME: what's the standard extension? diff --git a/PIL/PngImagePlugin.py b/PIL/PngImagePlugin.py new file mode 100644 index 000000000..0ee8589a5 --- /dev/null +++ b/PIL/PngImagePlugin.py @@ -0,0 +1,620 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PNG support code +# +# See "PNG (Portable Network Graphics) Specification, version 1.0; +# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). +# +# history: +# 1996-05-06 fl Created (couldn't resist it) +# 1996-12-14 fl Upgraded, added read and verify support (0.2) +# 1996-12-15 fl Separate PNG stream parser +# 1996-12-29 fl Added write support, added getchunks +# 1996-12-30 fl Eliminated circular references in decoder (0.3) +# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) +# 2001-02-08 fl Added transparency support (from Zircon) (0.5) +# 2001-04-16 fl Don't close data source in "open" method (0.6) +# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) +# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) +# 2004-09-20 fl Added PngInfo chunk container +# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) +# 2008-08-13 fl Added tRNS support for RGB images +# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) +# 2009-03-08 fl Added zTXT support (from Lowell Alleman) +# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) +# +# Copyright (c) 1997-2009 by Secret Labs AB +# Copyright (c) 1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.9" + +import re, string + +import Image, ImageFile, ImagePalette, zlib + + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + +is_cid = re.compile("\w\w\w\w").match + + +_MAGIC = "\211PNG\r\n\032\n" + + +_MODES = { + # supported bits/color combinations, and corresponding modes/rawmodes + (1, 0): ("1", "1"), + (2, 0): ("L", "L;2"), + (4, 0): ("L", "L;4"), + (8, 0): ("L", "L"), + (16,0): ("I", "I;16B"), + (8, 2): ("RGB", "RGB"), + (16,2): ("RGB", "RGB;16B"), + (1, 3): ("P", "P;1"), + (2, 3): ("P", "P;2"), + (4, 3): ("P", "P;4"), + (8, 3): ("P", "P"), + (8, 4): ("LA", "LA"), + (16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available + (8, 6): ("RGBA", "RGBA"), + (16,6): ("RGBA", "RGBA;16B"), +} + + +# -------------------------------------------------------------------- +# Support classes. Suitable for PNG and related formats like MNG etc. + +class ChunkStream: + + def __init__(self, fp): + + self.fp = fp + self.queue = [] + + if not hasattr(Image.core, "crc32"): + self.crc = self.crc_skip + + def read(self): + "Fetch a new chunk. Returns header information." + + if self.queue: + cid, pos, len = self.queue[-1] + del self.queue[-1] + self.fp.seek(pos) + else: + s = self.fp.read(8) + cid = s[4:] + pos = self.fp.tell() + len = i32(s) + + if not is_cid(cid): + raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid) + + return cid, pos, len + + def close(self): + self.queue = self.crc = self.fp = None + + def push(self, cid, pos, len): + + self.queue.append((cid, pos, len)) + + def call(self, cid, pos, len): + "Call the appropriate chunk handler" + + if Image.DEBUG: + print "STREAM", cid, pos, len + return getattr(self, "chunk_" + cid)(pos, len) + + def crc(self, cid, data): + "Read and verify checksum" + + crc1 = Image.core.crc32(data, Image.core.crc32(cid)) + crc2 = i16(self.fp.read(2)), i16(self.fp.read(2)) + if crc1 != crc2: + raise SyntaxError, "broken PNG file"\ + "(bad header checksum in %s)" % cid + + def crc_skip(self, cid, data): + "Read checksum. Used if the C module is not present" + + self.fp.read(4) + + def verify(self, endchunk = "IEND"): + + # Simple approach; just calculate checksum for all remaining + # blocks. Must be called directly after open. + + cids = [] + + while 1: + cid, pos, len = self.read() + if cid == endchunk: + break + self.crc(cid, ImageFile._safe_read(self.fp, len)) + cids.append(cid) + + return cids + + +# -------------------------------------------------------------------- +# PNG chunk container (for use with save(pnginfo=)) + +class PngInfo: + + def __init__(self): + self.chunks = [] + + def add(self, cid, data): + self.chunks.append((cid, data)) + + def add_text(self, key, value, zip=0): + if zip: + import zlib + self.add("zTXt", key + "\0\0" + zlib.compress(value)) + else: + self.add("tEXt", key + "\0" + value) + +# -------------------------------------------------------------------- +# PNG image stream (IHDR/IEND) + +class PngStream(ChunkStream): + + def __init__(self, fp): + + ChunkStream.__init__(self, fp) + + # local copies of Image attributes + self.im_info = {} + self.im_text = {} + self.im_size = (0,0) + self.im_mode = None + self.im_tile = None + self.im_palette = None + + def chunk_iCCP(self, pos, len): + + # ICC profile + s = ImageFile._safe_read(self.fp, len) + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + i = string.find(s, chr(0)) + if Image.DEBUG: + print "iCCP profile name", s[:i] + print "Compression method", ord(s[i]) + comp_method = ord(s[i]) + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in iCCP chunk" % comp_method) + try: + icc_profile = zlib.decompress(s[i+2:]) + except zlib.error: + icc_profile = None # FIXME + self.im_info["icc_profile"] = icc_profile + return s + + def chunk_IHDR(self, pos, len): + + # image header + s = ImageFile._safe_read(self.fp, len) + self.im_size = i32(s), i32(s[4:]) + try: + self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))] + except: + pass + if ord(s[12]): + self.im_info["interlace"] = 1 + if ord(s[11]): + raise SyntaxError, "unknown filter category" + return s + + def chunk_IDAT(self, pos, len): + + # image data + self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)] + self.im_idat = len + raise EOFError + + def chunk_IEND(self, pos, len): + + # end of PNG image + raise EOFError + + def chunk_PLTE(self, pos, len): + + # palette + s = ImageFile._safe_read(self.fp, len) + if self.im_mode == "P": + self.im_palette = "RGB", s + return s + + def chunk_tRNS(self, pos, len): + + # transparency + s = ImageFile._safe_read(self.fp, len) + if self.im_mode == "P": + i = string.find(s, chr(0)) + if i >= 0: + self.im_info["transparency"] = i + elif self.im_mode == "L": + self.im_info["transparency"] = i16(s) + elif self.im_mode == "RGB": + self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:]) + return s + + def chunk_gAMA(self, pos, len): + + # gamma setting + s = ImageFile._safe_read(self.fp, len) + self.im_info["gamma"] = i32(s) / 100000.0 + return s + + def chunk_pHYs(self, pos, len): + + # pixels per unit + s = ImageFile._safe_read(self.fp, len) + px, py = i32(s), i32(s[4:]) + unit = ord(s[8]) + if unit == 1: # meter + dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5) + self.im_info["dpi"] = dpi + elif unit == 0: + self.im_info["aspect"] = px, py + return s + + def chunk_tEXt(self, pos, len): + + # text + s = ImageFile._safe_read(self.fp, len) + try: + k, v = string.split(s, "\0", 1) + except ValueError: + k = s; v = "" # fallback for broken tEXt tags + if k: + self.im_info[k] = self.im_text[k] = v + return s + + def chunk_zTXt(self, pos, len): + + # compressed text + s = ImageFile._safe_read(self.fp, len) + k, v = string.split(s, "\0", 1) + comp_method = ord(v[0]) + if comp_method != 0: + raise SyntaxError("Unknown compression method %s in zTXt chunk" % comp_method) + import zlib + self.im_info[k] = self.im_text[k] = zlib.decompress(v[1:]) + return s + +# -------------------------------------------------------------------- +# PNG reader + +def _accept(prefix): + return prefix[:8] == _MAGIC + +## +# Image plugin for PNG images. + +class PngImageFile(ImageFile.ImageFile): + + format = "PNG" + format_description = "Portable network graphics" + + def _open(self): + + if self.fp.read(8) != _MAGIC: + raise SyntaxError, "not a PNG file" + + # + # Parse headers up to the first IDAT chunk + + self.png = PngStream(self.fp) + + while 1: + + # + # get next chunk + + cid, pos, len = self.png.read() + + try: + s = self.png.call(cid, pos, len) + except EOFError: + break + except AttributeError: + if Image.DEBUG: + print cid, pos, len, "(unknown)" + s = ImageFile._safe_read(self.fp, len) + + self.png.crc(cid, s) + + # + # Copy relevant attributes from the PngStream. An alternative + # would be to let the PngStream class modify these attributes + # directly, but that introduces circular references which are + # difficult to break if things go wrong in the decoder... + # (believe me, I've tried ;-) + + self.mode = self.png.im_mode + self.size = self.png.im_size + self.info = self.png.im_info + self.text = self.png.im_text # experimental + self.tile = self.png.im_tile + + if self.png.im_palette: + rawmode, data = self.png.im_palette + self.palette = ImagePalette.raw(rawmode, data) + + self.__idat = len # used by load_read() + + + def verify(self): + "Verify PNG file" + + if self.fp is None: + raise RuntimeError("verify must be called directly after open") + + # back up to beginning of IDAT block + self.fp.seek(self.tile[0][2] - 8) + + self.png.verify() + self.png.close() + + self.fp = None + + def load_prepare(self): + "internal: prepare to read PNG file" + + if self.info.get("interlace"): + self.decoderconfig = self.decoderconfig + (1,) + + ImageFile.ImageFile.load_prepare(self) + + def load_read(self, bytes): + "internal: read more image data" + + while self.__idat == 0: + # end of chunk, skip forward to next one + + self.fp.read(4) # CRC + + cid, pos, len = self.png.read() + + if cid not in ["IDAT", "DDAT"]: + self.png.push(cid, pos, len) + return "" + + self.__idat = len # empty chunks are allowed + + # read more data from this chunk + if bytes <= 0: + bytes = self.__idat + else: + bytes = min(bytes, self.__idat) + + self.__idat = self.__idat - bytes + + return self.fp.read(bytes) + + + def load_end(self): + "internal: finished reading image data" + + self.png.close() + self.png = None + + +# -------------------------------------------------------------------- +# PNG writer + +def o16(i): + return chr(i>>8&255) + chr(i&255) + +def o32(i): + return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) + +_OUTMODES = { + # supported PIL modes, and corresponding rawmodes/bits/color combinations + "1": ("1", chr(1)+chr(0)), + "L;1": ("L;1", chr(1)+chr(0)), + "L;2": ("L;2", chr(2)+chr(0)), + "L;4": ("L;4", chr(4)+chr(0)), + "L": ("L", chr(8)+chr(0)), + "LA": ("LA", chr(8)+chr(4)), + "I": ("I;16B", chr(16)+chr(0)), + "P;1": ("P;1", chr(1)+chr(3)), + "P;2": ("P;2", chr(2)+chr(3)), + "P;4": ("P;4", chr(4)+chr(3)), + "P": ("P", chr(8)+chr(3)), + "RGB": ("RGB", chr(8)+chr(2)), + "RGBA":("RGBA", chr(8)+chr(6)), +} + +def putchunk(fp, cid, *data): + "Write a PNG chunk (including CRC field)" + + data = string.join(data, "") + + fp.write(o32(len(data)) + cid) + fp.write(data) + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + fp.write(o16(hi) + o16(lo)) + +class _idat: + # wrap output from the encoder in IDAT chunks + + def __init__(self, fp, chunk): + self.fp = fp + self.chunk = chunk + def write(self, data): + self.chunk(self.fp, "IDAT", data) + +def _save(im, fp, filename, chunk=putchunk, check=0): + # save an image to disk (called by the save method) + + mode = im.mode + + if mode == "P": + + # + # attempt to minimize storage requirements for palette images + + if im.encoderinfo.has_key("bits"): + + # number of bits specified by user + n = 1 << im.encoderinfo["bits"] + + else: + + # check palette contents + n = 256 # FIXME + + if n <= 2: + bits = 1 + elif n <= 4: + bits = 2 + elif n <= 16: + bits = 4 + else: + bits = 8 + + if bits != 8: + mode = "%s;%d" % (mode, bits) + + # encoder options + if im.encoderinfo.has_key("dictionary"): + dictionary = im.encoderinfo["dictionary"] + else: + dictionary = "" + + im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary) + + # get the corresponding PNG mode + try: + rawmode, mode = _OUTMODES[mode] + except KeyError: + raise IOError, "cannot write mode %s as PNG" % mode + + if check: + return check + + # + # write minimal PNG file + + fp.write(_MAGIC) + + chunk(fp, "IHDR", + o32(im.size[0]), o32(im.size[1]), # 0: size + mode, # 8: depth/type + chr(0), # 10: compression + chr(0), # 11: filter category + chr(0)) # 12: interlace flag + + if im.mode == "P": + chunk(fp, "PLTE", im.im.getpalette("RGB")) + + if im.encoderinfo.has_key("transparency"): + if im.mode == "P": + transparency = max(0, min(255, im.encoderinfo["transparency"])) + chunk(fp, "tRNS", chr(255) * transparency + chr(0)) + elif im.mode == "L": + transparency = max(0, min(65535, im.encoderinfo["transparency"])) + chunk(fp, "tRNS", o16(transparency)) + elif im.mode == "RGB": + red, green, blue = im.encoderinfo["transparency"] + chunk(fp, "tRNS", o16(red) + o16(green) + o16(blue)) + else: + raise IOError("cannot use transparency for this mode") + + if 0: + # FIXME: to be supported some day + chunk(fp, "gAMA", o32(int(gamma * 100000.0))) + + dpi = im.encoderinfo.get("dpi") + if dpi: + chunk(fp, "pHYs", + o32(int(dpi[0] / 0.0254 + 0.5)), + o32(int(dpi[1] / 0.0254 + 0.5)), + chr(1)) + + info = im.encoderinfo.get("pnginfo") + if info: + for cid, data in info.chunks: + chunk(fp, cid, data) + + # ICC profile writing support -- 2008-06-06 Florian Hoech + if im.info.has_key("icc_profile"): + # ICC profile + # according to PNG spec, the iCCP chunk contains: + # Profile name 1-79 bytes (character string) + # Null separator 1 byte (null character) + # Compression method 1 byte (0) + # Compressed profile n bytes (zlib with deflate compression) + try: + import ICCProfile + p = ICCProfile.ICCProfile(im.info["icc_profile"]) + name = p.tags.desc.get("ASCII", p.tags.desc.get("Unicode", p.tags.desc.get("Macintosh", p.tags.desc.get("en", {}).get("US", "ICC Profile")))).encode("latin1", "replace")[:79] + except ImportError: + name = "ICC Profile" + data = name + "\0\0" + zlib.compress(im.info["icc_profile"]) + chunk(fp, "iCCP", data) + + ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)]) + + chunk(fp, "IEND", "") + + try: + fp.flush() + except: + pass + + +# -------------------------------------------------------------------- +# PNG chunk converter + +def getchunks(im, **params): + """Return a list of PNG chunks representing this image.""" + + class collector: + data = [] + def write(self, data): + pass + def append(self, chunk): + self.data.append(chunk) + + def append(fp, cid, *data): + data = string.join(data, "") + hi, lo = Image.core.crc32(data, Image.core.crc32(cid)) + crc = o16(hi) + o16(lo) + fp.append((cid, data, crc)) + + fp = collector() + + try: + im.encoderinfo = params + _save(im, fp, None, append) + finally: + del im.encoderinfo + + return fp.data + + +# -------------------------------------------------------------------- +# Registry + +Image.register_open("PNG", PngImageFile, _accept) +Image.register_save("PNG", _save) + +Image.register_extension("PNG", ".png") + +Image.register_mime("PNG", "image/png") diff --git a/PIL/PpmImagePlugin.py b/PIL/PpmImagePlugin.py new file mode 100644 index 000000000..e86146c10 --- /dev/null +++ b/PIL/PpmImagePlugin.py @@ -0,0 +1,131 @@ +# +# The Python Imaging Library. +# $Id$ +# +# PPM support for PIL +# +# History: +# 96-03-24 fl Created +# 98-03-06 fl Write RGBA images (as RGB, that is) +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + +import string + +import Image, ImageFile + +# +# -------------------------------------------------------------------- + +MODES = { + # standard + "P4": "1", + "P5": "L", + "P6": "RGB", + # extensions + "P0CMYK": "CMYK", + # PIL extensions (for test purposes only) + "PyP": "P", + "PyRGBA": "RGBA", + "PyCMYK": "CMYK" +} + +def _accept(prefix): + return prefix[0] == "P" and prefix[1] in "0456y" + +## +# Image plugin for PBM, PGM, and PPM images. + +class PpmImageFile(ImageFile.ImageFile): + + format = "PPM" + format_description = "Pbmplus image" + + def _token(self, s = ""): + while 1: # read until next whitespace + c = self.fp.read(1) + if not c or c in string.whitespace: + break + s = s + c + return s + + def _open(self): + + # check magic + s = self.fp.read(1) + if s != "P": + raise SyntaxError, "not a PPM file" + mode = MODES[self._token(s)] + + if mode == "1": + self.mode = "1" + rawmode = "1;I" + else: + self.mode = rawmode = mode + + for ix in range(3): + while 1: + while 1: + s = self.fp.read(1) + if s not in string.whitespace: + break + if s != "#": + break + s = self.fp.readline() + s = int(self._token(s)) + if ix == 0: + xsize = s + elif ix == 1: + ysize = s + if mode == "1": + break + + self.size = xsize, ysize + self.tile = [("raw", + (0, 0, xsize, ysize), + self.fp.tell(), + (rawmode, 0, 1))] + + # ALTERNATIVE: load via builtin debug function + # self.im = Image.core.open_ppm(self.filename) + # self.mode = self.im.mode + # self.size = self.im.size + +# +# -------------------------------------------------------------------- + +def _save(im, fp, filename): + if im.mode == "1": + rawmode, head = "1;I", "P4" + elif im.mode == "L": + rawmode, head = "L", "P5" + elif im.mode == "RGB": + rawmode, head = "RGB", "P6" + elif im.mode == "RGBA": + rawmode, head = "RGB", "P6" + else: + raise IOError, "cannot write mode %s as PPM" % im.mode + fp.write(head + "\n%d %d\n" % im.size) + if head != "P4": + fp.write("255\n") + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, 1))]) + + # ALTERNATIVE: save via builtin debug function + # im._dump(filename) + +# +# -------------------------------------------------------------------- + +Image.register_open("PPM", PpmImageFile, _accept) +Image.register_save("PPM", _save) + +Image.register_extension("PPM", ".pbm") +Image.register_extension("PPM", ".pgm") +Image.register_extension("PPM", ".ppm") diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py new file mode 100644 index 000000000..9d1b17a36 --- /dev/null +++ b/PIL/PsdImagePlugin.py @@ -0,0 +1,291 @@ +# +# The Python Imaging Library +# $Id$ +# +# Adobe PSD 2.5/3.0 file handling +# +# History: +# 1995-09-01 fl Created +# 1997-01-03 fl Read most PSD images +# 1997-01-18 fl Fixed P and CMYK support +# 2001-10-21 fl Added seek/tell support (for layers) +# +# Copyright (c) 1997-2001 by Secret Labs AB. +# Copyright (c) 1995-2001 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.4" + +import Image, ImageFile, ImagePalette + +MODES = { + # (photoshop mode, bits) -> (pil mode, required channels) + (0, 1): ("1", 1), + (0, 8): ("L", 1), + (1, 8): ("L", 1), + (2, 8): ("P", 1), + (3, 8): ("RGB", 3), + (4, 8): ("CMYK", 4), + (7, 8): ("L", 1), # FIXME: multilayer + (8, 8): ("L", 1), # duotone + (9, 8): ("LAB", 3) +} + +# +# helpers + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + +# --------------------------------------------------------------------. +# read PSD images + +def _accept(prefix): + return prefix[:4] == "8BPS" + +## +# Image plugin for Photoshop images. + +class PsdImageFile(ImageFile.ImageFile): + + format = "PSD" + format_description = "Adobe Photoshop" + + def _open(self): + + read = self.fp.read + + # + # header + + s = read(26) + if s[:4] != "8BPS" or i16(s[4:]) != 1: + raise SyntaxError, "not a PSD file" + + psd_bits = i16(s[22:]) + psd_channels = i16(s[12:]) + psd_mode = i16(s[24:]) + + mode, channels = MODES[(psd_mode, psd_bits)] + + if channels > psd_channels: + raise IOError, "not enough channels" + + self.mode = mode + self.size = i32(s[18:]), i32(s[14:]) + + # + # color mode data + + size = i32(read(4)) + if size: + data = read(size) + if mode == "P" and size == 768: + self.palette = ImagePalette.raw("RGB;L", data) + + # + # image resources + + self.resources = [] + + size = i32(read(4)) + if size: + # load resources + end = self.fp.tell() + size + while self.fp.tell() < end: + signature = read(4) + id = i16(read(2)) + name = read(ord(read(1))) + if not (len(name) & 1): + read(1) # padding + data = read(i32(read(4))) + if (len(data) & 1): + read(1) # padding + self.resources.append((id, name, data)) + if id == 1039: # ICC profile + self.info["icc_profile"] = data + + # + # layer and mask information + + self.layers = [] + + size = i32(read(4)) + if size: + end = self.fp.tell() + size + size = i32(read(4)) + if size: + self.layers = _layerinfo(self.fp) + self.fp.seek(end) + + # + # image descriptor + + self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) + + # keep the file open + self._fp = self.fp + self.frame = 0 + + def seek(self, layer): + # seek to given layer (1..max) + if layer == self.frame: + return + try: + if layer <= 0: + raise IndexError + name, mode, bbox, tile = self.layers[layer-1] + self.mode = mode + self.tile = tile + self.frame = layer + self.fp = self._fp + return name, bbox + except IndexError: + raise EOFError, "no such layer" + + def tell(self): + # return layer number (0=image, 1..max=layers) + return self.frame + + def load_prepare(self): + # create image memory if necessary + if not self.im or\ + self.im.mode != self.mode or self.im.size != self.size: + self.im = Image.core.fill(self.mode, self.size, 0) + # create palette (optional) + if self.mode == "P": + Image.Image.load(self) + +def _layerinfo(file): + # read layerinfo block + layers = [] + read = file.read + + for i in range(abs(i16(read(2)))): + + # bounding box + y0 = i32(read(4)); x0 = i32(read(4)) + y1 = i32(read(4)); x1 = i32(read(4)) + + # image info + info = [] + mode = [] + for i in range(i16(read(2))): + type = i16(read(2)) + if type == 65535: + m = "A" + else: + m = "RGB"[type] + mode.append(m) + size = i32(read(4)) + info.append((m, size)) + + # figure out the image mode + mode.sort() + if mode == ["R"]: + mode = "L" + elif mode == ["B", "G", "R"]: + mode = "RGB" + elif mode == ["A", "B", "G", "R"]: + mode = "RGBA" + else: + mode = None # unknown + + # skip over blend flags and extra information + filler = read(12) + name = "" + size = i32(read(4)) + combined = 0 + if size: + length = i32(read(4)) + if length: + mask_y = i32(read(4)); mask_x = i32(read(4)) + mask_h = i32(read(4)) - mask_y; mask_w = i32(read(4)) - mask_x + file.seek(length - 16, 1) + combined += length + 4 + + length = i32(read(4)) + if length: + file.seek(length, 1) + combined += length + 4 + + length = ord(read(1)) + if length: + name = read(length) + combined += length + 1 + + file.seek(size - combined, 1) + layers.append((name, mode, (x0, y0, x1, y1))) + + # get tiles + i = 0 + for name, mode, bbox in layers: + tile = [] + for m in mode: + t = _maketile(file, m, bbox, 1) + if t: + tile.extend(t) + layers[i] = name, mode, bbox, tile + i = i + 1 + + return layers + +def _maketile(file, mode, bbox, channels): + + tile = None + read = file.read + + compression = i16(read(2)) + + xsize = bbox[2] - bbox[0] + ysize = bbox[3] - bbox[1] + + offset = file.tell() + + if compression == 0: + # + # raw compression + tile = [] + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer = layer + ";I" + tile.append(("raw", bbox, offset, layer)) + offset = offset + xsize*ysize + + elif compression == 1: + # + # packbits compression + i = 0 + tile = [] + bytecount = read(channels * ysize * 2) + offset = file.tell() + for channel in range(channels): + layer = mode[channel] + if mode == "CMYK": + layer = layer + ";I" + tile.append( + ("packbits", bbox, offset, layer) + ) + for y in range(ysize): + offset = offset + i16(bytecount[i:i+2]) + i = i + 2 + + file.seek(offset) + + if offset & 1: + read(1) # padding + + return tile + +# -------------------------------------------------------------------- +# registry + +Image.register_open("PSD", PsdImageFile, _accept) + +Image.register_extension("PSD", ".psd") diff --git a/PIL/SgiImagePlugin.py b/PIL/SgiImagePlugin.py new file mode 100644 index 000000000..c65b91aca --- /dev/null +++ b/PIL/SgiImagePlugin.py @@ -0,0 +1,92 @@ +# +# The Python Imaging Library. +# $Id$ +# +# SGI image file handling +# +# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. +# +# +# History: +# 1995-09-10 fl Created +# +# Copyright (c) 2008 by Karsten Hiddemann. +# Copyright (c) 1997 by Secret Labs AB. +# Copyright (c) 1995 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +import Image, ImageFile + + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + + +def _accept(prefix): + return i16(prefix) == 474 + +## +# Image plugin for SGI images. + +class SgiImageFile(ImageFile.ImageFile): + + format = "SGI" + format_description = "SGI Image File Format" + + def _open(self): + + # HEAD + s = self.fp.read(512) + if i16(s) != 474: + raise SyntaxError("not an SGI image file") + + # relevant header entries + compression = ord(s[2]) + + # bytes, dimension, zsize + layout = ord(s[3]), i16(s[4:]), i16(s[10:]) + + # determine mode from bytes/zsize + if layout == (1, 2, 1) or layout == (1, 1, 1): + self.mode = "L" + elif layout == (1, 3, 3): + self.mode = "RGB" + elif layout == (1, 3, 4): + self.mode = "RGBA" + else: + raise SyntaxError("unsupported SGI image mode") + + # size + self.size = i16(s[6:]), i16(s[8:]) + + + # decoder info + if compression == 0: + offset = 512 + pagesize = self.size[0]*self.size[1]*layout[0] + self.tile = [] + for layer in self.mode: + self.tile.append(("raw", (0,0)+self.size, offset, (layer,0,-1))) + offset = offset + pagesize + elif compression == 1: + self.tile = [("sgi_rle", (0,0)+self.size, 512, (self.mode, 0, -1))] + +# +# registry + +Image.register_open("SGI", SgiImageFile, _accept) + +Image.register_extension("SGI", ".bw") +Image.register_extension("SGI", ".rgb") +Image.register_extension("SGI", ".rgba") + +Image.register_extension("SGI", ".sgi") # really? diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py new file mode 100644 index 000000000..e73d55918 --- /dev/null +++ b/PIL/SpiderImagePlugin.py @@ -0,0 +1,294 @@ +# +# The Python Imaging Library. +# +# SPIDER image file handling +# +# History: +# 2004-08-02 Created BB +# 2006-03-02 added save method +# 2006-03-13 added support for stack images +# +# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. +# Copyright (c) 2004 by William Baxter. +# Copyright (c) 2004 by Secret Labs AB. +# Copyright (c) 2004 by Fredrik Lundh. +# + +## +# Image plugin for the Spider image format. This format is is used +# by the SPIDER software, in processing image data from electron +# microscopy and tomography. +## + +# +# SpiderImagePlugin.py +# +# The Spider image format is used by SPIDER software, in processing +# image data from electron microscopy and tomography. +# +# Spider home page: +# http://www.wadsworth.org/spider_doc/spider/docs/spider.html +# +# Details about the Spider image format: +# http://www.wadsworth.org/spider_doc/spider/docs/image_doc.html +# + +import Image, ImageFile +import os, struct, sys + +def isInt(f): + try: + i = int(f) + if f-i == 0: return 1 + else: return 0 + except: + return 0 + +iforms = [1,3,-11,-12,-21,-22] + +# There is no magic number to identify Spider files, so just check a +# series of header locations to see if they have reasonable values. +# Returns no.of bytes in the header, if it is a valid Spider header, +# otherwise returns 0 + +def isSpiderHeader(t): + h = (99,) + t # add 1 value so can use spider header index start=1 + # header values 1,2,5,12,13,22,23 should be integers + for i in [1,2,5,12,13,22,23]: + if not isInt(h[i]): return 0 + # check iform + iform = int(h[5]) + if not iform in iforms: return 0 + # check other header values + labrec = int(h[13]) # no. records in file header + labbyt = int(h[22]) # total no. of bytes in header + lenbyt = int(h[23]) # record length in bytes + #print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt) + if labbyt != (labrec * lenbyt): return 0 + # looks like a valid header + return labbyt + +def isSpiderImage(filename): + fp = open(filename,'rb') + f = fp.read(92) # read 23 * 4 bytes + fp.close() + bigendian = 1 + t = struct.unpack('>23f',f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + bigendian = 0 + t = struct.unpack('<23f',f) # little-endian + hdrlen = isSpiderHeader(t) + return hdrlen + + +class SpiderImageFile(ImageFile.ImageFile): + + format = "SPIDER" + format_description = "Spider 2D image" + + def _open(self): + # check header + n = 27 * 4 # read 27 float values + f = self.fp.read(n) + + try: + self.bigendian = 1 + t = struct.unpack('>27f',f) # try big-endian first + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + self.bigendian = 0 + t = struct.unpack('<27f',f) # little-endian + hdrlen = isSpiderHeader(t) + if hdrlen == 0: + raise SyntaxError, "not a valid Spider file" + except struct.error: + raise SyntaxError, "not a valid Spider file" + + h = (99,) + t # add 1 value : spider header index starts at 1 + iform = int(h[5]) + if iform != 1: + raise SyntaxError, "not a Spider 2D image" + + self.size = int(h[12]), int(h[2]) # size in pixels (width, height) + self.istack = int(h[24]) + self.imgnumber = int(h[27]) + + if self.istack == 0 and self.imgnumber == 0: + # stk=0, img=0: a regular 2D image + offset = hdrlen + self.nimages = 1 + elif self.istack > 0 and self.imgnumber == 0: + # stk>0, img=0: Opening the stack for the first time + self.imgbytes = int(h[12]) * int(h[2]) * 4 + self.hdrlen = hdrlen + self.nimages = int(h[26]) + # Point to the first image in the stack + offset = hdrlen * 2 + self.imgnumber = 1 + elif self.istack == 0 and self.imgnumber > 0: + # stk=0, img>0: an image within the stack + offset = hdrlen + self.stkoffset + self.istack = 2 # So Image knows it's still a stack + else: + raise SyntaxError, "inconsistent stack header values" + + if self.bigendian: + self.rawmode = "F;32BF" + else: + self.rawmode = "F;32F" + self.mode = "F" + + self.tile = [("raw", (0, 0) + self.size, offset, + (self.rawmode, 0, 1))] + self.__fp = self.fp # FIXME: hack + + # 1st image index is zero (although SPIDER imgnumber starts at 1) + def tell(self): + if self.imgnumber < 1: + return 0 + else: + return self.imgnumber - 1 + + def seek(self, frame): + if self.istack == 0: + return + if frame >= self.nimages: + raise EOFError, "attempt to seek past end of file" + self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) + self.fp = self.__fp + self.fp.seek(self.stkoffset) + self._open() + + # returns a byte image after rescaling to 0..255 + def convert2byte(self, depth=255): + (min, max) = self.getextrema() + m = 1 + if max != min: + m = depth / (max-min) + b = -m * min + return self.point(lambda i, m=m, b=b: i * m + b).convert("L") + + # returns a ImageTk.PhotoImage object, after rescaling to 0..255 + def tkPhotoImage(self): + import ImageTk + return ImageTk.PhotoImage(self.convert2byte(), palette=256) + +# -------------------------------------------------------------------- +# Image series + +# given a list of filenames, return a list of images +def loadImageSeries(filelist=None): + " create a list of Image.images for use in montage " + if filelist == None or len(filelist) < 1: + return + + imglist = [] + for img in filelist: + if not os.path.exists(img): + print "unable to find %s" % img + continue + try: + im = Image.open(img).convert2byte() + except: + if not isSpiderImage(img): + print img + " is not a Spider image file" + continue + im.info['filename'] = img + imglist.append(im) + return imglist + +# -------------------------------------------------------------------- +# For saving images in Spider format + +def makeSpiderHeader(im): + nsam,nrow = im.size + lenbyt = nsam * 4 # There are labrec records in the header + labrec = 1024 / lenbyt + if 1024%lenbyt != 0: labrec += 1 + labbyt = labrec * lenbyt + hdr = [] + nvalues = labbyt / 4 + for i in range(nvalues): + hdr.append(0.0) + + if len(hdr) < 23: + return [] + + # NB these are Fortran indices + hdr[1] = 1.0 # nslice (=1 for an image) + hdr[2] = float(nrow) # number of rows per slice + hdr[5] = 1.0 # iform for 2D image + hdr[12] = float(nsam) # number of pixels per line + hdr[13] = float(labrec) # number of records in file header + hdr[22] = float(labbyt) # total number of bytes in header + hdr[23] = float(lenbyt) # record length in bytes + + # adjust for Fortran indexing + hdr = hdr[1:] + hdr.append(0.0) + # pack binary data into a string + hdrstr = [] + for v in hdr: + hdrstr.append(struct.pack('f',v)) + return hdrstr + +def _save(im, fp, filename): + if im.mode[0] != "F": + im = im.convert('F') + + hdr = makeSpiderHeader(im) + if len(hdr) < 256: + raise IOError, "Error creating Spider header" + + # write the SPIDER header + try: + fp = open(filename, 'wb') + except: + raise IOError, "Unable to open %s for writing" % filename + fp.writelines(hdr) + + rawmode = "F;32NF" #32-bit native floating point + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode,0,1))]) + + fp.close() + +def _save_spider(im, fp, filename): + # get the filename extension and register it with Image + fn, ext = os.path.splitext(filename) + Image.register_extension("SPIDER", ext) + _save(im, fp, filename) + +# -------------------------------------------------------------------- + +Image.register_open("SPIDER", SpiderImageFile) +Image.register_save("SPIDER", _save_spider) + +if __name__ == "__main__": + + if not sys.argv[1:]: + print "Syntax: python SpiderImagePlugin.py Spiderimage [outfile]" + sys.exit() + + filename = sys.argv[1] + if not isSpiderImage(filename): + print "input image must be in Spider format" + sys.exit() + + outfile = "" + if len(sys.argv[1:]) > 1: + outfile = sys.argv[2] + + im = Image.open(filename) + print "image: " + str(im) + print "format: " + str(im.format) + print "size: " + str(im.size) + print "mode: " + str(im.mode) + print "max, min: ", + print im.getextrema() + + if outfile != "": + # perform some image operation + im = im.transpose(Image.FLIP_LEFT_RIGHT) + print "saving a flipped version of %s as %s " % (os.path.basename(filename), outfile) + im.save(outfile, "SPIDER") diff --git a/PIL/SunImagePlugin.py b/PIL/SunImagePlugin.py new file mode 100644 index 000000000..d189562a4 --- /dev/null +++ b/PIL/SunImagePlugin.py @@ -0,0 +1,86 @@ +# +# The Python Imaging Library. +# $Id$ +# +# Sun image file handling +# +# History: +# 1995-09-10 fl Created +# 1996-05-28 fl Fixed 32-bit alignment +# 1998-12-29 fl Import ImagePalette module +# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) +# +# Copyright (c) 1997-2001 by Secret Labs AB +# Copyright (c) 1995-1996 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + + +import Image, ImageFile, ImagePalette + + +def i16(c): + return ord(c[1]) + (ord(c[0])<<8) + +def i32(c): + return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24) + + +def _accept(prefix): + return i32(prefix) == 0x59a66a95 + +## +# Image plugin for Sun raster files. + +class SunImageFile(ImageFile.ImageFile): + + format = "SUN" + format_description = "Sun Raster File" + + def _open(self): + + # HEAD + s = self.fp.read(32) + if i32(s) != 0x59a66a95: + raise SyntaxError, "not an SUN raster file" + + offset = 32 + + self.size = i32(s[4:8]), i32(s[8:12]) + + depth = i32(s[12:16]) + if depth == 1: + self.mode, rawmode = "1", "1;I" + elif depth == 8: + self.mode = rawmode = "L" + elif depth == 24: + self.mode, rawmode = "RGB", "BGR" + else: + raise SyntaxError, "unsupported mode" + + compression = i32(s[20:24]) + + if i32(s[24:28]) != 0: + length = i32(s[28:32]) + offset = offset + length + self.palette = ImagePalette.raw("RGB;L", self.fp.read(length)) + if self.mode == "L": + self.mode = rawmode = "P" + + stride = (((self.size[0] * depth + 7) / 8) + 3) & (~3) + + if compression == 1: + self.tile = [("raw", (0,0)+self.size, offset, (rawmode, stride))] + elif compression == 2: + self.tile = [("sun_rle", (0,0)+self.size, offset, rawmode)] + +# +# registry + +Image.register_open("SUN", SunImageFile, _accept) + +Image.register_extension("SUN", ".ras") diff --git a/PIL/TarIO.py b/PIL/TarIO.py new file mode 100644 index 000000000..d5de729cd --- /dev/null +++ b/PIL/TarIO.py @@ -0,0 +1,57 @@ +# +# The Python Imaging Library. +# $Id$ +# +# read files from within a tar file +# +# History: +# 95-06-18 fl Created +# 96-05-28 fl Open files in binary mode +# +# Copyright (c) Secret Labs AB 1997. +# Copyright (c) Fredrik Lundh 1995-96. +# +# See the README file for information on usage and redistribution. +# + +import ContainerIO +import string + +## +# A file object that provides read access to a given member of a TAR +# file. + +class TarIO(ContainerIO.ContainerIO): + + ## + # Create file object. + # + # @param tarfile Name of TAR file. + # @param file Name of member file. + + def __init__(self, tarfile, file): + + fh = open(tarfile, "rb") + + while 1: + + s = fh.read(512) + if len(s) != 512: + raise IOError, "unexpected end of tar file" + + name = s[:100] + i = string.find(name, chr(0)) + if i == 0: + raise IOError, "cannot find subfile" + if i > 0: + name = name[:i] + + size = string.atoi(s[124:136], 8) + + if file == name: + break + + fh.seek((size + 511) & (~511), 1) + + # Open region + ContainerIO.ContainerIO.__init__(self, fh, fh.tell(), size) diff --git a/PIL/TgaImagePlugin.py b/PIL/TgaImagePlugin.py new file mode 100644 index 000000000..3375991ca --- /dev/null +++ b/PIL/TgaImagePlugin.py @@ -0,0 +1,201 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TGA file handling +# +# History: +# 95-09-01 fl created (reads 24-bit files only) +# 97-01-04 fl support more TGA versions, including compressed images +# 98-07-04 fl fixed orientation and alpha layer bugs +# 98-09-11 fl fixed orientation for runlength decoder +# +# Copyright (c) Secret Labs AB 1997-98. +# Copyright (c) Fredrik Lundh 1995-97. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.3" + +import Image, ImageFile, ImagePalette + + +# +# -------------------------------------------------------------------- +# Read RGA file + +def i16(c): + return ord(c[0]) + (ord(c[1])<<8) + +def i32(c): + return ord(c[0]) + (ord(c[1])<<8) + (ord(c[2])<<16) + (ord(c[3])<<24) + + +MODES = { + # map imagetype/depth to rawmode + (1, 8): "P", + (3, 1): "1", + (3, 8): "L", + (2, 16): "BGR;5", + (2, 24): "BGR", + (2, 32): "BGRA", +} + + +def _accept(prefix): + return prefix[0] == "\0" + +## +# Image plugin for Targa files. + +class TgaImageFile(ImageFile.ImageFile): + + format = "TGA" + format_description = "Targa" + + def _open(self): + + # process header + s = self.fp.read(18) + + id = ord(s[0]) + + colormaptype = ord(s[1]) + imagetype = ord(s[2]) + + depth = ord(s[16]) + + flags = ord(s[17]) + + self.size = i16(s[12:]), i16(s[14:]) + + # validate header fields + if id != 0 or colormaptype not in (0, 1) or\ + self.size[0] <= 0 or self.size[1] <= 0 or\ + depth not in (1, 8, 16, 24, 32): + raise SyntaxError, "not a TGA file" + + # image mode + if imagetype in (3, 11): + self.mode = "L" + if depth == 1: + self.mode = "1" # ??? + elif imagetype in (1, 9): + self.mode = "P" + elif imagetype in (2, 10): + self.mode = "RGB" + if depth == 32: + self.mode = "RGBA" + else: + raise SyntaxError, "unknown TGA mode" + + # orientation + orientation = flags & 0x30 + if orientation == 0x20: + orientation = 1 + elif not orientation: + orientation = -1 + else: + raise SyntaxError, "unknown TGA orientation" + + self.info["orientation"] = orientation + + if imagetype & 8: + self.info["compression"] = "tga_rle" + + if colormaptype: + # read palette + start, size, mapdepth = i16(s[3:]), i16(s[5:]), i16(s[7:]) + if mapdepth == 16: + self.palette = ImagePalette.raw("BGR;16", + "\0"*2*start + self.fp.read(2*size)) + elif mapdepth == 24: + self.palette = ImagePalette.raw("BGR", + "\0"*3*start + self.fp.read(3*size)) + elif mapdepth == 32: + self.palette = ImagePalette.raw("BGRA", + "\0"*4*start + self.fp.read(4*size)) + + # setup tile descriptor + try: + rawmode = MODES[(imagetype&7, depth)] + if imagetype & 8: + # compressed + self.tile = [("tga_rle", (0, 0)+self.size, + self.fp.tell(), (rawmode, orientation, depth))] + else: + self.tile = [("raw", (0, 0)+self.size, + self.fp.tell(), (rawmode, 0, orientation))] + except KeyError: + pass # cannot decode + +# +# -------------------------------------------------------------------- +# Write TGA file + +def o16(i): + return chr(i&255) + chr(i>>8&255) + +def o32(i): + return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) + +SAVE = { + "1": ("1", 1, 0, 3), + "L": ("L", 8, 0, 3), + "P": ("P", 8, 1, 1), + "RGB": ("BGR", 24, 0, 2), + "RGBA": ("BGRA", 32, 0, 2), +} + +def _save(im, fp, filename, check=0): + + try: + rawmode, bits, colormaptype, imagetype = SAVE[im.mode] + except KeyError: + raise IOError("cannot write mode %s as TGA" % im.mode) + + if check: + return check + + if colormaptype: + colormapfirst, colormaplength, colormapentry = 0, 256, 24 + else: + colormapfirst, colormaplength, colormapentry = 0, 0, 0 + + if im.mode == "RGBA": + flags = 8 + else: + flags = 0 + + orientation = im.info.get("orientation", -1) + if orientation > 0: + flags = flags | 0x20 + + fp.write("\000" + + chr(colormaptype) + + chr(imagetype) + + o16(colormapfirst) + + o16(colormaplength) + + chr(colormapentry) + + o16(0) + + o16(0) + + o16(im.size[0]) + + o16(im.size[1]) + + chr(bits) + + chr(flags)) + + if colormaptype: + fp.write(im.im.getpalette("RGB", "BGR")) + + ImageFile._save(im, fp, [("raw", (0,0)+im.size, 0, (rawmode, 0, orientation))]) + +# +# -------------------------------------------------------------------- +# Registry + +Image.register_open("TGA", TgaImageFile, _accept) +Image.register_save("TGA", _save) + +Image.register_extension("TGA", ".tga") diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py new file mode 100644 index 000000000..8a0fde9cd --- /dev/null +++ b/PIL/TiffImagePlugin.py @@ -0,0 +1,860 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF file handling +# +# TIFF is a flexible, if somewhat aged, image file format originally +# defined by Aldus. Although TIFF supports a wide variety of pixel +# layouts and compression methods, the name doesn't really stand for +# "thousands of incompatible file formats," it just feels that way. +# +# To read TIFF data from a stream, the stream must be seekable. For +# progressive decoding, make sure to use TIFF files where the tag +# directory is placed first in the file. +# +# History: +# 1995-09-01 fl Created +# 1996-05-04 fl Handle JPEGTABLES tag +# 1996-05-18 fl Fixed COLORMAP support +# 1997-01-05 fl Fixed PREDICTOR support +# 1997-08-27 fl Added support for rational tags (from Perry Stoll) +# 1998-01-10 fl Fixed seek/tell (from Jan Blom) +# 1998-07-15 fl Use private names for internal variables +# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) +# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) +# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) +# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) +# 2001-12-18 fl Added workaround for broken Matrox library +# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) +# 2003-05-19 fl Check FILLORDER tag +# 2003-09-26 fl Added RGBa support +# 2004-02-24 fl Added DPI support; fixed rational write support +# 2005-02-07 fl Added workaround for broken Corel Draw 10 files +# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) +# +# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. +# Copyright (c) 1995-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "1.3.5" + +import Image, ImageFile +import ImagePalette + +import array, string, sys + +II = "II" # little-endian (intel-style) +MM = "MM" # big-endian (motorola-style) + +try: + if sys.byteorder == "little": + native_prefix = II + else: + native_prefix = MM +except AttributeError: + if ord(array.array("i",[1]).tostring()[0]): + native_prefix = II + else: + native_prefix = MM + +# +# -------------------------------------------------------------------- +# Read TIFF files + +def il16(c,o=0): + return ord(c[o]) + (ord(c[o+1])<<8) +def il32(c,o=0): + return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) +def ol16(i): + return chr(i&255) + chr(i>>8&255) +def ol32(i): + return chr(i&255) + chr(i>>8&255) + chr(i>>16&255) + chr(i>>24&255) + +def ib16(c,o=0): + return ord(c[o+1]) + (ord(c[o])<<8) +def ib32(c,o=0): + return ord(c[o+3]) + (ord(c[o+2])<<8) + (ord(c[o+1])<<16) + (ord(c[o])<<24) +def ob16(i): + return chr(i>>8&255) + chr(i&255) +def ob32(i): + return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255) + +# a few tag names, just to make the code below a bit more readable +IMAGEWIDTH = 256 +IMAGELENGTH = 257 +BITSPERSAMPLE = 258 +COMPRESSION = 259 +PHOTOMETRIC_INTERPRETATION = 262 +FILLORDER = 266 +IMAGEDESCRIPTION = 270 +STRIPOFFSETS = 273 +SAMPLESPERPIXEL = 277 +ROWSPERSTRIP = 278 +STRIPBYTECOUNTS = 279 +X_RESOLUTION = 282 +Y_RESOLUTION = 283 +PLANAR_CONFIGURATION = 284 +RESOLUTION_UNIT = 296 +SOFTWARE = 305 +DATE_TIME = 306 +ARTIST = 315 +PREDICTOR = 317 +COLORMAP = 320 +TILEOFFSETS = 324 +EXTRASAMPLES = 338 +SAMPLEFORMAT = 339 +JPEGTABLES = 347 +COPYRIGHT = 33432 +IPTC_NAA_CHUNK = 33723 # newsphoto properties +PHOTOSHOP_CHUNK = 34377 # photoshop properties +ICCPROFILE = 34675 +EXIFIFD = 34665 +XMP = 700 + +COMPRESSION_INFO = { + # Compression => pil compression name + 1: "raw", + 2: "tiff_ccitt", + 3: "group3", + 4: "group4", + 5: "tiff_lzw", + 6: "tiff_jpeg", # obsolete + 7: "jpeg", + 32771: "tiff_raw_16", # 16-bit padding + 32773: "packbits" +} + +OPEN_INFO = { + # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, + # ExtraSamples) => mode, rawmode + (II, 0, 1, 1, (1,), ()): ("1", "1;I"), + (II, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (II, 0, 1, 1, (8,), ()): ("L", "L;I"), + (II, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (II, 1, 1, 1, (1,), ()): ("1", "1"), + (II, 1, 1, 2, (1,), ()): ("1", "1;R"), + (II, 1, 1, 1, (8,), ()): ("L", "L"), + (II, 1, 1, 1, (8,8), (2,)): ("LA", "LA"), + (II, 1, 1, 2, (8,), ()): ("L", "L;R"), + (II, 1, 1, 1, (16,), ()): ("I;16", "I;16"), + (II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"), + (II, 1, 2, 1, (32,), ()): ("I", "I;32S"), + (II, 1, 3, 1, (32,), ()): ("F", "F;32F"), + (II, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"), + (II, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"), + (II, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"), + (II, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"), + (II, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"), + (II, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10 + (II, 3, 1, 1, (1,), ()): ("P", "P;1"), + (II, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (II, 3, 1, 1, (2,), ()): ("P", "P;2"), + (II, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (II, 3, 1, 1, (4,), ()): ("P", "P;4"), + (II, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (II, 3, 1, 1, (8,), ()): ("P", "P"), + (II, 3, 1, 1, (8,8), (2,)): ("PA", "PA"), + (II, 3, 1, 2, (8,), ()): ("P", "P;R"), + (II, 5, 1, 1, (8,8,8,8), ()): ("CMYK", "CMYK"), + (II, 6, 1, 1, (8,8,8), ()): ("YCbCr", "YCbCr"), + (II, 8, 1, 1, (8,8,8), ()): ("LAB", "LAB"), + + (MM, 0, 1, 1, (1,), ()): ("1", "1;I"), + (MM, 0, 1, 2, (1,), ()): ("1", "1;IR"), + (MM, 0, 1, 1, (8,), ()): ("L", "L;I"), + (MM, 0, 1, 2, (8,), ()): ("L", "L;IR"), + (MM, 1, 1, 1, (1,), ()): ("1", "1"), + (MM, 1, 1, 2, (1,), ()): ("1", "1;R"), + (MM, 1, 1, 1, (8,), ()): ("L", "L"), + (MM, 1, 1, 1, (8,8), (2,)): ("LA", "LA"), + (MM, 1, 1, 2, (8,), ()): ("L", "L;R"), + (MM, 1, 1, 1, (16,), ()): ("I;16B", "I;16B"), + (MM, 1, 2, 1, (16,), ()): ("I;16BS", "I;16BS"), + (MM, 1, 2, 1, (32,), ()): ("I;32BS", "I;32BS"), + (MM, 1, 3, 1, (32,), ()): ("F;32BF", "F;32BF"), + (MM, 2, 1, 1, (8,8,8), ()): ("RGB", "RGB"), + (MM, 2, 1, 2, (8,8,8), ()): ("RGB", "RGB;R"), + (MM, 2, 1, 1, (8,8,8,8), (0,)): ("RGBX", "RGBX"), + (MM, 2, 1, 1, (8,8,8,8), (1,)): ("RGBA", "RGBa"), + (MM, 2, 1, 1, (8,8,8,8), (2,)): ("RGBA", "RGBA"), + (MM, 2, 1, 1, (8,8,8,8), (999,)): ("RGBA", "RGBA"), # corel draw 10 + (MM, 3, 1, 1, (1,), ()): ("P", "P;1"), + (MM, 3, 1, 2, (1,), ()): ("P", "P;1R"), + (MM, 3, 1, 1, (2,), ()): ("P", "P;2"), + (MM, 3, 1, 2, (2,), ()): ("P", "P;2R"), + (MM, 3, 1, 1, (4,), ()): ("P", "P;4"), + (MM, 3, 1, 2, (4,), ()): ("P", "P;4R"), + (MM, 3, 1, 1, (8,), ()): ("P", "P"), + (MM, 3, 1, 1, (8,8), (2,)): ("PA", "PA"), + (MM, 3, 1, 2, (8,), ()): ("P", "P;R"), + (MM, 5, 1, 1, (8,8,8,8), ()): ("CMYK", "CMYK"), + (MM, 6, 1, 1, (8,8,8), ()): ("YCbCr", "YCbCr"), + (MM, 8, 1, 1, (8,8,8), ()): ("LAB", "LAB"), + +} + +PREFIXES = ["MM\000\052", "II\052\000", "II\xBC\000"] + +def _accept(prefix): + return prefix[:4] in PREFIXES + +## +# Wrapper for TIFF IFDs. + +class ImageFileDirectory: + + # represents a TIFF tag directory. to speed things up, + # we don't decode tags unless they're asked for. + + def __init__(self, prefix): + self.prefix = prefix[:2] + if self.prefix == MM: + self.i16, self.i32 = ib16, ib32 + self.o16, self.o32 = ob16, ob32 + elif self.prefix == II: + self.i16, self.i32 = il16, il32 + self.o16, self.o32 = ol16, ol32 + else: + raise SyntaxError("not a TIFF IFD") + self.reset() + + def reset(self): + self.tags = {} + self.tagdata = {} + self.tagtype = {} # added 2008-06-05 by Florian Hoech + self.next = None + + # dictionary API (sort of) + + def keys(self): + return self.tagdata.keys() + self.tags.keys() + + def items(self): + items = self.tags.items() + for tag in self.tagdata.keys(): + items.append((tag, self[tag])) + return items + + def __len__(self): + return len(self.tagdata) + len(self.tags) + + def __getitem__(self, tag): + try: + return self.tags[tag] + except KeyError: + type, data = self.tagdata[tag] # unpack on the fly + size, handler = self.load_dispatch[type] + self.tags[tag] = data = handler(self, data) + del self.tagdata[tag] + return data + + def get(self, tag, default=None): + try: + return self[tag] + except KeyError: + return default + + def getscalar(self, tag, default=None): + try: + value = self[tag] + if len(value) != 1: + if tag == SAMPLEFORMAT: + # work around broken (?) matrox library + # (from Ted Wright, via Bob Klimek) + raise KeyError # use default + raise ValueError, "not a scalar" + return value[0] + except KeyError: + if default is None: + raise + return default + + def has_key(self, tag): + return self.tags.has_key(tag) or self.tagdata.has_key(tag) + + def __setitem__(self, tag, value): + if type(value) is not type(()): + value = (value,) + self.tags[tag] = value + + # load primitives + + load_dispatch = {} + + def load_byte(self, data): + l = [] + for i in range(len(data)): + l.append(ord(data[i])) + return tuple(l) + load_dispatch[1] = (1, load_byte) + + def load_string(self, data): + if data[-1:] == '\0': + data = data[:-1] + return data + load_dispatch[2] = (1, load_string) + + def load_short(self, data): + l = [] + for i in range(0, len(data), 2): + l.append(self.i16(data, i)) + return tuple(l) + load_dispatch[3] = (2, load_short) + + def load_long(self, data): + l = [] + for i in range(0, len(data), 4): + l.append(self.i32(data, i)) + return tuple(l) + load_dispatch[4] = (4, load_long) + + def load_rational(self, data): + l = [] + for i in range(0, len(data), 8): + l.append((self.i32(data, i), self.i32(data, i+4))) + return tuple(l) + load_dispatch[5] = (8, load_rational) + + def load_float(self, data): + a = array.array("f", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[11] = (4, load_float) + + def load_double(self, data): + a = array.array("d", data) + if self.prefix != native_prefix: + a.byteswap() + return tuple(a) + load_dispatch[12] = (8, load_double) + + def load_undefined(self, data): + # Untyped data + return data + load_dispatch[7] = (1, load_undefined) + + def load(self, fp): + # load tag dictionary + + self.reset() + + i16 = self.i16 + i32 = self.i32 + + for i in range(i16(fp.read(2))): + + ifd = fp.read(12) + + tag, typ = i16(ifd), i16(ifd, 2) + + if Image.DEBUG: + import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print "tag: %s (%d)" % (tagname, tag), + print "- type: %s (%d)" % (typname, typ), + + try: + dispatch = self.load_dispatch[typ] + except KeyError: + if Image.DEBUG: + print "- unsupported type", typ + continue # ignore unsupported type + + size, handler = dispatch + + size = size * i32(ifd, 4) + + # Get and expand tag value + if size > 4: + here = fp.tell() + fp.seek(i32(ifd, 8)) + data = ImageFile._safe_read(fp, size) + fp.seek(here) + else: + data = ifd[8:8+size] + + if len(data) != size: + raise IOError, "not enough data" + + self.tagdata[tag] = typ, data + self.tagtype[tag] = typ + + if Image.DEBUG: + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): + print "- value: " % size + else: + print "- value:", self[tag] + + self.next = i32(fp.read(4)) + + # save primitives + + def save(self, fp): + + o16 = self.o16 + o32 = self.o32 + + fp.write(o16(len(self.tags))) + + # always write in ascending tag order + tags = self.tags.items() + tags.sort() + + directory = [] + append = directory.append + + offset = fp.tell() + len(self.tags) * 12 + 4 + + stripoffsets = None + + # pass 1: convert tags to binary format + for tag, value in tags: + + typ = None + + if self.tagtype.has_key(tag): + typ = self.tagtype[tag] + + if typ == 1: + # byte data + data = value = string.join(map(chr, value), "") + elif typ == 7: + # untyped data + data = value = string.join(value, "") + elif type(value[0]) is type(""): + # string data + typ = 2 + data = value = string.join(value, "\0") + "\0" + else: + # integer data + if tag == STRIPOFFSETS: + stripoffsets = len(directory) + typ = 4 # to avoid catch-22 + elif tag in (X_RESOLUTION, Y_RESOLUTION): + # identify rational data fields + typ = 5 + elif not typ: + typ = 3 + for v in value: + if v >= 65536: + typ = 4 + if typ == 3: + data = string.join(map(o16, value), "") + else: + data = string.join(map(o32, value), "") + + if Image.DEBUG: + import TiffTags + tagname = TiffTags.TAGS.get(tag, "unknown") + typname = TiffTags.TYPES.get(typ, "unknown") + print "save: %s (%d)" % (tagname, tag), + print "- type: %s (%d)" % (typname, typ), + if tag in (COLORMAP, IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, ICCPROFILE, XMP): + size = len(data) + print "- value: " % size + else: + print "- value:", value + + # figure out if data fits into the directory + if len(data) == 4: + append((tag, typ, len(value), data, "")) + elif len(data) < 4: + append((tag, typ, len(value), data + (4-len(data))*"\0", "")) + else: + count = len(value) + if typ == 5: + count = count / 2 # adjust for rational data field + append((tag, typ, count, o32(offset), data)) + offset = offset + len(data) + if offset & 1: + offset = offset + 1 # word padding + + # update strip offset data to point beyond auxiliary data + if stripoffsets is not None: + tag, typ, count, value, data = directory[stripoffsets] + assert not data, "multistrip support not yet implemented" + value = o32(self.i32(value) + offset) + directory[stripoffsets] = tag, typ, count, value, data + + # pass 2: write directory to file + for tag, typ, count, value, data in directory: + if Image.DEBUG > 1: + print tag, typ, count, repr(value), repr(data) + fp.write(o16(tag) + o16(typ) + o32(count) + value) + + # -- overwrite here for multi-page -- + fp.write("\0\0\0\0") # end of directory + + # pass 3: write auxiliary data to file + for tag, typ, count, value, data in directory: + fp.write(data) + if len(data) & 1: + fp.write("\0") + + return offset + +## +# Image plugin for TIFF files. + +class TiffImageFile(ImageFile.ImageFile): + + format = "TIFF" + format_description = "Adobe TIFF" + + def _open(self): + "Open the first image in a TIFF file" + + # Header + ifh = self.fp.read(8) + + if ifh[:4] not in PREFIXES: + raise SyntaxError, "not a TIFF file" + + # image file directory (tag dictionary) + self.tag = self.ifd = ImageFileDirectory(ifh[:2]) + + # setup frame pointers + self.__first = self.__next = self.ifd.i32(ifh, 4) + self.__frame = -1 + self.__fp = self.fp + + # and load the first frame + self._seek(0) + + def seek(self, frame): + "Select a given frame as current image" + + if frame < 0: + frame = 0 + self._seek(frame) + + def tell(self): + "Return the current frame number" + + return self._tell() + + def _seek(self, frame): + + self.fp = self.__fp + if frame < self.__frame: + # rewind file + self.__frame = -1 + self.__next = self.__first + while self.__frame < frame: + if not self.__next: + raise EOFError, "no more images in TIFF file" + self.fp.seek(self.__next) + self.tag.load(self.fp) + self.__next = self.tag.next + self.__frame = self.__frame + 1 + self._setup() + + def _tell(self): + + return self.__frame + + def _decoder(self, rawmode, layer): + "Setup decoder contexts" + + args = None + if rawmode == "RGB" and self._planar_configuration == 2: + rawmode = rawmode[layer] + compression = self._compression + if compression == "raw": + args = (rawmode, 0, 1) + elif compression == "jpeg": + args = rawmode, "" + if self.tag.has_key(JPEGTABLES): + # Hack to handle abbreviated JPEG headers + self.tile_prefix = self.tag[JPEGTABLES] + elif compression == "packbits": + args = rawmode + elif compression == "tiff_lzw": + args = rawmode + if self.tag.has_key(317): + # Section 14: Differencing Predictor + self.decoderconfig = (self.tag[PREDICTOR][0],) + + if self.tag.has_key(ICCPROFILE): + self.info['icc_profile'] = self.tag[ICCPROFILE] + + return args + + def _setup(self): + "Setup this image object based on current tags" + + if self.tag.has_key(0xBC01): + raise IOError, "Windows Media Photo files not yet supported" + + getscalar = self.tag.getscalar + + # extract relevant tags + self._compression = COMPRESSION_INFO[getscalar(COMPRESSION, 1)] + self._planar_configuration = getscalar(PLANAR_CONFIGURATION, 1) + + # photometric is a required tag, but not everyone is reading + # the specification + photo = getscalar(PHOTOMETRIC_INTERPRETATION, 0) + + fillorder = getscalar(FILLORDER, 1) + + if Image.DEBUG: + print "*** Summary ***" + print "- compression:", self._compression + print "- photometric_interpretation:", photo + print "- planar_configuration:", self._planar_configuration + print "- fill_order:", fillorder + + # size + xsize = getscalar(IMAGEWIDTH) + ysize = getscalar(IMAGELENGTH) + self.size = xsize, ysize + + if Image.DEBUG: + print "- size:", self.size + + format = getscalar(SAMPLEFORMAT, 1) + + # mode: check photometric interpretation and bits per pixel + key = ( + self.tag.prefix, photo, format, fillorder, + self.tag.get(BITSPERSAMPLE, (1,)), + self.tag.get(EXTRASAMPLES, ()) + ) + if Image.DEBUG: + print "format key:", key + try: + self.mode, rawmode = OPEN_INFO[key] + except KeyError: + if Image.DEBUG: + print "- unsupported format" + raise SyntaxError, "unknown pixel mode" + + if Image.DEBUG: + print "- raw mode:", rawmode + print "- pil mode:", self.mode + + self.info["compression"] = self._compression + + xres = getscalar(X_RESOLUTION, (1, 1)) + yres = getscalar(Y_RESOLUTION, (1, 1)) + + if xres and yres: + xres = xres[0] / (xres[1] or 1) + yres = yres[0] / (yres[1] or 1) + resunit = getscalar(RESOLUTION_UNIT, 1) + if resunit == 2: # dots per inch + self.info["dpi"] = xres, yres + elif resunit == 3: # dots per centimeter. convert to dpi + self.info["dpi"] = xres * 2.54, yres * 2.54 + else: # No absolute unit of measurement + self.info["resolution"] = xres, yres + + # build tile descriptors + x = y = l = 0 + self.tile = [] + if self.tag.has_key(STRIPOFFSETS): + # striped image + h = getscalar(ROWSPERSTRIP, ysize) + w = self.size[0] + a = None + for o in self.tag[STRIPOFFSETS]: + if not a: + a = self._decoder(rawmode, l) + self.tile.append( + (self._compression, + (0, min(y, ysize), w, min(y+h, ysize)), + o, a)) + y = y + h + if y >= self.size[1]: + x = y = 0 + l = l + 1 + a = None + elif self.tag.has_key(TILEOFFSETS): + # tiled image + w = getscalar(322) + h = getscalar(323) + a = None + for o in self.tag[TILEOFFSETS]: + if not a: + a = self._decoder(rawmode, l) + # FIXME: this doesn't work if the image size + # is not a multiple of the tile size... + self.tile.append( + (self._compression, + (x, y, x+w, y+h), + o, a)) + x = x + w + if x >= self.size[0]: + x, y = 0, y + h + if y >= self.size[1]: + x = y = 0 + l = l + 1 + a = None + else: + if Image.DEBUG: + print "- unsupported data organization" + raise SyntaxError("unknown data organization") + + # fixup palette descriptor + + if self.mode == "P": + palette = map(lambda a: chr(a / 256), self.tag[COLORMAP]) + self.palette = ImagePalette.raw("RGB;L", string.join(palette, "")) +# +# -------------------------------------------------------------------- +# Write TIFF files + +# little endian is default except for image modes with explict big endian byte-order + +SAVE_INFO = { + # mode => rawmode, byteorder, photometrics, sampleformat, bitspersample, extra + "1": ("1", II, 1, 1, (1,), None), + "L": ("L", II, 1, 1, (8,), None), + "LA": ("LA", II, 1, 1, (8,8), 2), + "P": ("P", II, 3, 1, (8,), None), + "PA": ("PA", II, 3, 1, (8,8), 2), + "I": ("I;32S", II, 1, 2, (32,), None), + "I;16": ("I;16", II, 1, 1, (16,), None), + "I;16S": ("I;16S", II, 1, 2, (16,), None), + "F": ("F;32F", II, 1, 3, (32,), None), + "RGB": ("RGB", II, 2, 1, (8,8,8), None), + "RGBX": ("RGBX", II, 2, 1, (8,8,8,8), 0), + "RGBA": ("RGBA", II, 2, 1, (8,8,8,8), 2), + "CMYK": ("CMYK", II, 5, 1, (8,8,8,8), None), + "YCbCr": ("YCbCr", II, 6, 1, (8,8,8), None), + "LAB": ("LAB", II, 8, 1, (8,8,8), None), + + "I;32BS": ("I;32BS", MM, 1, 2, (32,), None), + "I;16B": ("I;16B", MM, 1, 1, (16,), None), + "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), + "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), +} + +def _cvt_res(value): + # convert value to TIFF rational number -- (numerator, denominator) + if type(value) in (type([]), type(())): + assert(len(value) % 2 == 0) + return value + if type(value) == type(1): + return (value, 1) + value = float(value) + return (int(value * 65536), 65536) + +def _save(im, fp, filename): + + try: + rawmode, prefix, photo, format, bits, extra = SAVE_INFO[im.mode] + except KeyError: + raise IOError, "cannot write mode %s as TIFF" % im.mode + + ifd = ImageFileDirectory(prefix) + + # -- multi-page -- skip TIFF header on subsequent pages + if fp.tell() == 0: + # tiff header (write via IFD to get everything right) + # PIL always starts the first IFD at offset 8 + fp.write(ifd.prefix + ifd.o16(42) + ifd.o32(8)) + + ifd[IMAGEWIDTH] = im.size[0] + ifd[IMAGELENGTH] = im.size[1] + + # additions written by Greg Couch, gregc@cgl.ucsf.edu + # inspired by image-sig posting from Kevin Cazabon, kcazabon@home.com + if hasattr(im, 'tag'): + # preserve tags from original TIFF image file + for key in (RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION): + if im.tag.tagdata.has_key(key): + ifd[key] = im.tag.tagdata.get(key) + # preserve some more tags from original TIFF image file + # -- 2008-06-06 Florian Hoech + ifd.tagtype = im.tag.tagtype + for key in (IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP): + if im.tag.has_key(key): + ifd[key] = im.tag[key] + # preserve ICC profile (should also work when saving other formats + # which support profiles as TIFF) -- 2008-06-06 Florian Hoech + if im.info.has_key("icc_profile"): + ifd[ICCPROFILE] = im.info["icc_profile"] + if im.encoderinfo.has_key("description"): + ifd[IMAGEDESCRIPTION] = im.encoderinfo["description"] + if im.encoderinfo.has_key("resolution"): + ifd[X_RESOLUTION] = ifd[Y_RESOLUTION] \ + = _cvt_res(im.encoderinfo["resolution"]) + if im.encoderinfo.has_key("x resolution"): + ifd[X_RESOLUTION] = _cvt_res(im.encoderinfo["x resolution"]) + if im.encoderinfo.has_key("y resolution"): + ifd[Y_RESOLUTION] = _cvt_res(im.encoderinfo["y resolution"]) + if im.encoderinfo.has_key("resolution unit"): + unit = im.encoderinfo["resolution unit"] + if unit == "inch": + ifd[RESOLUTION_UNIT] = 2 + elif unit == "cm" or unit == "centimeter": + ifd[RESOLUTION_UNIT] = 3 + else: + ifd[RESOLUTION_UNIT] = 1 + if im.encoderinfo.has_key("software"): + ifd[SOFTWARE] = im.encoderinfo["software"] + if im.encoderinfo.has_key("date time"): + ifd[DATE_TIME] = im.encoderinfo["date time"] + if im.encoderinfo.has_key("artist"): + ifd[ARTIST] = im.encoderinfo["artist"] + if im.encoderinfo.has_key("copyright"): + ifd[COPYRIGHT] = im.encoderinfo["copyright"] + + dpi = im.encoderinfo.get("dpi") + if dpi: + ifd[RESOLUTION_UNIT] = 2 + ifd[X_RESOLUTION] = _cvt_res(dpi[0]) + ifd[Y_RESOLUTION] = _cvt_res(dpi[1]) + + if bits != (1,): + ifd[BITSPERSAMPLE] = bits + if len(bits) != 1: + ifd[SAMPLESPERPIXEL] = len(bits) + if extra is not None: + ifd[EXTRASAMPLES] = extra + if format != 1: + ifd[SAMPLEFORMAT] = format + + ifd[PHOTOMETRIC_INTERPRETATION] = photo + + if im.mode == "P": + lut = im.im.getpalette("RGB", "RGB;L") + ifd[COLORMAP] = tuple(map(lambda v: ord(v) * 256, lut)) + + # data orientation + stride = len(bits) * ((im.size[0]*bits[0]+7)/8) + ifd[ROWSPERSTRIP] = im.size[1] + ifd[STRIPBYTECOUNTS] = stride * im.size[1] + ifd[STRIPOFFSETS] = 0 # this is adjusted by IFD writer + ifd[COMPRESSION] = 1 # no compression + + offset = ifd.save(fp) + + ImageFile._save(im, fp, [ + ("raw", (0,0)+im.size, offset, (rawmode, stride, 1)) + ]) + + + # -- helper for multi-page save -- + if im.encoderinfo.has_key("_debug_multipage"): + #just to access o32 and o16 (using correct byte order) + im._debug_multipage = ifd + +# +# -------------------------------------------------------------------- +# Register + +Image.register_open("TIFF", TiffImageFile, _accept) +Image.register_save("TIFF", _save) + +Image.register_extension("TIFF", ".tif") +Image.register_extension("TIFF", ".tiff") + +Image.register_mime("TIFF", "image/tiff") diff --git a/PIL/TiffTags.py b/PIL/TiffTags.py new file mode 100644 index 000000000..33fd20941 --- /dev/null +++ b/PIL/TiffTags.py @@ -0,0 +1,209 @@ +# +# The Python Imaging Library. +# $Id$ +# +# TIFF tags +# +# This module provides clear-text names for various well-known +# TIFF tags. the TIFF codec works just fine without it. +# +# Copyright (c) Secret Labs AB 1999. +# +# See the README file for information on usage and redistribution. +# + +## +# This module provides constants and clear-text names for various +# well-known TIFF tags. +## + +## +# Map tag numbers (or tag number, tag value tuples) to tag names. + +TAGS = { + + 254: "NewSubfileType", + 255: "SubfileType", + 256: "ImageWidth", + 257: "ImageLength", + 258: "BitsPerSample", + + 259: "Compression", + (259, 1): "Uncompressed", + (259, 2): "CCITT 1d", + (259, 3): "Group 3 Fax", + (259, 4): "Group 4 Fax", + (259, 5): "LZW", + (259, 6): "JPEG", + (259, 32773): "PackBits", + + 262: "PhotometricInterpretation", + (262, 0): "WhiteIsZero", + (262, 1): "BlackIsZero", + (262, 2): "RGB", + (262, 3): "RGB Palette", + (262, 4): "Transparency Mask", + (262, 5): "CMYK", + (262, 6): "YCbCr", + (262, 8): "CieLAB", + (262, 32803): "CFA", # TIFF/EP, Adobe DNG + (262, 32892): "LinearRaw", # Adobe DNG + + 263: "Thresholding", + 264: "CellWidth", + 265: "CellHeight", + 266: "FillOrder", + 269: "DocumentName", + + 270: "ImageDescription", + 271: "Make", + 272: "Model", + 273: "StripOffsets", + 274: "Orientation", + 277: "SamplesPerPixel", + 278: "RowsPerStrip", + 279: "StripByteCounts", + + 280: "MinSampleValue", + 281: "MaxSampleValue", + 282: "XResolution", + 283: "YResolution", + 284: "PlanarConfiguration", + (284, 1): "Contigous", + (284, 2): "Separate", + + 285: "PageName", + 286: "XPosition", + 287: "YPosition", + 288: "FreeOffsets", + 289: "FreeByteCounts", + + 290: "GrayResponseUnit", + 291: "GrayResponseCurve", + 292: "T4Options", + 293: "T6Options", + 296: "ResolutionUnit", + 297: "PageNumber", + + 301: "TransferFunction", + 305: "Software", + 306: "DateTime", + + 315: "Artist", + 316: "HostComputer", + 317: "Predictor", + 318: "WhitePoint", + 319: "PrimaryChromaticies", + + 320: "ColorMap", + 321: "HalftoneHints", + 322: "TileWidth", + 323: "TileLength", + 324: "TileOffsets", + 325: "TileByteCounts", + + 332: "InkSet", + 333: "InkNames", + 334: "NumberOfInks", + 336: "DotRange", + 337: "TargetPrinter", + 338: "ExtraSamples", + 339: "SampleFormat", + + 340: "SMinSampleValue", + 341: "SMaxSampleValue", + 342: "TransferRange", + + 347: "JPEGTables", + + # obsolete JPEG tags + 512: "JPEGProc", + 513: "JPEGInterchangeFormat", + 514: "JPEGInterchangeFormatLength", + 515: "JPEGRestartInterval", + 517: "JPEGLosslessPredictors", + 518: "JPEGPointTransforms", + 519: "JPEGQTables", + 520: "JPEGDCTables", + 521: "JPEGACTables", + + 529: "YCbCrCoefficients", + 530: "YCbCrSubSampling", + 531: "YCbCrPositioning", + 532: "ReferenceBlackWhite", + + # XMP + 700: "XMP", + + 33432: "Copyright", + + # various extensions (should check specs for "official" names) + 33723: "IptcNaaInfo", + 34377: "PhotoshopInfo", + + # Exif IFD + 34665: "ExifIFD", + + # ICC Profile + 34675: "ICCProfile", + + # Adobe DNG + 50706: "DNGVersion", + 50707: "DNGBackwardVersion", + 50708: "UniqueCameraModel", + 50709: "LocalizedCameraModel", + 50710: "CFAPlaneColor", + 50711: "CFALayout", + 50712: "LinearizationTable", + 50713: "BlackLevelRepeatDim", + 50714: "BlackLevel", + 50715: "BlackLevelDeltaH", + 50716: "BlackLevelDeltaV", + 50717: "WhiteLevel", + 50718: "DefaultScale", + 50741: "BestQualityScale", + 50719: "DefaultCropOrigin", + 50720: "DefaultCropSize", + 50778: "CalibrationIlluminant1", + 50779: "CalibrationIlluminant2", + 50721: "ColorMatrix1", + 50722: "ColorMatrix2", + 50723: "CameraCalibration1", + 50724: "CameraCalibration2", + 50725: "ReductionMatrix1", + 50726: "ReductionMatrix2", + 50727: "AnalogBalance", + 50728: "AsShotNeutral", + 50729: "AsShotWhiteXY", + 50730: "BaselineExposure", + 50731: "BaselineNoise", + 50732: "BaselineSharpness", + 50733: "BayerGreenSplit", + 50734: "LinearResponseLimit", + 50735: "CameraSerialNumber", + 50736: "LensInfo", + 50737: "ChromaBlurRadius", + 50738: "AntiAliasStrength", + 50740: "DNGPrivateData", + 50741: "MakerNoteSafety", +} + +## +# Map type numbers to type names. + +TYPES = { + + 1: "byte", + 2: "ascii", + 3: "short", + 4: "long", + 5: "rational", + 6: "signed byte", + 7: "undefined", + 8: "signed short", + 9: "signed long", + 10: "signed rational", + 11: "float", + 12: "double", + +} diff --git a/PIL/WalImageFile.py b/PIL/WalImageFile.py new file mode 100644 index 000000000..fb8c38ab4 --- /dev/null +++ b/PIL/WalImageFile.py @@ -0,0 +1,126 @@ +# -*- coding: iso-8859-1 -*- +# +# The Python Imaging Library. +# $Id$ +# +# WAL file handling +# +# History: +# 2003-04-23 fl created +# +# Copyright (c) 2003 by Fredrik Lundh. +# +# See the README file for information on usage and redistribution. +# + +# NOTE: This format cannot be automatically recognized, so the reader +# is not registered for use with Image.open(). To open a WEL file, use +# the WalImageFile.open() function instead. + +# This reader is based on the specification available from: +# http://www.flipcode.com/tutorials/tut_q2levels.shtml +# and has been tested with a few sample files found using google. + +import Image + +def i32(c, o=0): + return ord(c[o])+(ord(c[o+1])<<8)+(ord(c[o+2])<<16)+(ord(c[o+3])<<24) + +## +# Load texture from a Quake2 WAL texture file. +#

+# By default, a Quake2 standard palette is attached to the texture. +# To override the palette, use the putpalette method. +# +# @param filename WAL file name, or an opened file handle. +# @return An image instance. + +def open(filename): + # FIXME: modify to return a WalImageFile instance instead of + # plain Image object ? + + if hasattr(filename, "read"): + fp = filename + else: + import __builtin__ + fp = __builtin__.open(filename, "rb") + + # read header fields + header = fp.read(32+24+32+12) + size = i32(header, 32), i32(header, 36) + offset = i32(header, 40) + + # load pixel data + fp.seek(offset) + + im = Image.fromstring("P", size, fp.read(size[0] * size[1])) + im.putpalette(quake2palette) + + im.format = "WAL" + im.format_description = "Quake2 Texture" + + # strings are null-terminated + im.info["name"] = header[:32].split("\0", 1)[0] + next_name = header[56:56+32].split("\0", 1)[0] + if next_name: + im.info["next_name"] = next_name + + return im + + +quake2palette = ( + # default palette taken from piffo 0.93 by Hans Häggström + "\x01\x01\x01\x0b\x0b\x0b\x12\x12\x12\x17\x17\x17\x1b\x1b\x1b\x1e" + "\x1e\x1e\x22\x22\x22\x26\x26\x26\x29\x29\x29\x2c\x2c\x2c\x2f\x2f" + "\x2f\x32\x32\x32\x35\x35\x35\x37\x37\x37\x3a\x3a\x3a\x3c\x3c\x3c" + "\x24\x1e\x13\x22\x1c\x12\x20\x1b\x12\x1f\x1a\x10\x1d\x19\x10\x1b" + "\x17\x0f\x1a\x16\x0f\x18\x14\x0d\x17\x13\x0d\x16\x12\x0d\x14\x10" + "\x0b\x13\x0f\x0b\x10\x0d\x0a\x0f\x0b\x0a\x0d\x0b\x07\x0b\x0a\x07" + "\x23\x23\x26\x22\x22\x25\x22\x20\x23\x21\x1f\x22\x20\x1e\x20\x1f" + "\x1d\x1e\x1d\x1b\x1c\x1b\x1a\x1a\x1a\x19\x19\x18\x17\x17\x17\x16" + "\x16\x14\x14\x14\x13\x13\x13\x10\x10\x10\x0f\x0f\x0f\x0d\x0d\x0d" + "\x2d\x28\x20\x29\x24\x1c\x27\x22\x1a\x25\x1f\x17\x38\x2e\x1e\x31" + "\x29\x1a\x2c\x25\x17\x26\x20\x14\x3c\x30\x14\x37\x2c\x13\x33\x28" + "\x12\x2d\x24\x10\x28\x1f\x0f\x22\x1a\x0b\x1b\x14\x0a\x13\x0f\x07" + "\x31\x1a\x16\x30\x17\x13\x2e\x16\x10\x2c\x14\x0d\x2a\x12\x0b\x27" + "\x0f\x0a\x25\x0f\x07\x21\x0d\x01\x1e\x0b\x01\x1c\x0b\x01\x1a\x0b" + "\x01\x18\x0a\x01\x16\x0a\x01\x13\x0a\x01\x10\x07\x01\x0d\x07\x01" + "\x29\x23\x1e\x27\x21\x1c\x26\x20\x1b\x25\x1f\x1a\x23\x1d\x19\x21" + "\x1c\x18\x20\x1b\x17\x1e\x19\x16\x1c\x18\x14\x1b\x17\x13\x19\x14" + "\x10\x17\x13\x0f\x14\x10\x0d\x12\x0f\x0b\x0f\x0b\x0a\x0b\x0a\x07" + "\x26\x1a\x0f\x23\x19\x0f\x20\x17\x0f\x1c\x16\x0f\x19\x13\x0d\x14" + "\x10\x0b\x10\x0d\x0a\x0b\x0a\x07\x33\x22\x1f\x35\x29\x26\x37\x2f" + "\x2d\x39\x35\x34\x37\x39\x3a\x33\x37\x39\x30\x34\x36\x2b\x31\x34" + "\x27\x2e\x31\x22\x2b\x2f\x1d\x28\x2c\x17\x25\x2a\x0f\x20\x26\x0d" + "\x1e\x25\x0b\x1c\x22\x0a\x1b\x20\x07\x19\x1e\x07\x17\x1b\x07\x14" + "\x18\x01\x12\x16\x01\x0f\x12\x01\x0b\x0d\x01\x07\x0a\x01\x01\x01" + "\x2c\x21\x21\x2a\x1f\x1f\x29\x1d\x1d\x27\x1c\x1c\x26\x1a\x1a\x24" + "\x18\x18\x22\x17\x17\x21\x16\x16\x1e\x13\x13\x1b\x12\x12\x18\x10" + "\x10\x16\x0d\x0d\x12\x0b\x0b\x0d\x0a\x0a\x0a\x07\x07\x01\x01\x01" + "\x2e\x30\x29\x2d\x2e\x27\x2b\x2c\x26\x2a\x2a\x24\x28\x29\x23\x27" + "\x27\x21\x26\x26\x1f\x24\x24\x1d\x22\x22\x1c\x1f\x1f\x1a\x1c\x1c" + "\x18\x19\x19\x16\x17\x17\x13\x13\x13\x10\x0f\x0f\x0d\x0b\x0b\x0a" + "\x30\x1e\x1b\x2d\x1c\x19\x2c\x1a\x17\x2a\x19\x14\x28\x17\x13\x26" + "\x16\x10\x24\x13\x0f\x21\x12\x0d\x1f\x10\x0b\x1c\x0f\x0a\x19\x0d" + "\x0a\x16\x0b\x07\x12\x0a\x07\x0f\x07\x01\x0a\x01\x01\x01\x01\x01" + "\x28\x29\x38\x26\x27\x36\x25\x26\x34\x24\x24\x31\x22\x22\x2f\x20" + "\x21\x2d\x1e\x1f\x2a\x1d\x1d\x27\x1b\x1b\x25\x19\x19\x21\x17\x17" + "\x1e\x14\x14\x1b\x13\x12\x17\x10\x0f\x13\x0d\x0b\x0f\x0a\x07\x07" + "\x2f\x32\x29\x2d\x30\x26\x2b\x2e\x24\x29\x2c\x21\x27\x2a\x1e\x25" + "\x28\x1c\x23\x26\x1a\x21\x25\x18\x1e\x22\x14\x1b\x1f\x10\x19\x1c" + "\x0d\x17\x1a\x0a\x13\x17\x07\x10\x13\x01\x0d\x0f\x01\x0a\x0b\x01" + "\x01\x3f\x01\x13\x3c\x0b\x1b\x39\x10\x20\x35\x14\x23\x31\x17\x23" + "\x2d\x18\x23\x29\x18\x3f\x3f\x3f\x3f\x3f\x39\x3f\x3f\x31\x3f\x3f" + "\x2a\x3f\x3f\x20\x3f\x3f\x14\x3f\x3c\x12\x3f\x39\x0f\x3f\x35\x0b" + "\x3f\x32\x07\x3f\x2d\x01\x3d\x2a\x01\x3b\x26\x01\x39\x21\x01\x37" + "\x1d\x01\x34\x1a\x01\x32\x16\x01\x2f\x12\x01\x2d\x0f\x01\x2a\x0b" + "\x01\x27\x07\x01\x23\x01\x01\x1d\x01\x01\x17\x01\x01\x10\x01\x01" + "\x3d\x01\x01\x19\x19\x3f\x3f\x01\x01\x01\x01\x3f\x16\x16\x13\x10" + "\x10\x0f\x0d\x0d\x0b\x3c\x2e\x2a\x36\x27\x20\x30\x21\x18\x29\x1b" + "\x10\x3c\x39\x37\x37\x32\x2f\x31\x2c\x28\x2b\x26\x21\x30\x22\x20" +) + +if __name__ == "__main__": + im = open("../hacks/sample.wal") + print im.info, im.mode, im.size + im.save("../out.png") diff --git a/PIL/WmfImagePlugin.py b/PIL/WmfImagePlugin.py new file mode 100644 index 000000000..5191f1e51 --- /dev/null +++ b/PIL/WmfImagePlugin.py @@ -0,0 +1,167 @@ +# +# The Python Imaging Library +# $Id$ +# +# WMF stub codec +# +# history: +# 1996-12-14 fl Created +# 2004-02-22 fl Turned into a stub driver +# 2004-02-23 fl Added EMF support +# +# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. +# Copyright (c) Fredrik Lundh 1996. +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.2" + +import Image, ImageFile + +_handler = None + +## +# Install application-specific WMF image handler. +# +# @param handler Handler object. + +def register_handler(handler): + global _handler + _handler = handler + +if hasattr(Image.core, "drawwmf"): + # install default handler (windows only) + + class WmfHandler: + + def open(self, im): + im.mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im): + im.fp.seek(0) # rewind + return Image.fromstring( + "RGB", im.size, + Image.core.drawwmf(im.fp.read(), im.size, self.bbox), + "raw", "BGR", (im.size[0]*3 + 3) & -4, -1 + ) + + register_handler(WmfHandler()) + +# -------------------------------------------------------------------- + +def word(c, o=0): + return ord(c[o]) + (ord(c[o+1])<<8) + +def short(c, o=0): + v = ord(c[o]) + (ord(c[o+1])<<8) + if v >= 32768: + v = v - 65536 + return v + +def dword(c, o=0): + return ord(c[o]) + (ord(c[o+1])<<8) + (ord(c[o+2])<<16) + (ord(c[o+3])<<24) + +def long(c, o=0): + return dword(c, o) + +# +# -------------------------------------------------------------------- +# Read WMF file + +def _accept(prefix): + return ( + prefix[:6] == "\xd7\xcd\xc6\x9a\x00\x00" or + prefix[:4] == "\x01\x00\x00\x00" + ) + +## +# Image plugin for Windows metafiles. + +class WmfStubImageFile(ImageFile.StubImageFile): + + format = "WMF" + format_description = "Windows Metafile" + + def _open(self): + + # check placable header + s = self.fp.read(80) + + if s[:6] == "\xd7\xcd\xc6\x9a\x00\x00": + + # placeable windows metafile + + # get units per inch + inch = word(s, 14) + + # get bounding box + x0 = short(s, 6); y0 = short(s, 8) + x1 = short(s, 10); y1 = short(s, 12) + + # normalize size to 72 dots per inch + size = (x1 - x0) * 72 / inch, (y1 - y0) * 72 / inch + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + self.info["dpi"] = 72 + + # print self.mode, self.size, self.info + + # sanity check (standard metafile header) + if s[22:26] != "\x01\x00\t\x00": + raise SyntaxError("Unsupported WMF file format") + + elif long(s) == 1 and s[40:44] == " EMF": + # enhanced metafile + + # get bounding box + x0 = long(s, 8); y0 = long(s, 12) + x1 = long(s, 16); y1 = long(s, 20) + + # get frame (in 0.01 millimeter units) + frame = long(s, 24), long(s, 28), long(s, 32), long(s, 36) + + # normalize size to 72 dots per inch + size = x1 - x0, y1 - y0 + + # calculate dots per inch from bbox and frame + xdpi = 2540 * (x1 - y0) / (frame[2] - frame[0]) + ydpi = 2540 * (y1 - y0) / (frame[3] - frame[1]) + + self.info["wmf_bbox"] = x0, y0, x1, y1 + + if xdpi == ydpi: + self.info["dpi"] = xdpi + else: + self.info["dpi"] = xdpi, ydpi + + else: + raise SyntaxError("Unsupported file format") + + self.mode = "RGB" + self.size = size + + loader = self._load() + if loader: + loader.open(self) + + def _load(self): + return _handler + + +def _save(im, fp, filename): + if _handler is None or not hasattr("_handler", "save"): + raise IOError("WMF save handler not installed") + _handler.save(im, fp, filename) + +# +# -------------------------------------------------------------------- +# Registry stuff + +Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept) +Image.register_save(WmfStubImageFile.format, _save) + +Image.register_extension(WmfStubImageFile.format, ".wmf") +Image.register_extension(WmfStubImageFile.format, ".emf") diff --git a/PIL/XVThumbImagePlugin.py b/PIL/XVThumbImagePlugin.py new file mode 100644 index 000000000..ab1fb5d7a --- /dev/null +++ b/PIL/XVThumbImagePlugin.py @@ -0,0 +1,73 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XV Thumbnail file handler by Charles E. "Gene" Cash +# (gcash@magicnet.net) +# +# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, +# available from ftp://ftp.cis.upenn.edu/pub/xv/ +# +# history: +# 98-08-15 cec created (b/w only) +# 98-12-09 cec added color palette +# 98-12-28 fl added to PIL (with only a few very minor modifications) +# +# To do: +# FIXME: make save work (this requires quantization support) +# + +__version__ = "0.1" + +import string +import Image, ImageFile, ImagePalette + +# standard color palette for thumbnails (RGB332) +PALETTE = "" +for r in range(8): + for g in range(8): + for b in range(4): + PALETTE = PALETTE + (chr((r*255)/7)+chr((g*255)/7)+chr((b*255)/3)) + +## +# Image plugin for XV thumbnail images. + +class XVThumbImageFile(ImageFile.ImageFile): + + format = "XVThumb" + format_description = "XV thumbnail image" + + def _open(self): + + # check magic + s = self.fp.read(6) + if s != "P7 332": + raise SyntaxError, "not an XV thumbnail file" + + # Skip to beginning of next line + self.fp.readline() + + # skip info comments + while 1: + s = self.fp.readline() + if not s: + raise SyntaxError, "Unexpected EOF reading XV thumbnail file" + if s[0] != '#': + break + + # parse header line (already read) + s = string.split(s.strip()) + + self.mode = "P" + self.size = int(s[0]), int(s[1]) + + self.palette = ImagePalette.raw("RGB", PALETTE) + + self.tile = [ + ("raw", (0, 0)+self.size, + self.fp.tell(), (self.mode, 0, 1) + )] + +# -------------------------------------------------------------------- + +Image.register_open("XVThumb", XVThumbImageFile) diff --git a/PIL/XbmImagePlugin.py b/PIL/XbmImagePlugin.py new file mode 100644 index 000000000..a8cf1026d --- /dev/null +++ b/PIL/XbmImagePlugin.py @@ -0,0 +1,94 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XBM File handling +# +# History: +# 1995-09-08 fl Created +# 1996-11-01 fl Added save support +# 1997-07-07 fl Made header parser more tolerant +# 1997-07-22 fl Fixed yet another parser bug +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) +# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) +# 2004-02-24 fl Allow some whitespace before first #define +# +# Copyright (c) 1997-2004 by Secret Labs AB +# Copyright (c) 1996-1997 by Fredrik Lundh +# +# See the README file for information on usage and redistribution. +# + +__version__ = "0.6" + +import re, string +import Image, ImageFile + +# XBM header +xbm_head = re.compile( + "\s*#define[ \t]+[^_]*_width[ \t]+(?P[0-9]+)[\r\n]+" + "#define[ \t]+[^_]*_height[ \t]+(?P[0-9]+)[\r\n]+" + "(?P" + "#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" + "#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" + ")?" + "[\\000-\\377]*_bits\\[\\]" +) + +def _accept(prefix): + return string.lstrip(prefix)[:7] == "#define" + +## +# Image plugin for X11 bitmaps. + +class XbmImageFile(ImageFile.ImageFile): + + format = "XBM" + format_description = "X11 Bitmap" + + def _open(self): + + m = xbm_head.match(self.fp.read(512)) + + if m: + + xsize = int(m.group("width")) + ysize = int(m.group("height")) + + if m.group("hotspot"): + self.info["hotspot"] = ( + int(m.group("xhot")), int(m.group("yhot")) + ) + + self.mode = "1" + self.size = xsize, ysize + + self.tile = [("xbm", (0, 0)+self.size, m.end(), None)] + + +def _save(im, fp, filename): + + if im.mode != "1": + raise IOError, "cannot write mode %s as XBM" % im.mode + + fp.write("#define im_width %d\n" % im.size[0]) + fp.write("#define im_height %d\n" % im.size[1]) + + hotspot = im.encoderinfo.get("hotspot") + if hotspot: + fp.write("#define im_x_hot %d\n" % hotspot[0]) + fp.write("#define im_y_hot %d\n" % hotspot[1]) + + fp.write("static char im_bits[] = {\n") + + ImageFile._save(im, fp, [("xbm", (0,0)+im.size, 0, None)]) + + fp.write("};\n") + + +Image.register_open("XBM", XbmImageFile, _accept) +Image.register_save("XBM", _save) + +Image.register_extension("XBM", ".xbm") + +Image.register_mime("XBM", "image/xbm") diff --git a/PIL/XpmImagePlugin.py b/PIL/XpmImagePlugin.py new file mode 100644 index 000000000..a3f40f02c --- /dev/null +++ b/PIL/XpmImagePlugin.py @@ -0,0 +1,129 @@ +# +# The Python Imaging Library. +# $Id$ +# +# XPM File handling +# +# History: +# 1996-12-29 fl Created +# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) +# +# Copyright (c) Secret Labs AB 1997-2001. +# Copyright (c) Fredrik Lundh 1996-2001. +# +# See the README file for information on usage and redistribution. +# + + +__version__ = "0.2" + + +import re, string +import Image, ImageFile, ImagePalette + +# XPM header +xpm_head = re.compile("\"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)") + + +def _accept(prefix): + return prefix[:9] == "/* XPM */" + +## +# Image plugin for X11 pixel maps. + +class XpmImageFile(ImageFile.ImageFile): + + format = "XPM" + format_description = "X11 Pixel Map" + + def _open(self): + + if not _accept(self.fp.read(9)): + raise SyntaxError, "not an XPM file" + + # skip forward to next string + while 1: + s = self.fp.readline() + if not s: + raise SyntaxError, "broken XPM file" + m = xpm_head.match(s) + if m: + break + + self.size = int(m.group(1)), int(m.group(2)) + + pal = int(m.group(3)) + bpp = int(m.group(4)) + + if pal > 256 or bpp != 1: + raise ValueError, "cannot read this XPM file" + + # + # load palette description + + palette = ["\0\0\0"] * 256 + + for i in range(pal): + + s = self.fp.readline() + if s[-2:] == '\r\n': + s = s[:-2] + elif s[-1:] in '\r\n': + s = s[:-1] + + c = ord(s[1]) + s = string.split(s[2:-2]) + + for i in range(0, len(s), 2): + + if s[i] == "c": + + # process colour key + rgb = s[i+1] + if rgb == "None": + self.info["transparency"] = c + elif rgb[0] == "#": + # FIXME: handle colour names (see ImagePalette.py) + rgb = string.atoi(rgb[1:], 16) + palette[c] = chr((rgb >> 16) & 255) +\ + chr((rgb >> 8) & 255) +\ + chr(rgb & 255) + else: + # unknown colour + raise ValueError, "cannot read this XPM file" + break + + else: + + # missing colour key + raise ValueError, "cannot read this XPM file" + + self.mode = "P" + self.palette = ImagePalette.raw("RGB", string.join(palette, "")) + + self.tile = [("raw", (0, 0)+self.size, self.fp.tell(), ("P", 0, 1))] + + def load_read(self, bytes): + + # + # load all image data in one chunk + + xsize, ysize = self.size + + s = [None] * ysize + + for i in range(ysize): + s[i] = string.ljust(self.fp.readline()[1:xsize+1], xsize) + + self.fp = None + + return string.join(s, "") + +# +# Registry + +Image.register_open("XPM", XpmImageFile, _accept) + +Image.register_extension("XPM", ".xpm") + +Image.register_mime("XPM", "image/xpm") diff --git a/PIL/__init__.py b/PIL/__init__.py new file mode 100644 index 000000000..ed54d26f6 --- /dev/null +++ b/PIL/__init__.py @@ -0,0 +1,12 @@ +# +# The Python Imaging Library. +# $Id$ +# +# package placeholder +# +# Copyright (c) 1999 by Secret Labs AB. +# +# See the README file for information on usage and redistribution. +# + +# ;-) diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 000000000..c27993fb2 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,18 @@ +Metadata-Version: 1.0 +Name: PIL +Version: 1.1.7 +Summary: Python Imaging Library +Home-page: http://www.pythonware.com/products/pil +Author: Secret Labs AB (PythonWare) +Author-email: info@pythonware.com +License: Python (MIT style) +Download-URL: http://effbot.org/downloads/PIL-1.1.7.tar.gz +Description: Python Imaging Library +Platform: Python 1.5.2 and later. +Classifier: Development Status :: 6 - Mature +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Scanners +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers diff --git a/README b/README index e69de29bb..458975b3b 100644 --- a/README +++ b/README @@ -0,0 +1,300 @@ +The Python Imaging Library +$Id$ + +Release 1.1.7 (November 15, 2009) + +==================================================================== +The Python Imaging Library 1.1.7 +==================================================================== + +Contents +-------- + ++ Introduction ++ Support Options + - Commercial support + - Free support ++ Software License ++ Build instructions (all platforms) + - Additional notes for Mac OS X + - Additional notes for Windows + +-------------------------------------------------------------------- +Introduction +-------------------------------------------------------------------- + +The Python Imaging Library (PIL) adds image processing capabilities +to your Python environment. This library provides extensive file +format support, an efficient internal representation, and powerful +image processing capabilities. + +This source kit has been built and tested with Python 2.0 and newer, +on Windows, Mac OS X, and major Unix platforms. Large parts of the +library also work on 1.5.2 and 1.6. + +The main distribution site for this software is: + + http://www.pythonware.com/products/pil/ + +That site also contains information about free and commercial support +options, PIL add-ons, answers to frequently asked questions, and more. + + +Development versions (alphas, betas) are available here: + + http://effbot.org/downloads/ + + +The PIL handbook is not included in this distribution; to get the +latest version, check: + + http://www.pythonware.com/library/ + http://effbot.org/books/imagingbook/ (drafts) + + +For installation and licensing details, see below. + + +-------------------------------------------------------------------- +Support Options +-------------------------------------------------------------------- + ++ Commercial Support + +Secret Labs (PythonWare) offers support contracts for companies using +the Python Imaging Library in commercial applications, and in mission- +critical environments. The support contract includes technical support, +bug fixes, extensions to the PIL library, sample applications, and more. + +For the full story, check: + + http://www.pythonware.com/products/pil/support.htm + + ++ Free Support + +For support and general questions on the Python Imaging Library, send +e-mail to the Image SIG mailing list: + + image-sig@python.org + +You can join the Image SIG by sending a mail to: + + image-sig-request@python.org + +Put "subscribe" in the message body to automatically subscribe to the +list, or "help" to get additional information. Alternatively, you can +send your questions to the Python mailing list, python-list@python.org, +or post them to the newsgroup comp.lang.python. DO NOT SEND SUPPORT +QUESTIONS TO PYTHONWARE ADDRESSES. + + +-------------------------------------------------------------------- +Software License +-------------------------------------------------------------------- + +The Python Imaging Library is + +Copyright (c) 1997-2009 by Secret Labs AB +Copyright (c) 1995-2009 by Fredrik Lundh + +By obtaining, using, and/or copying this software and/or its +associated documentation, you agree that you have read, understood, +and will comply with the following terms and conditions: + +Permission to use, copy, modify, and distribute this software and its +associated documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies, and that both that copyright notice and this permission notice +appear in supporting documentation, and that the name of Secret Labs +AB or the author not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR +ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +-------------------------------------------------------------------- +Build instructions (all platforms) +-------------------------------------------------------------------- + +For a list of changes in this release, see the CHANGES document. + +0. If you're in a hurry, try this: + + $ tar xvfz Imaging-1.1.7.tar.gz + $ cd Imaging-1.1.7 + $ python setup.py install + + If you prefer to know what you're doing, read on. + + +1. Prerequisites. + + If you need any of the features described below, make sure you + have the necessary libraries before building PIL. + + feature library + ----------------------------------------------------------------- + JPEG support libjpeg (6a or 6b) + + http://www.ijg.org + http://www.ijg.org/files/jpegsrc.v6b.tar.gz + ftp://ftp.uu.net/graphics/jpeg/ + + PNG support zlib (1.2.3 or later is recommended) + + http://www.gzip.org/zlib/ + + OpenType/TrueType freetype2 (2.3.9 or later is recommended) + support + http://www.freetype.org + http://freetype.sourceforge.net + + CMS support littleCMS (1.1.5 or later is recommended) + support + http://www.littlecms.com/ + + If you have a recent Linux version, the libraries provided with the + operating system usually work just fine. If some library is + missing, installing a prebuilt version (jpeg-devel, zlib-devel, + etc) is usually easier than building from source. For example, for + Ubuntu 9.10 (karmic), you can install the following libraries: + + sudo apt-get install libjpeg62-dev + sudo apt-get install zlib1g-dev + sudo apt-get install libfreetype6-dev + sudo apt-get install liblcms1-dev + + If you're using Mac OS X, you can use the 'fink' tool to install + missing libraries (also see the Mac OS X section below). + + Similar tools are available for many other platforms. + + +2. To build under Python 1.5.2, you need to install the stand-alone + version of the distutils library: + + http://www.python.org/sigs/distutils-sig/download.html + + You can fetch distutils 1.0.2 from the Python source repository: + + svn export http://svn.python.org/projects/python/tags/Distutils-1_0_2/Lib/distutils/ + + For newer releases, the distutils library is included in the + Python standard library. + + NOTE: Version 1.1.7 is not fully compatible with 1.5.2. Some + more recent additions to the library may not work, but the core + functionality is available. + + +3. If you didn't build Python from sources, make sure you have + Python's build support files on your machine. If you've down- + loaded a prebuilt package (e.g. a Linux RPM), you probably + need additional developer packages. Look for packages named + "python-dev", "python-devel", or similar. For example, for + Ubuntu 9.10 (karmic), use the following command: + + sudo apt-get install python-dev + + +4. When you have everything you need, unpack the PIL distribution + (the file Imaging-1.1.7.tar.gz) in a suitable work directory: + + $ cd MyExtensions # example + $ gunzip Imaging-1.1.7.tar.gz + $ tar xvf Imaging-1.1.7.tar + + +5. Build the library. We recommend that you do an in-place build, + and run the self test before installing. + + $ cd Imaging-1.1.7 + $ python setup.py build_ext -i + $ python selftest.py + + During the build process, the setup.py will display a summary + report that lists what external components it found. The self- + test will display a similar report, with what external components + the tests found in the actual build files: + + ---------------------------------------------------------------- + PIL 1.1.7 SETUP SUMMARY + ---------------------------------------------------------------- + *** TKINTER support not available (Tcl/Tk 8.5 libraries needed) + --- JPEG support available + --- ZLIB (PNG/ZIP) support available + --- FREETYPE support available + ---------------------------------------------------------------- + + Make sure that the optional components you need are included. + + If the build script won't find a given component, you can edit the + setup.py file and set the appropriate ROOT variable. For details, + see instructions in the file. + + If the build script finds the component, but the tests cannot + identify it, try rebuilding *all* modules: + + $ python setup.py clean + $ python setup.py build_ext -i + + +6. If the setup.py and selftest.py commands finish without any + errors, you're ready to install the library: + + $ python setup.py install + + (depending on how Python has been installed on your machine, + you might have to log in as a superuser to run the 'install' + command, or use the 'sudo' command to run 'install'.) + + +-------------------------------------------------------------------- +Additional notes for Mac OS X +-------------------------------------------------------------------- + +On Mac OS X you will usually install additional software such as +libjpeg or freetype with the "fink" tool, and then it ends up in +"/sw". If you have installed the libraries elsewhere, you may have +to tweak the "setup.py" file before building. + + +-------------------------------------------------------------------- +Additional notes for Windows +-------------------------------------------------------------------- + +On Windows, you need to tweak the ROOT settings in the "setup.py" +file, to make it find the external libraries. See comments in the +file for details. + +Make sure to build PIL and the external libraries with the same +runtime linking options as was used for the Python interpreter +(usually /MD, under Visual Studio). + + +Note that most Python distributions for Windows include libraries +compiled for Microsoft Visual Studio. You can get the free Express +edition of Visual Studio from: + + http://www.microsoft.com/Express/ + +To build extensions using other tool chains, see the "Using +non-Microsoft compilers on Windows" section in the distutils handbook: + + http://www.python.org/doc/current/inst/non-ms-compilers.html + +For additional information on how to build extensions using the +popular MinGW compiler, see: + + http://mingw.org (compiler) + http://sebsauvage.net/python/mingw.html (build instructions) + http://sourceforge.net/projects/gnuwin32 (prebuilt libraries) + diff --git a/README.txt b/README.txt new file mode 100644 index 000000000..85882da91 --- /dev/null +++ b/README.txt @@ -0,0 +1,4 @@ +Pillow +====== + +Pillow is a fork of the Python Imaging Library diff --git a/Sane/CHANGES b/Sane/CHANGES new file mode 100644 index 000000000..95c14697e --- /dev/null +++ b/Sane/CHANGES @@ -0,0 +1,34 @@ + +from V1.0 to V2.0 + +_sane.c: + - Values for option constraints are correctly translated to floats + if value type is TYPE_FIXED for SANE_CONSTRAINT_RANGE and + SANE_CONSTRAINT_WORD_LIST + - added constants INFO_INEXACT, INFO_RELOAD_OPTIONS, + INFO_RELOAD_PARAMS (possible return values of set_option()) + to module dictionnary. + - removed additional return variable 'i' from SaneDev_get_option(), + because it is only set when SANE_ACTION_SET_VALUE is used. + - scanDev.get_parameters() now returns the scanner mode as 'format', + no more the typical PIL codes. So 'L' became 'gray', 'RGB' is now + 'color', 'R' is 'red', 'G' is 'green', 'B' is 'red'. This matches + the way scanDev.mode is set. + This should be the only incompatibility vs. version 1.0. + +sane.py + - ScanDev got new method __load_option_dict() called from __init__() + and from __setattr__() if backend reported that the frontend should + reload the options. + - Nice human-readable __repr__() method added for class Option + - if __setattr__ (i.e. set_option) reports that all other options + have to be reloaded due to a change in the backend then they are reloaded. + - due to the change in SaneDev_get_option() only the 'value' is + returned from get_option(). + - in __setattr__ integer values are automatically converted to floats + if SANE backend expects SANE_FIXED (i.e. fix-point float) + - The scanner options can now directly be accessed via scanDev[optionName] + instead scanDev.opt[optionName]. (The old way still works). + +V1.0: + A.M. Kuchling's original pysane package. \ No newline at end of file diff --git a/Sane/README b/Sane/README new file mode 100644 index 000000000..ddff0cf24 --- /dev/null +++ b/Sane/README @@ -0,0 +1,22 @@ + +Python SANE module V1.1 (30 Sep. 2004) + +The SANE module provides an interface to the SANE scanner and frame +grabber interface for Linux. This module was contributed by Andrew +Kuchling and is extended and currently maintained by Ralph Heinkel +(rheinkel-at-email.de). If you write to me please make sure to have the +word 'SANE' or 'sane' in the subject of your mail, otherwise it might +be classified as spam in the future. + + +To build this module, type (in the Sane directory): + + python setup.py build + +In order to install the module type: + + python setup.py install + + +For some basic documentation please look at the file sanedoc.txt +The two demo_*.py scripts give basic examples on how to use the software. diff --git a/Sane/_sane.c b/Sane/_sane.c new file mode 100644 index 000000000..21e542fa5 --- /dev/null +++ b/Sane/_sane.c @@ -0,0 +1,1325 @@ +/*********************************************************** +(C) Copyright 2003 A.M. Kuchling. All Rights Reserved +(C) Copyright 2004 A.M. Kuchling, Ralph Heinkel All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of A.M. Kuchling and +Ralph Heinkel not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +A.M. KUCHLING, R.H. HEINKEL DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +******************************************************************/ + +/* SaneDev objects */ + +#include "Python.h" +#include "Imaging.h" +#include + +#include + +static PyObject *ErrorObject; + +typedef struct { + PyObject_HEAD + SANE_Handle h; +} SaneDevObject; + +#ifdef WITH_THREAD +PyThreadState *_save; +#endif + +/* Raise a SANE exception */ +PyObject * +PySane_Error(SANE_Status st) +{ + const char *string; + + if (st==SANE_STATUS_GOOD) {Py_INCREF(Py_None); return (Py_None);} + string=sane_strstatus(st); + PyErr_SetString(ErrorObject, string); + return NULL; +} + +staticforward PyTypeObject SaneDev_Type; + +#define SaneDevObject_Check(v) ((v)->ob_type == &SaneDev_Type) + +static SaneDevObject * +newSaneDevObject(void) +{ + SaneDevObject *self; + self = PyObject_NEW(SaneDevObject, &SaneDev_Type); + if (self == NULL) + return NULL; + self->h=NULL; + return self; +} + +/* SaneDev methods */ + +static void +SaneDev_dealloc(SaneDevObject *self) +{ + if (self->h) sane_close(self->h); + self->h=NULL; + PyObject_DEL(self); +} + +static PyObject * +SaneDev_close(SaneDevObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h) sane_close(self->h); + self->h=NULL; + Py_INCREF(Py_None); + return (Py_None); +} + +static PyObject * +SaneDev_get_parameters(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + SANE_Parameters p; + char *format="unknown format"; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + Py_BEGIN_ALLOW_THREADS + st=sane_get_parameters(self->h, &p); + Py_END_ALLOW_THREADS + + if (st) return PySane_Error(st); + switch (p.format) + { + case(SANE_FRAME_GRAY): format="gray"; break; + case(SANE_FRAME_RGB): format="color"; break; + case(SANE_FRAME_RED): format="red"; break; + case(SANE_FRAME_GREEN): format="green"; break; + case(SANE_FRAME_BLUE): format="blue"; break; + } + + return Py_BuildValue("si(ii)ii", format, p.last_frame, p.pixels_per_line, + p.lines, p.depth, p.bytes_per_line); +} + + +static PyObject * +SaneDev_fileno(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + SANE_Int fd; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + st=sane_get_select_fd(self->h, &fd); + if (st) return PySane_Error(st); + return PyInt_FromLong(fd); +} + +static PyObject * +SaneDev_start(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + /* sane_start can take several seconds, if the user initiates + a new scan, while the scan head of a flatbed scanner moves + back to the start position after finishing a previous scan. + Hence it is worth to allow threads here. + */ + Py_BEGIN_ALLOW_THREADS + st=sane_start(self->h); + Py_END_ALLOW_THREADS + if (st) return PySane_Error(st); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +SaneDev_cancel(SaneDevObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + sane_cancel(self->h); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +SaneDev_get_options(SaneDevObject *self, PyObject *args) +{ + const SANE_Option_Descriptor *d; + PyObject *list, *value; + int i=1; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + if (!(list = PyList_New(0))) + return NULL; + + do + { + d=sane_get_option_descriptor(self->h, i); + if (d!=NULL) + { + PyObject *constraint=NULL; + int j; + + switch (d->constraint_type) + { + case(SANE_CONSTRAINT_NONE): + Py_INCREF(Py_None); constraint=Py_None; break; + case(SANE_CONSTRAINT_RANGE): + if (d->type == SANE_TYPE_INT) + constraint=Py_BuildValue("iii", d->constraint.range->min, + d->constraint.range->max, + d->constraint.range->quant); + else + constraint=Py_BuildValue("ddd", + SANE_UNFIX(d->constraint.range->min), + SANE_UNFIX(d->constraint.range->max), + SANE_UNFIX(d->constraint.range->quant)); + break; + case(SANE_CONSTRAINT_WORD_LIST): + constraint=PyList_New(d->constraint.word_list[0]); + if (d->type == SANE_TYPE_INT) + for (j=1; j<=d->constraint.word_list[0]; j++) + PyList_SetItem(constraint, j-1, + PyInt_FromLong(d->constraint.word_list[j])); + else + for (j=1; j<=d->constraint.word_list[0]; j++) + PyList_SetItem(constraint, j-1, + PyFloat_FromDouble(SANE_UNFIX(d->constraint.word_list[j]))); + break; + case(SANE_CONSTRAINT_STRING_LIST): + constraint=PyList_New(0); + for(j=0; d->constraint.string_list[j]!=NULL; j++) + PyList_Append(constraint, + PyString_FromString(d->constraint.string_list[j])); + break; + } + value=Py_BuildValue("isssiiiiO", i, d->name, d->title, d->desc, + d->type, d->unit, d->size, d->cap, constraint); + PyList_Append(list, value); + } + i++; + } while (d!=NULL); + return list; +} + +static PyObject * +SaneDev_get_option(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + const SANE_Option_Descriptor *d; + PyObject *value=NULL; + int n; + void *v; + + if (!PyArg_ParseTuple(args, "i", &n)) + { + return NULL; + } + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + d=sane_get_option_descriptor(self->h, n); + v=malloc(d->size+1); + st=sane_control_option(self->h, n, SANE_ACTION_GET_VALUE, + v, NULL); + + if (st) + { + free(v); + return PySane_Error(st); + } + + switch(d->type) + { + case(SANE_TYPE_BOOL): + case(SANE_TYPE_INT): + value=Py_BuildValue("i", *( (SANE_Int*)v) ); + break; + case(SANE_TYPE_FIXED): + value=Py_BuildValue("d", SANE_UNFIX((*((SANE_Fixed*)v))) ); + break; + case(SANE_TYPE_STRING): + value=Py_BuildValue("s", v); + break; + case(SANE_TYPE_BUTTON): + case(SANE_TYPE_GROUP): + value=Py_BuildValue("O", Py_None); + break; + } + + free(v); + return value; +} + +static PyObject * +SaneDev_set_option(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + const SANE_Option_Descriptor *d; + SANE_Int i; + PyObject *value; + int n; + void *v; + + if (!PyArg_ParseTuple(args, "iO", &n, &value)) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + d=sane_get_option_descriptor(self->h, n); + v=malloc(d->size+1); + + switch(d->type) + { + case(SANE_TYPE_BOOL): + if (!PyInt_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_BOOL requires an integer"); + free(v); + return NULL; + } + /* fall through */ + case(SANE_TYPE_INT): + if (!PyInt_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_INT requires an integer"); + free(v); + return NULL; + } + *( (SANE_Int*)v) = PyInt_AsLong(value); + break; + case(SANE_TYPE_FIXED): + if (!PyFloat_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_FIXED requires a floating point number"); + free(v); + return NULL; + } + *( (SANE_Fixed*)v) = SANE_FIX(PyFloat_AsDouble(value)); + break; + case(SANE_TYPE_STRING): + if (!PyString_Check(value)) + { + PyErr_SetString(PyExc_TypeError, "SANE_STRING requires a string"); + free(v); + return NULL; + } + strncpy(v, PyString_AsString(value), d->size-1); + ((char*)v)[d->size-1] = 0; + break; + case(SANE_TYPE_BUTTON): + case(SANE_TYPE_GROUP): + break; + } + + st=sane_control_option(self->h, n, SANE_ACTION_SET_VALUE, + v, &i); + if (st) {free(v); return PySane_Error(st);} + + free(v); + return Py_BuildValue("i", i); +} + +static PyObject * +SaneDev_set_auto_option(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + const SANE_Option_Descriptor *d; + SANE_Int i; + int n; + + if (!PyArg_ParseTuple(args, "i", &n)) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + d=sane_get_option_descriptor(self->h, n); + st=sane_control_option(self->h, n, SANE_ACTION_SET_AUTO, + NULL, &i); + if (st) {return PySane_Error(st);} + + return Py_BuildValue("i", i); + } + +#define READSIZE 32768 + +static PyObject * +SaneDev_snap(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + /* The buffer should be a multiple of 3 in size, so each sane_read + operation will return an integral number of RGB triples. */ + SANE_Byte buffer[READSIZE]; /* XXX how big should the buffer be? */ + SANE_Int len, lastlen; + Imaging im; + SANE_Parameters p; + int px, py, remain, cplen, bufpos, padbytes; + long L; + char errmsg[80]; + union + { char c[2]; + INT16 i16; + } + endian; + PyObject *pyNoCancel = NULL; + int noCancel = 0; + + endian.i16 = 1; + + if (!PyArg_ParseTuple(args, "l|O", &L, &pyNoCancel)) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + im=(Imaging)L; + + if (pyNoCancel) + noCancel = PyObject_IsTrue(pyNoCancel); + + st=SANE_STATUS_GOOD; px=py=0; + /* xxx not yet implemented + - handscanner support (i.e., unknown image length during start) + - generally: move the functionality from method snap in sane.py + down here -- I don't like this cross-dependency. + we need to call sane_get_parameters here, and we can create + the result Image object here. + */ + + Py_UNBLOCK_THREADS + sane_get_parameters(self->h, &p); + if (p.format == SANE_FRAME_GRAY) + { + switch (p.depth) + { + case 1: + remain = p.bytes_per_line * im->ysize; + padbytes = p.bytes_per_line - (im->xsize+7)/8; + bufpos = 0; + lastlen = len = 0; + while (st!=SANE_STATUS_EOF && py < im->ysize) + { + while (len > 0 && py < im->ysize) + { + int i, j, k; + j = buffer[bufpos++]; + k = 0x80; + for (i = 0; i < 8 && px < im->xsize; i++) + { + im->image8[py][px++] = (k&j) ? 0 : 0xFF; + k = k >> 1; + } + len--; + if (px >= im->xsize) + { + bufpos += padbytes; + len -= padbytes; + py++; + px = 0; + } + } + st=sane_read(self->h, buffer, + remainh); + Py_BLOCK_THREADS + return PySane_Error(st); + } + bufpos -= lastlen; + lastlen = len; + remain -= len; + /* skip possible pad bytes at the start of the buffer */ + len -= bufpos; + } + break; + case 8: + remain = p.bytes_per_line * im->ysize; + padbytes = p.bytes_per_line - im->xsize; + bufpos = 0; + len = 0; + while (st!=SANE_STATUS_EOF && py < im->ysize) + { + while (len > 0 && py < im->ysize) + { + cplen = len; + if (px + cplen >= im->xsize) + cplen = im->xsize - px; + memcpy(&im->image8[py][px], &buffer[bufpos], cplen); + len -= cplen; + bufpos += cplen; + px += cplen; + if (px >= im->xsize) + { + px = 0; + py++; + bufpos += padbytes; + len -= padbytes; + } + } + bufpos = -len; + + st=sane_read(self->h, buffer, + remainh); + Py_BLOCK_THREADS + return PySane_Error(st); + } + remain -= len; + len -= bufpos; + } + break; + case 16: + remain = p.bytes_per_line * im->ysize; + padbytes = p.bytes_per_line - 2 * im->xsize; + bufpos = endian.c[0]; + lastlen = len = 0; + while (st!=SANE_STATUS_EOF && py < im->ysize) + { + while (len > 0 && py < im->ysize) + { + im->image8[py][px++] = buffer[bufpos]; + bufpos += 2; + len -= 2; + if (px >= im->xsize) + { + bufpos += padbytes; + len -= padbytes; + py++; + px = 0; + } + } + st=sane_read(self->h, buffer, + remainh); + Py_BLOCK_THREADS + return PySane_Error(st); + } + remain -= len; + bufpos -= lastlen; + lastlen = len; + len -= bufpos; + } + break; + default: + /* other depths are not formally "illegal" according to the + Sane API, but it's agreed by Sane developers that other + depths than 1, 8, 16 should not be used + */ + sane_cancel(self->h); + Py_BLOCK_THREADS + snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + } + else if (p.format == SANE_FRAME_RGB) + { + int incr, color, pxs, pxmax, bit, val, mask; + switch (p.depth) + { + case 1: + remain = p.bytes_per_line * im->ysize; + padbytes = p.bytes_per_line - ((im->xsize+7)/8) * 3; + bufpos = 0; + len = 0; + lastlen = 0; + pxmax = 4 * im->xsize; + while (st!=SANE_STATUS_EOF && py < im->ysize) + { + pxs = px; + for (color = 0; color < 3; color++) + { + while (len <= 0 && st == SANE_STATUS_GOOD) + { + st=sane_read(self->h, buffer, + remainh); + Py_BLOCK_THREADS + return PySane_Error(st); + } + bufpos -= lastlen; + remain -= len; + lastlen = len; + /* skip possible pad bytes at the start of the buffer */ + len -= bufpos; + } + if (st == SANE_STATUS_EOF) break; + pxs = px; + val = buffer[bufpos++]; + len--; + mask = 0x80; + for (bit = 0; (bit < 8) && (px < pxmax); bit++) + { + ((UINT8**)(im->image32))[py][px] = (val&mask) ? 0xFF : 0; + mask = mask >> 1; + px += 4; + } + pxs++; + px = pxs; + } + if (st == SANE_STATUS_EOF) + break; + for (bit = 0; bit < 8 && px < pxmax; bit++) + { + ((UINT8**)(im->image32))[py][px] = 0; + px += 4; + } + px -= 3; + if (px >= pxmax) + { + bufpos += padbytes; + len -= padbytes; + py++; + px = 0; + } + } + break; + case 8: + case 16: + if (p.depth == 8) + { + padbytes = p.bytes_per_line - 3 * im->xsize; + bufpos = 0; + incr = 1; + } + else + { + padbytes = p.bytes_per_line - 6 * im->xsize; + bufpos = endian.c[0]; + incr = 2; + } + remain = p.bytes_per_line * im->ysize; + len = 0; + lastlen = 0; + pxmax = 4 * im->xsize; + /* probably not very efficient. But we have to deal with these + possible conditions: + - we may have padding bytes at the end of a scan line + - the number of bytes read with sane_read may be smaller + than the number of pad bytes + - the buffer may become empty after setting any of the + red/green/blue pixel values + + */ + while (st != SANE_STATUS_EOF && py < im->ysize) + { + for (color = 0; color < 3; color++) + { + while (len <= 0 && st == SANE_STATUS_GOOD) + { + bufpos -= lastlen; + if (remain == 0) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + PyErr_SetString(ErrorObject, "internal _sane error: premature end of scan"); + return NULL; + } + st = sane_read(self->h, buffer, + remain<(READSIZE) ? remain : (READSIZE), &len); + if (st && (st!=SANE_STATUS_EOF)) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + return PySane_Error(st); + } + lastlen = len; + remain -= len; + len -= bufpos; + } + if (st == SANE_STATUS_EOF) break; + ((UINT8**)(im->image32))[py][px++] = buffer[bufpos]; + bufpos += incr; + len -= incr; + } + if (st == SANE_STATUS_EOF) break; + + ((UINT8**)(im->image32))[py][px++] = 0; + + if (px >= pxmax) + { + px = 0; + py++; + bufpos += padbytes; + len -= padbytes; + } + } + break; + default: + Py_BLOCK_THREADS + sane_cancel(self->h); + snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + + } + else /* should be SANE_FRAME_RED, GREEN or BLUE */ + { + int lastlen, pxa, pxmax, offset, incr, frame_count = 0; + /* at least the Sane test backend behaves a bit weird, if + it returns "premature EOF" for sane_read, i.e., if the + option "return value of sane_read" is set to SANE_STATUS_EOF. + In this case, the test backend does not advance to the next frame, + so p.last_frame will never be set... + So let's count the number of frames we try to acquire + */ + while (!p.last_frame && frame_count < 4) + { + frame_count++; + st = sane_get_parameters(self->h, &p); + if (st) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + return PySane_Error(st); + } + remain = p.bytes_per_line * im->ysize; + bufpos = 0; + len = 0; + lastlen = 0; + py = 0; + switch (p.format) + { + case SANE_FRAME_RED: + offset = 0; + break; + case SANE_FRAME_GREEN: + offset = 1; + break; + case SANE_FRAME_BLUE: + offset = 2; + break; + default: + sane_cancel(self->h); + Py_BLOCK_THREADS + snprintf(errmsg, 80, "unknown/invalid frame format: %i", p.format); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + px = offset; + pxa = 3; + pxmax = im->xsize * 4; + switch (p.depth) + { + case 1: + padbytes = p.bytes_per_line - (im->xsize+7)/8; + st = SANE_STATUS_GOOD; + while (st != SANE_STATUS_EOF && py < im->ysize) + { + while (len > 0) + { + int bit, mask, val; + val = buffer[bufpos++]; len--; + mask = 0x80; + for (bit = 0; bit < 8 && px < pxmax; bit++) + { + ((UINT8**)(im->image32))[py][px] + = val&mask ? 0xFF : 0; + ((UINT8**)(im->image32))[py][pxa] = 0; + px += 4; + pxa += 4; + mask = mask >> 1; + } + + if (px >= pxmax) + { + px = offset; + pxa = 3; + py++; + bufpos += padbytes; + len -= padbytes; + } + } + while (len <= 0 && st == SANE_STATUS_GOOD && remain > 0) + { + bufpos -= lastlen; + st = sane_read(self->h, buffer, + remain<(READSIZE) ? remain : (READSIZE), &len); + if (st && (st!=SANE_STATUS_EOF)) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + return PySane_Error(st); + } + remain -= len; + lastlen = len; + len -= bufpos; + } + } + break; + case 8: + case 16: + if (p.depth == 8) + { + padbytes = p.bytes_per_line - im->xsize; + incr = 1; + } + else + { + padbytes = p.bytes_per_line - 2 * im->xsize; + incr = 2; + bufpos = endian.c[0]; + } + st = SANE_STATUS_GOOD; + while (st != SANE_STATUS_EOF && py < im->ysize) + { + while (len <= 0) + { + bufpos -= lastlen; + if (remain == 0) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + PyErr_SetString(ErrorObject, "internal _sane error: premature end of scan"); + return NULL; + } + st = sane_read(self->h, buffer, + remain<(READSIZE) ? remain : (READSIZE), &len); + if (st && (st!=SANE_STATUS_EOF)) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + return PySane_Error(st); + } + if (st == SANE_STATUS_EOF) + break; + lastlen = len; + remain -= len; + if (bufpos >= len) + len = 0; + else + len -= bufpos; + } + if (st == SANE_STATUS_EOF) + break; + ((UINT8**)(im->image32))[py][px] = buffer[bufpos]; + ((UINT8**)(im->image32))[py][pxa] = 0; + bufpos += incr; + len -= incr; + px += 4; + pxa += 4; + + if (px >= pxmax) + { + px = offset; + pxa = 3; + py++; + bufpos += padbytes; + len -= padbytes; + } + } + break; + default: + sane_cancel(self->h); + Py_BLOCK_THREADS + snprintf(errmsg, 80, "unsupported pixel depth: %i", p.depth); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + if (!p.last_frame) + { + /* all sane_read calls in the above loop may return + SANE_STATUS_GOOD, but the backend may need another sane_read + call which returns SANE_STATUS_EOF in order to start + a new frame. + */ + do { + st = sane_read(self->h, buffer, READSIZE, &len); + } + while (st == SANE_STATUS_GOOD); + if (st != SANE_STATUS_EOF) + { + Py_BLOCK_THREADS + sane_cancel(self->h); + return PySane_Error(st); + } + + st = sane_start(self->h); + if (st) + { + Py_BLOCK_THREADS + return PySane_Error(st); + } + } + } + } + /* enforce SANE_STATUS_EOF. Can be necessary for ADF scans for some backends */ + do { + st = sane_read(self->h, buffer, READSIZE, &len); + } + while (st == SANE_STATUS_GOOD); + if (st != SANE_STATUS_EOF) + { + sane_cancel(self->h); + Py_BLOCK_THREADS + return PySane_Error(st); + } + + if (!noCancel) + sane_cancel(self->h); + Py_BLOCK_THREADS + Py_INCREF(Py_None); + return Py_None; +} + + +#ifdef WITH_NUMARRAY + +#include "numarray/libnumarray.h" + +/* this global variable is set to 1 in 'init_sane()' after successfully + importing the numarray module. */ +int NUMARRAY_IMPORTED = 0; + +static PyObject * +SaneDev_arr_snap(SaneDevObject *self, PyObject *args) +{ + SANE_Status st; + SANE_Byte buffer[READSIZE]; + SANE_Int len; + SANE_Parameters p; + + PyArrayObject *pyArr = NULL; + NumarrayType arrType; + int line, line_index, buffer_index, remain_bytes_line, num_pad_bytes; + int cp_num_bytes, total_remain, bpp, arr_bytes_per_line; + int pixels_per_line = -1; + char errmsg[80]; + + if (!NUMARRAY_IMPORTED) + { + PyErr_SetString(ErrorObject, "numarray package not available"); + return NULL; + } + + if (!PyArg_ParseTuple(args, "|i", &pixels_per_line)) + return NULL; + if (self->h==NULL) + { + PyErr_SetString(ErrorObject, "SaneDev object is closed"); + return NULL; + } + + sane_get_parameters(self->h, &p); + if (p.format != SANE_FRAME_GRAY) + { + sane_cancel(self->h); + snprintf(errmsg, 80, "numarray only supports gray-scale images"); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + + if (p.depth == 8) + { + bpp=1; /* bytes-per-pixel */ + arrType = tUInt8; + } + else if (p.depth == 16) + { + bpp=2; /* bytes-per-pixel */ + arrType = tUInt16; + } + else + { + sane_cancel(self->h); + snprintf(errmsg, 80, "arrsnap: unsupported pixel depth: %i", p.depth); + PyErr_SetString(ErrorObject, errmsg); + return NULL; + } + + if (pixels_per_line < 1) + /* The user can choose a smaller result array than the actual scan */ + pixels_per_line = p.pixels_per_line; + else + if (pixels_per_line > p.pixels_per_line) + { + PyErr_SetString(ErrorObject,"given pixels_per_line too big"); + return NULL; + } + /* important: NumArray have indices like (y, x) !! */ + if (!(pyArr = NA_NewArray(NULL, arrType, 2, p.lines, pixels_per_line))) + { + PyErr_SetString(ErrorObject, "failed to create NumArray object"); + return NULL; + } + + arr_bytes_per_line = pixels_per_line * bpp; + st=SANE_STATUS_GOOD; +#ifdef WRITE_PGM + FILE *fp; + fp = fopen("sane_p5.pgm", "w"); + fprintf(fp, "P5\n%d %d\n%d\n", p.pixels_per_line, + p.lines, (int) pow(2.0, (double) p.depth)-1); +#endif + line_index = line = 0; + remain_bytes_line = arr_bytes_per_line; + total_remain = p.bytes_per_line * p.lines; + num_pad_bytes = p.bytes_per_line - arr_bytes_per_line; + + while (st!=SANE_STATUS_EOF) + { + Py_BEGIN_ALLOW_THREADS + st = sane_read(self->h, buffer, + READSIZE < total_remain ? READSIZE : total_remain, &len); + Py_END_ALLOW_THREADS +#ifdef WRITE_PGM + printf("p5_write: read %d of %d\n", len, READSIZE); + fwrite(buffer, 1, len, fp); +#endif + + buffer_index = 0; + total_remain -= len; + + while (len > 0) + { + /* copy at most the number of bytes that fit into (the rest of) + one line: */ + cp_num_bytes = (len > remain_bytes_line ? remain_bytes_line : len); + remain_bytes_line -= cp_num_bytes; + len -= cp_num_bytes; +#ifdef DEBUG + printf("copying %d bytes from b_idx %d to d_idx %d\n", + cp_num_bytes, buffer_index, + line * arr_bytes_per_line + line_index); + printf("len is now %d\n", len); +#endif + memcpy(pyArr->data + line * arr_bytes_per_line + line_index, + buffer + buffer_index, cp_num_bytes); + + buffer_index += cp_num_bytes; + if (remain_bytes_line ==0) + { + /* The line has been completed, so reinitialize remain_bytes_line + increase the line counter, and reset line_index */ +#ifdef DEBUG + printf("line %d full, skipping %d bytes\n",line,num_pad_bytes); +#endif + remain_bytes_line = arr_bytes_per_line; + line++; + line_index = 0; + /* Skip the number of bytes in the input stream which + are not used: */ + len -= num_pad_bytes; + buffer_index += num_pad_bytes; + } + else + line_index += cp_num_bytes; + } + } +#ifdef WRITE_PGM + fclose(fp); + printf("p5_write finished\n"); +#endif + sane_cancel(self->h); + return (PyObject*) pyArr; +} + + + +#endif /* WITH_NUMARRAY */ + +static PyMethodDef SaneDev_methods[] = { + {"get_parameters", (PyCFunction)SaneDev_get_parameters, 1}, + + {"get_options", (PyCFunction)SaneDev_get_options, 1}, + {"get_option", (PyCFunction)SaneDev_get_option, 1}, + {"set_option", (PyCFunction)SaneDev_set_option, 1}, + {"set_auto_option", (PyCFunction)SaneDev_set_auto_option, 1}, + + {"start", (PyCFunction)SaneDev_start, 1}, + {"cancel", (PyCFunction)SaneDev_cancel, 1}, + {"snap", (PyCFunction)SaneDev_snap, 1}, +#ifdef WITH_NUMARRAY + {"arr_snap", (PyCFunction)SaneDev_arr_snap, 1}, +#endif /* WITH_NUMARRAY */ + {"fileno", (PyCFunction)SaneDev_fileno, 1}, + {"close", (PyCFunction)SaneDev_close, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SaneDev_getattr(SaneDevObject *self, char *name) +{ + return Py_FindMethod(SaneDev_methods, (PyObject *)self, name); +} + +staticforward PyTypeObject SaneDev_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /*ob_size*/ + "SaneDev", /*tp_name*/ + sizeof(SaneDevObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)SaneDev_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)SaneDev_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ +}; + +/* --------------------------------------------------------------------- */ + +static PyObject * +PySane_init(PyObject *self, PyObject *args) +{ + SANE_Status st; + SANE_Int version; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + + /* XXX Authorization is not yet supported */ + st=sane_init(&version, NULL); + if (st) return PySane_Error(st); + return Py_BuildValue("iiii", version, SANE_VERSION_MAJOR(version), + SANE_VERSION_MINOR(version), SANE_VERSION_BUILD(version)); +} + +static PyObject * +PySane_exit(PyObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + sane_exit(); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +PySane_get_devices(PyObject *self, PyObject *args) +{ + SANE_Device **devlist; + SANE_Device *dev; + SANE_Status st; + PyObject *list; + int local_only, i; + + if (!PyArg_ParseTuple(args, "|i", &local_only)) + { + return NULL; + } + + st=sane_get_devices(&devlist, local_only); + if (st) return PySane_Error(st); + if (!(list = PyList_New(0))) + return NULL; + for(i=0; devlist[i]!=NULL; i++) + { + dev=devlist[i]; + PyList_Append(list, Py_BuildValue("ssss", dev->name, dev->vendor, + dev->model, dev->type)); + } + + return list; +} + +/* Function returning new SaneDev object */ + +static PyObject * +PySane_open(PyObject *self, PyObject *args) +{ + SaneDevObject *rv; + SANE_Status st; + char *name; + + if (!PyArg_ParseTuple(args, "s", &name)) + return NULL; + rv = newSaneDevObject(); + if ( rv == NULL ) + return NULL; + st = sane_open(name, &(rv->h)); + if (st) + { + Py_DECREF(rv); + return PySane_Error(st); + } + return (PyObject *)rv; +} + +static PyObject * +PySane_OPTION_IS_ACTIVE(PyObject *self, PyObject *args) +{ + SANE_Int cap; + long lg; + + if (!PyArg_ParseTuple(args, "l", &lg)) + return NULL; + cap=lg; + return PyInt_FromLong( SANE_OPTION_IS_ACTIVE(cap)); +} + +static PyObject * +PySane_OPTION_IS_SETTABLE(PyObject *self, PyObject *args) +{ + SANE_Int cap; + long lg; + + if (!PyArg_ParseTuple(args, "l", &lg)) + return NULL; + cap=lg; + return PyInt_FromLong( SANE_OPTION_IS_SETTABLE(cap)); +} + + +/* List of functions defined in the module */ + +static PyMethodDef PySane_methods[] = { + {"init", PySane_init, 1}, + {"exit", PySane_exit, 1}, + {"get_devices", PySane_get_devices, 1}, + {"_open", PySane_open, 1}, + {"OPTION_IS_ACTIVE", PySane_OPTION_IS_ACTIVE, 1}, + {"OPTION_IS_SETTABLE", PySane_OPTION_IS_SETTABLE, 1}, + {NULL, NULL} /* sentinel */ +}; + + +static void +insint(PyObject *d, char *name, int value) +{ + PyObject *v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(d, name, v)) + Py_FatalError("can't initialize sane module"); + + Py_DECREF(v); +} + +void +init_sane(void) +{ + PyObject *m, *d; + + /* Create the module and add the functions */ + m = Py_InitModule("_sane", PySane_methods); + + /* Add some symbolic constants to the module */ + d = PyModule_GetDict(m); + ErrorObject = PyString_FromString("_sane.error"); + PyDict_SetItemString(d, "error", ErrorObject); + + insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); + insint(d, "INFO_RELOAD_OPTIONS", SANE_INFO_RELOAD_OPTIONS); + insint(d, "RELOAD_PARAMS", SANE_INFO_RELOAD_PARAMS); + + insint(d, "FRAME_GRAY", SANE_FRAME_GRAY); + insint(d, "FRAME_RGB", SANE_FRAME_RGB); + insint(d, "FRAME_RED", SANE_FRAME_RED); + insint(d, "FRAME_GREEN", SANE_FRAME_GREEN); + insint(d, "FRAME_BLUE", SANE_FRAME_BLUE); + + insint(d, "CONSTRAINT_NONE", SANE_CONSTRAINT_NONE); + insint(d, "CONSTRAINT_RANGE", SANE_CONSTRAINT_RANGE); + insint(d, "CONSTRAINT_WORD_LIST", SANE_CONSTRAINT_WORD_LIST); + insint(d, "CONSTRAINT_STRING_LIST", SANE_CONSTRAINT_STRING_LIST); + + insint(d, "TYPE_BOOL", SANE_TYPE_BOOL); + insint(d, "TYPE_INT", SANE_TYPE_INT); + insint(d, "TYPE_FIXED", SANE_TYPE_FIXED); + insint(d, "TYPE_STRING", SANE_TYPE_STRING); + insint(d, "TYPE_BUTTON", SANE_TYPE_BUTTON); + insint(d, "TYPE_GROUP", SANE_TYPE_GROUP); + + insint(d, "UNIT_NONE", SANE_UNIT_NONE); + insint(d, "UNIT_PIXEL", SANE_UNIT_PIXEL); + insint(d, "UNIT_BIT", SANE_UNIT_BIT); + insint(d, "UNIT_MM", SANE_UNIT_MM); + insint(d, "UNIT_DPI", SANE_UNIT_DPI); + insint(d, "UNIT_PERCENT", SANE_UNIT_PERCENT); + insint(d, "UNIT_MICROSECOND", SANE_UNIT_MICROSECOND); + + insint(d, "CAP_SOFT_SELECT", SANE_CAP_SOFT_SELECT); + insint(d, "CAP_HARD_SELECT", SANE_CAP_HARD_SELECT); + insint(d, "CAP_SOFT_DETECT", SANE_CAP_SOFT_DETECT); + insint(d, "CAP_EMULATED", SANE_CAP_EMULATED); + insint(d, "CAP_AUTOMATIC", SANE_CAP_AUTOMATIC); + insint(d, "CAP_INACTIVE", SANE_CAP_INACTIVE); + insint(d, "CAP_ADVANCED", SANE_CAP_ADVANCED); + + /* handy for checking array lengths: */ + insint(d, "SANE_WORD_SIZE", sizeof(SANE_Word)); + + /* possible return values of set_option() */ + insint(d, "INFO_INEXACT", SANE_INFO_INEXACT); + insint(d, "INFO_RELOAD_OPTIONS", SANE_INFO_RELOAD_OPTIONS); + insint(d, "INFO_RELOAD_PARAMS", SANE_INFO_RELOAD_PARAMS); + + /* Check for errors */ + if (PyErr_Occurred()) + Py_FatalError("can't initialize module _sane"); + +#ifdef WITH_NUMARRAY + import_libnumarray(); + if (PyErr_Occurred()) + PyErr_Clear(); + else + /* this global variable is declared just in front of the + arr_snap() function and should be set to 1 after + successfully importing the numarray module. */ + NUMARRAY_IMPORTED = 1; + +#endif /* WITH_NUMARRAY */ +} diff --git a/Sane/demo_numarray.py b/Sane/demo_numarray.py new file mode 100644 index 000000000..0104af2d5 --- /dev/null +++ b/Sane/demo_numarray.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# +# Shows how to scan a 16 bit grayscale image into a numarray object +# + +# Get the path set up to find PIL modules if not installed yet: +import sys ; sys.path.append('../PIL') + +from numarray import * +import sane +import Image + +def toImage(arr): + if arr.type().bytes == 1: + # need to swap coordinates btw array and image (with [::-1]) + im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + else: + arr_c = arr - arr.min() + arr_c *= (255./arr_c.max()) + arr = arr_c.astype(UInt8) + # need to swap coordinates btw array and image (with [::-1]) + im = Image.fromstring('L', arr.shape[::-1], arr.tostring()) + return im + +print 'SANE version:', sane.init() +print 'Available devices=', sane.get_devices() + +s = sane.open(sane.get_devices()[0][0]) + +# Set scan parameters +s.mode = 'gray' +s.br_x=320. ; s.br_y=240. + +print 'Device parameters:', s.get_parameters() + +s.depth=16 +arr16 = s.arr_scan() +toImage(arr16).show() diff --git a/Sane/demo_pil.py b/Sane/demo_pil.py new file mode 100644 index 000000000..016361f8a --- /dev/null +++ b/Sane/demo_pil.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +# +# Shows how to scan a color image into a PIL rgb-image +# + +# Get the path set up to find PIL modules if not installed yet: +import sys ; sys.path.append('../PIL') + +import sane +print 'SANE version:', sane.init() +print 'Available devices=', sane.get_devices() + +s = sane.open(sane.get_devices()[0][0]) + +s.mode = 'color' +s.br_x=320. ; s.br_y=240. + +print 'Device parameters:', s.get_parameters() + +# Initiate the scan +s.start() + +# Get an Image object +# (For my B&W QuickCam, this is a grey-scale image. Other scanning devices +# may return a +im=s.snap() + +# Write the image out as a GIF file +#im.save('foo.gif') + +# The show method() simply saves the image to a temporary file and calls "xv". +im.show() diff --git a/Sane/sane.py b/Sane/sane.py new file mode 100644 index 000000000..27be5a259 --- /dev/null +++ b/Sane/sane.py @@ -0,0 +1,289 @@ +# sane.py +# +# Python wrapper on top of the _sane module, which is in turn a very +# thin wrapper on top of the SANE library. For a complete understanding +# of SANE, consult the documentation at the SANE home page: +# http://www.mostang.com/sane/ . + +__version__ = '2.0' +__author__ = ['Andrew Kuchling', 'Ralph Heinkel'] + +from PIL import Image + +import _sane +from _sane import * + +TYPE_STR = { TYPE_BOOL: "TYPE_BOOL", TYPE_INT: "TYPE_INT", + TYPE_FIXED: "TYPE_FIXED", TYPE_STRING: "TYPE_STRING", + TYPE_BUTTON: "TYPE_BUTTON", TYPE_GROUP: "TYPE_GROUP" } + +UNIT_STR = { UNIT_NONE: "UNIT_NONE", + UNIT_PIXEL: "UNIT_PIXEL", + UNIT_BIT: "UNIT_BIT", + UNIT_MM: "UNIT_MM", + UNIT_DPI: "UNIT_DPI", + UNIT_PERCENT: "UNIT_PERCENT", + UNIT_MICROSECOND: "UNIT_MICROSECOND" } + + +class Option: + """Class representing a SANE option. + Attributes: + index -- number from 0 to n, giving the option number + name -- a string uniquely identifying the option + title -- single-line string containing a title for the option + desc -- a long string describing the option; useful as a help message + type -- type of this option. Possible values: TYPE_BOOL, + TYPE_INT, TYPE_STRING, and so forth. + unit -- units of this option. Possible values: UNIT_NONE, + UNIT_PIXEL, etc. + size -- size of the value in bytes + cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc. + constraint -- constraint on values. Possible values: + None : No constraint + (min,max,step) Integer values, from min to max, stepping by + list of integers or strings: only the listed values are allowed + """ + + def __init__(self, args, scanDev): + import string + self.scanDev = scanDev # needed to get current value of this option + self.index, self.name = args[0], args[1] + self.title, self.desc = args[2], args[3] + self.type, self.unit = args[4], args[5] + self.size, self.cap = args[6], args[7] + self.constraint = args[8] + def f(x): + if x=='-': return '_' + else: return x + if type(self.name)!=type(''): self.py_name=str(self.name) + else: self.py_name=string.join(map(f, self.name), '') + + def is_active(self): + return _sane.OPTION_IS_ACTIVE(self.cap) + def is_settable(self): + return _sane.OPTION_IS_SETTABLE(self.cap) + def __repr__(self): + if self.is_settable(): + settable = 'yes' + else: + settable = 'no' + if self.is_active(): + active = 'yes' + curValue = repr(getattr(self.scanDev, self.py_name)) + else: + active = 'no' + curValue = '' + s = """\nName: %s +Cur value: %s +Index: %d +Title: %s +Desc: %s +Type: %s +Unit: %s +Constr: %s +active: %s +settable: %s\n""" % (self.py_name, curValue, + self.index, self.title, self.desc, + TYPE_STR[self.type], UNIT_STR[self.unit], + `self.constraint`, active, settable) + return s + + +class _SaneIterator: + """ intended for ADF scans. + """ + + def __init__(self, device): + self.device = device + + def __iter__(self): + return self + + def __del__(self): + self.device.cancel() + + def next(self): + try: + self.device.start() + except error, v: + if v == 'Document feeder out of documents': + raise StopIteration + else: + raise + return self.device.snap(1) + + + +class SaneDev: + """Class representing a SANE device. + Methods: + start() -- initiate a scan, using the current settings + snap() -- snap a picture, returning an Image object + arr_snap() -- snap a picture, returning a numarray object + cancel() -- cancel an in-progress scanning operation + fileno() -- return the file descriptor for the scanner (handy for select) + + Also available, but rather low-level: + get_parameters() -- get the current parameter settings of the device + get_options() -- return a list of tuples describing all the options. + + Attributes: + optlist -- list of option names + + You can also access an option name to retrieve its value, and to + set it. For example, if one option has a .name attribute of + imagemode, and scanner is a SaneDev object, you can do: + print scanner.imagemode + scanner.imagemode = 'Full frame' + scanner.['imagemode'] returns the corresponding Option object. + """ + def __init__(self, devname): + d=self.__dict__ + d['sane_signature'] = self._getSaneSignature(devname) + d['scanner_model'] = d['sane_signature'][1:3] + d['dev'] = _sane._open(devname) + self.__load_option_dict() + + def _getSaneSignature(self, devname): + devices = get_devices() + if not devices: + raise RuntimeError('no scanner available') + for dev in devices: + if devname == dev[0]: + return dev + raise RuntimeError('no such scan device "%s"' % devname) + + def __load_option_dict(self): + d=self.__dict__ + d['opt']={} + optlist=d['dev'].get_options() + for t in optlist: + o=Option(t, self) + if o.type!=TYPE_GROUP: + d['opt'][o.py_name]=o + + def __setattr__(self, key, value): + dev=self.__dict__['dev'] + optdict=self.__dict__['opt'] + if not optdict.has_key(key): + self.__dict__[key]=value ; return + opt=optdict[key] + if opt.type==TYPE_GROUP: + raise AttributeError, "Groups can't be set: "+key + if not _sane.OPTION_IS_ACTIVE(opt.cap): + raise AttributeError, 'Inactive option: '+key + if not _sane.OPTION_IS_SETTABLE(opt.cap): + raise AttributeError, "Option can't be set by software: "+key + if type(value) == int and opt.type == TYPE_FIXED: + # avoid annoying errors of backend if int is given instead float: + value = float(value) + self.last_opt = dev.set_option(opt.index, value) + # do binary AND to find if we have to reload options: + if self.last_opt & INFO_RELOAD_OPTIONS: + self.__load_option_dict() + + def __getattr__(self, key): + dev=self.__dict__['dev'] + optdict=self.__dict__['opt'] + if key=='optlist': + return self.opt.keys() + if key=='area': + return (self.tl_x, self.tl_y),(self.br_x, self.br_y) + if not optdict.has_key(key): + raise AttributeError, 'No such attribute: '+key + opt=optdict[key] + if opt.type==TYPE_BUTTON: + raise AttributeError, "Buttons don't have values: "+key + if opt.type==TYPE_GROUP: + raise AttributeError, "Groups don't have values: "+key + if not _sane.OPTION_IS_ACTIVE(opt.cap): + raise AttributeError, 'Inactive option: '+key + value = dev.get_option(opt.index) + return value + + def __getitem__(self, key): + return self.opt[key] + + def get_parameters(self): + """Return a 5-tuple holding all the current device settings: + (format, last_frame, (pixels_per_line, lines), depth, bytes_per_line) + +- format is one of 'L' (grey), 'RGB', 'R' (red), 'G' (green), 'B' (blue). +- last_frame [bool] indicates if this is the last frame of a multi frame image +- (pixels_per_line, lines) specifies the size of the scanned image (x,y) +- lines denotes the number of scanlines per frame +- depth gives number of pixels per sample +""" + return self.dev.get_parameters() + + def get_options(self): + "Return a list of tuples describing all the available options" + return self.dev.get_options() + + def start(self): + "Initiate a scanning operation" + return self.dev.start() + + def cancel(self): + "Cancel an in-progress scanning operation" + return self.dev.cancel() + + def snap(self, no_cancel=0): + "Snap a picture, returning a PIL image object with the results" + (mode, last_frame, + (xsize, ysize), depth, bytes_per_line) = self.get_parameters() + if mode in ['gray', 'red', 'green', 'blue']: + format = 'L' + elif mode == 'color': + format = 'RGB' + else: + raise ValueError('got unknown "mode" from self.get_parameters()') + im=Image.new(format, (xsize,ysize)) + self.dev.snap( im.im.id, no_cancel ) + return im + + def scan(self): + self.start() + return self.snap() + + def multi_scan(self): + return _SaneIterator(self) + + def arr_snap(self, multipleOf=1): + """Snap a picture, returning a numarray object with the results. + By default the resulting array has the same number of pixels per + line as specified in self.get_parameters()[2][0] + However sometimes it is necessary to obtain arrays where + the number of pixels per line is e.g. a multiple of 4. This can then + be achieved with the option 'multipleOf=4'. So if the scanner + scanned 34 pixels per line, you will obtain an array with 32 pixels + per line. + """ + (mode, last_frame, (xsize, ysize), depth, bpl) = self.get_parameters() + if not mode in ['gray', 'red', 'green', 'blue']: + raise RuntimeError('arr_snap() only works with monochrome images') + if multipleOf < 1: + raise ValueError('option "multipleOf" must be a positive number') + elif multipleOf > 1: + pixels_per_line = xsize - divmod(xsize, 4)[1] + else: + pixels_per_line = xsize + return self.dev.arr_snap(pixels_per_line) + + def arr_scan(self, multipleOf=1): + self.start() + return self.arr_snap(multipleOf=multipleOf) + + def fileno(self): + "Return the file descriptor for the scanning device" + return self.dev.fileno() + + def close(self): + self.dev.close() + + +def open(devname): + "Open a device for scanning" + new=SaneDev(devname) + return new diff --git a/Sane/sanedoc.txt b/Sane/sanedoc.txt new file mode 100644 index 000000000..db86938e3 --- /dev/null +++ b/Sane/sanedoc.txt @@ -0,0 +1,294 @@ +The _sane_ module is an Python interface to the SANE (Scanning is Now +Easy) library, which provides access to various raster scanning +devices such as flatbed scanners and digital cameras. For more +information about SANE, consult the SANE Web site at +http://www.mostang.com/sane/ . Note that this +documentation doesn't duplicate all the information in the SANE +documentation, which you must also consult to get a complete +understanding. + +This module has been originally developed by A.M. Kuchling (amk1@erols.com), +now development has been taken over by Ralph Heinkel (rheinkel-at-email.de). +If you write to me please make sure to have the word 'SANE' or 'sane' in +the subject of your mail, otherwise it might be classified as spam in the +future. + + +The module exports two object types, a bunch of constants, and two +functions. + +get_devices() + Return a list of 4-tuples containing the available scanning + devices. Each tuple contains 4 strings: the device name, suitable for + passing to _open()_; the device's vendor; the model; and the type of + device, such as 'virtual device' or 'video camera'. + + >>> import sane ; sane.get_devices() + [('epson:libusb:001:004', 'Epson', 'GT-8300', 'flatbed scanner')] + +open(devicename) + Open a device, given a string containing its name. SANE + devices have names like 'epson:libusb:001:004'. If the attempt + to open the device fails, a _sane.error_ exception will be raised. If + there are no problems, a SaneDev object will be returned. + As an easy way to open the scanner (if only one is available) just type + >>> sane.open(sane.get_devices()[0][0]) + + +SaneDev objects +=============== + +The basic process of scanning an image consists of getting a SaneDev +object for the device, setting various parameters, starting the scan, +and then reading the image data. Images are composed of one or more +frames; greyscale and one-pass colour scanners return a single frame +containing all the image data, but 3-pass scanners will usually return +3 frames, one for each of the red, green, blue channels. + +Methods: +-------- +fileno() + Returns a file descriptor for the scanning device. This + method's existence means that SaneDev objects can be used by the + select module. + +get_parameters() + Return a tuple containing information about the current settings of + the device and the current frame: (format, last_frame, + pixels_per_line, lines, depth, bytes_per_line). + + mode -- 'gray' for greyscale image, 'color' for RGB image, or + one of 'red', 'green', 'blue' if the image is a single + channel of an RGB image (from PIL's point of view, + this is equivalent to 'L'). + last_frame -- A Boolean value, which is true if this is the + last frame of the image, and false otherwise. + pixels_per_line -- Width of the frame. + lines -- Height of the frame. + depth -- Depth of the image, measured in bits. SANE will only + allow using 8, 16, or 24-bit depths. + bytes_per_line -- Bytes required to store a single line of + data, as computed from pixels_per_line and depth. + +start() + Start a scan. This function must be called before the + _snap()_ method can be used. + +cancel() + Cancel a scan already in progress. + +snap(no_cancel=0) + Snap a single frame of data, returning a PIL Image object + containing the data. If no_cancel is false, the Sane library function + sane_cancel is called after the scan. This is reasonable in most cases, + but may cause backends for duplex ADF scanners to drop the backside image, + when snap() is called for the front side image. If no_cancel is true, + cancel() should be called manually, after all scans are finished. + +scan() + This is just a shortcut for s.start(); s.snap() + Returns a PIL image + +multi_scan() + This method returns an iterator. It is intended to be used for + scanning with an automatic document feeder. The next() method of the + iterator tries to start a scan. If this is successful, it returns a + PIL Image object, like scan(); if the document feeder runs out of + paper, it raises StopIteration, thereby signaling that the sequence + is ran out of items. + +arr_snap(multipleOf=1) + same as snap, but the result is a NumArray object. (Not that + num_array must be installed already at compilation time, otherwise + this feature will not be activated). + By default the resulting array has the same number of pixels per + line as specified in self.get_parameters()[2][0] + However sometimes it is necessary to obtain arrays where + the number of pixels per line is e.g. a multiple of 4. This can then + be achieved with the option 'multipleOf=4'. So if the scanner + scanned 34 pixels per line, you will obtain an array with 32 pixels + per line. + Note that this only works with monochrome images (e.g. gray-scales) + +arr_scan(multipleOf=1) + This is just a shortcut for s.start(); s.arr_snap(multipleOf=1) + Returns a NumArray object + +close() + Closes the object. + + +Attributes: +----------- +SaneDev objects have a few fixed attributes which are always +available, and a larger collection of attributes which vary depending +on the device. An Epson 1660 photo scanner has attributes like +'mode', 'depth', etc. +Another (pseudo scanner), the _pnm:0_ device, takes a PNM file and +simulates a scanner using the image data; a SaneDev object +representing the _pnm:0_ device therefore has a _filename_ attribute +which can be changed to specify the filename, _contrast_ and +_brightness_ attributes to modify the returned image, and so forth. + +The values of the scanner options may be an integer, floating-point +value, or string, depending on the nature of the option. + +sane_signature + The tuple for this scandev that is returned by sane.get_devices() + e.g. ('epson:libusb:001:006', 'Epson', 'GT-8300', 'flatbed scanner') + +scanner_model + same as sane_signature[1:3], i.e. ('Epson', 'GT-8300') for the case above. + +optlist + A list containing the all the options supported by this device. + + >>> import sane ; s=sane.open('epson:libusb:001:004') ; s.optlist + ['focus_position', 'color_correction', 'sharpness', ...., 'br_x'] + +A closer look at all options listed in s.optlist can be obtained +through the SaneOption objects. + +SaneOption objects +================== + +SANE's option handling is its most elaborate subsystem, intended to +allow automatically generating dialog boxes and prompts for user +configuration of the scanning device. The SaneOption object can be +used to get a human-readable name and description for an option, the +units to use, and what the legal values are. No information about the +current value of the option is available; for that, read the +corresponding attribute of a SaneDev object. + +This documentation does not explain all the details of SANE's option +handling; consult the SANE documentation for all the details. + +A scandevice option is accessed via __getitem__. For example +s['mode'] returns the option descriptor for the mode-option which +controls whether the scanner works in color, grayscale, or b/w mode. + +>>> s['mode'] +Name: mode +Cur value: Color +Index: 2 +Title: Scan mode +Desc: Selects the scan mode (e.g., lineart, monochrome, or color). +Type: TYPE_STRING +Unit: UNIT_NONE +Constr: ['Binary', 'Gray', 'Color'] +active: yes +settable: yes + +In order to change 'mode' to 'gray', just type: +>>> s.mode = 'gray' + + +With the attributes and methods of sane-option objects it is possible +to access individual option values: + +is_active() + Returns true if the option is active. + +is_settable() + Returns true if the option can be set under software control. + + +Attributes: + +cap + An integer containing various flags about the object's + capabilities; whether it's active, whether it's settable, etc. Also + available as the _capability_ attribute. + +constraint + The constraint placed on the value of this option. If it's + _None_, there are essentially no constraint of the value. It may also + be a list of integers or strings, in which case the value *must* be + one of the possibilities in the list. Numeric values may have a + 3-tuple as the constraint; this 3-tuple contains _(minimum, maximum, + increment)_, and the value must be in the defined range. + +desc + A lengthy description of what the option does; it may be shown + to the user for clarification. + +index + An integer giving the option's index in the option list. + +name + A short name for the option, as it comes from the sane-backend. + +py_name + The option's name, as a legal Python identifier. The name + attribute may contain the '-' character, so it will be converted to + '_' for the py_name attribute. + +size + For a string-valued option, this is the maximum length allowed. + +title + A single-line string that can be used as a title string. + +type + A constant giving the type of this option: will be one of the following + constants found in the SANE module: + TYPE_BOOL + TYPE_INT + TYPE_FIXED + TYPE_STRING + TYPE_BUTTON + TYPE_GROUP + +unit + For numeric-valued options, this is a constant representing + the unit used for this option. It will be one of the following + constants found in the SANE module: + UNIT_NONE + UNIT_PIXEL + UNIT_BIT + UNIT_MM + UNIT_DPI + UNIT_PERCENT + + + +Example us usage: +================= +>>> import sane +>>> print 'SANE version:', sane.init() +>>> print 'Available devices=', sane.get_devices() +SANE version: (16777230, 1, 0, 14) +>>> s = sane.open(sane.get_devices()[0][0]) +>>> print 'Device parameters:', s.get_parameters() +Device parameters: ('L', 1, (424, 585), 1, 53) +>>> print s.resolution +50 + +## In order to scan a color image into a PIL object: +>>> s.mode = 'color' +>>> s.start() +>>> img = s.snap() +>>> img.show() + + +## In order to obtain a 16-bit grayscale image at 100DPI in a numarray object +## with bottom-right coordinates set to (160, 120) [in millimeter] : +>>> s.mode = 'gray' +>>> s.br_x=160. ; s.br_y=120. +>>> s.resolution = 100 +>>> s.depth=16 +>>> s.start() +>>> s.get_parameters()[2] # just check the size +(624, 472) +>>> arr16 = s.arr_snap() +>>> arr16 +array([[63957, 64721, 65067, ..., 65535, 65535, 65535], + [63892, 64342, 64236, ..., 65535, 65535, 65535], + [64286, 64248, 64705, ..., 65535, 65535, 65535], + ..., + [65518, 65249, 65058, ..., 65535, 65535, 65535], + [64435, 65047, 65081, ..., 65535, 65535, 65535], + [65309, 65438, 65535, ..., 65535, 65535, 65535]], type=UInt16) +>>> arr16.shape # inverse order of coordinates, first y, then x! +(472, 624) + diff --git a/Sane/setup.py b/Sane/setup.py new file mode 100644 index 000000000..3837198ec --- /dev/null +++ b/Sane/setup.py @@ -0,0 +1,24 @@ +from distutils.core import setup, Extension + +PIL_BUILD_DIR = '..' +PIL_IMAGING_DIR = PIL_BUILD_DIR+'/libImaging' + +defs = [] +try: + import numarray + defs.append(('WITH_NUMARRAY',None)) +except ImportError: + pass + +sane = Extension('_sane', + include_dirs = [PIL_IMAGING_DIR], + libraries = ['sane'], + library_dirs = [PIL_IMAGING_DIR], + define_macros = defs, + sources = ['_sane.c']) + +setup (name = 'pysane', + version = '2.0', + description = 'This is the pysane package', + py_modules = ['sane'], + ext_modules = [sane]) diff --git a/Scripts/README b/Scripts/README new file mode 100644 index 000000000..a09b0621a --- /dev/null +++ b/Scripts/README @@ -0,0 +1,83 @@ +------- +Scripts +------- + +This directory contains a number of more or less trivial utilities +and demo programs. + +Comments and contributions are welcome. + + + +-------------------------------------------------------------------- +pildriver.py (by Eric S. Raymond) + +A class implementing an image-processing calculator for scripts. +Parses lists of commnds (or, called interactively, command-line +arguments) into image loads, transformations, and saves. + +-------------------------------------------------------------------- +viewer.py + +A simple image viewer. Can display all file formats handled by +PIL. Transparent images are properly handled. + +-------------------------------------------------------------------- +thresholder.py + +A simple utility that demonstrates how a transparent 1-bit overlay +can be used to show the current thresholding of an 8-bit image. + +-------------------------------------------------------------------- +enhancer.py + +Illustrates the ImageEnhance module. Drag the sliders to modify the +images. This might be very slow on some platforms, depending on the +Tk version. + +-------------------------------------------------------------------- +painter.py + +Illustrates how a painting program could be based on PIL and Tk. +Press the left mouse button and drag over the image to remove the +colour. Some clever tricks have been used to get decent performance +when updating the screen; see the sources for details. + +-------------------------------------------------------------------- +player.py + +A simple image sequence player. You can use either a sequence format +like FLI/FLC, GIF, or ARG, or give a number of images which are +interpreted as frames in a sequence. All frames must have the same +size. + +-------------------------------------------------------------------- +gifmaker.py + +Convert a sequence file to a GIF animation. + +Note that the GIF encoder provided with this release of PIL writes +uncompressed GIF files only, so the resulting animations are rather +large compared with these created by other tools. + +-------------------------------------------------------------------- +explode.py + +Split a sequence file into individual frames. + +-------------------------------------------------------------------- +image2py.py + +Convert an image to a Python module containing an IMAGE variable. +Note that the module using the module must include JPEG and ZIP +decoders, unless the -u option is used. + +-------------------------------------------------------------------- +olesummary.py + +Uses the OleFileIO module to dump the summary information from an OLE +structured storage file. This works with most OLE files, including +Word documents, FlashPix images, etc. + +Note that datetime fields currently show the number of seconds since +January 1st, 1601. diff --git a/Scripts/enhancer.py b/Scripts/enhancer.py new file mode 100644 index 000000000..957b51c8d --- /dev/null +++ b/Scripts/enhancer.py @@ -0,0 +1,53 @@ +# +# The Python Imaging Library +# $Id$ +# +# this demo script creates four windows containing an image and a slider. +# drag the slider to modify the image. +# + +from Tkinter import * +from PIL import Image, ImageTk, ImageEnhance +import sys + +# +# enhancer widget + +class Enhance(Frame): + def __init__(self, master, image, name, enhancer, lo, hi): + Frame.__init__(self, master) + + # set up the image + self.tkim = ImageTk.PhotoImage(image.mode, image.size) + self.enhancer = enhancer(image) + self.update("1.0") # normalize + + # image window + Label(self, image=self.tkim).pack() + + # scale + s = Scale(self, label=name, orient=HORIZONTAL, + from_=lo, to=hi, resolution=0.01, + command=self.update) + s.set(self.value) + s.pack() + + def update(self, value): + self.value = eval(value) + self.tkim.paste(self.enhancer.enhance(self.value)) + +# +# main + +root = Tk() + +im = Image.open(sys.argv[1]) + +im.thumbnail((200, 200)) + +Enhance(root, im, "Color", ImageEnhance.Color, 0.0, 4.0).pack() +Enhance(Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack() +Enhance(Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack() +Enhance(Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack() + +root.mainloop() diff --git a/Scripts/explode.py b/Scripts/explode.py new file mode 100644 index 000000000..a336f0699 --- /dev/null +++ b/Scripts/explode.py @@ -0,0 +1,107 @@ +# +# The Python Imaging Library +# $Id$ +# +# split an animation into a number of frame files +# + +from PIL import Image +import os, string, sys + +class Interval: + + def __init__(self, interval = "0"): + + self.setinterval(interval) + + def setinterval(self, interval): + + self.hilo = [] + + for s in string.split(interval, ","): + if not string.strip(s): + continue + try: + v = string.atoi(s) + if v < 0: + lo, hi = 0, -v + else: + lo = hi = v + except ValueError: + i = string.find(s, "-") + lo, hi = string.atoi(s[:i]), string.atoi(s[i+1:]) + + self.hilo.append((hi, lo)) + + if not self.hilo: + self.hilo = [(sys.maxint, 0)] + + def __getitem__(self, index): + + for hi, lo in self.hilo: + if hi >= index >= lo: + return 1 + return 0 + +# -------------------------------------------------------------------- +# main program + +html = 0 + +if sys.argv[1:2] == ["-h"]: + html = 1 + del sys.argv[1] + +if not sys.argv[2:]: + print + print "Syntax: python explode.py infile template [range]" + print + print "The template argument is used to construct the names of the" + print "individual frame files. The frames are numbered file001.ext," + print "file002.ext, etc. You can insert %d to control the placement" + print "and syntax of the frame number." + print + print "The optional range argument specifies which frames to extract." + print "You can give one or more ranges like 1-10, 5, -15 etc. If" + print "omitted, all frames are extracted." + sys.exit(1) + +infile = sys.argv[1] +outfile = sys.argv[2] + +frames = Interval(string.join(sys.argv[3:], ",")) + +try: + # check if outfile contains a placeholder + outfile % 1 +except TypeError: + file, ext = os.path.splitext(outfile) + outfile = file + "%03d" + ext + +ix = 1 + +im = Image.open(infile) + +if html: + file, ext = os.path.splitext(outfile) + html = open(file+".html", "w") + html.write("\n\n") + +while 1: + + if frames[ix]: + im.save(outfile % ix) + print outfile % ix + + if html: + html.write("
\n" % outfile % ix) + + try: + im.seek(ix) + except EOFError: + break + + ix = ix + 1 + +if html: + html.write("\n\n") diff --git a/Scripts/gifmaker.py b/Scripts/gifmaker.py new file mode 100644 index 000000000..95524eacc --- /dev/null +++ b/Scripts/gifmaker.py @@ -0,0 +1,135 @@ +# +# The Python Imaging Library +# $Id$ +# +# convert sequence format to GIF animation +# +# history: +# 97-01-03 fl created +# +# Copyright (c) Secret Labs AB 1997. All rights reserved. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +# +# For special purposes, you can import this module and call +# the makedelta or compress functions yourself. For example, +# if you have an application that generates a sequence of +# images, you can convert it to a GIF animation using some- +# thing like the following code: +# +# import Image +# import gifmaker +# +# sequence = [] +# +# # generate sequence +# for i in range(100): +# im = +# sequence.append(im) +# +# # write GIF animation +# fp = open("out.gif", "wb") +# gifmaker.makedelta(fp, sequence) +# fp.close() +# +# Alternatively, use an iterator to generate the sequence, and +# write data directly to a socket. Or something... +# + +from PIL import Image, ImageChops +import string + +from PIL.GifImagePlugin import getheader, getdata + +# -------------------------------------------------------------------- +# sequence iterator + +class image_sequence: + def __init__(self, im): + self.im = im + def __getitem__(self, ix): + try: + if ix: + self.im.seek(ix) + return self.im + except EOFError: + raise IndexError # end of sequence + +# -------------------------------------------------------------------- +# straightforward delta encoding + +def makedelta(fp, sequence): + """Convert list of image frames to a GIF animation file""" + + frames = 0 + + previous = None + + for im in sequence: + + # + # FIXME: write graphics control block before each frame + + if not previous: + + # global header + for s in getheader(im) + getdata(im): + fp.write(s) + + else: + + # delta frame + delta = ImageChops.subtract_modulo(im, previous) + + bbox = delta.getbbox() + + if bbox: + + # compress difference + for s in getdata(im.crop(bbox), offset = bbox[:2]): + fp.write(s) + + else: + # FIXME: what should we do in this case? + pass + + previous = im.copy() + + frames = frames + 1 + + fp.write(";") + + return frames + +# -------------------------------------------------------------------- +# main hack + +def compress(infile, outfile): + + # open input image, and force loading of first frame + im = Image.open(infile) + im.load() + + # open output file + fp = open(outfile, "wb") + + seq = image_sequence(im) + + makedelta(fp, seq) + + fp.close() + + +if __name__ == "__main__": + + import sys + + if len(sys.argv) < 3: + print "GIFMAKER -- create GIF animations" + print "Usage: gifmaker infile outfile" + sys.exit(1) + + compress(sys.argv[1], sys.argv[2]) diff --git a/Scripts/painter.py b/Scripts/painter.py new file mode 100644 index 000000000..efe307386 --- /dev/null +++ b/Scripts/painter.py @@ -0,0 +1,72 @@ +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates pasting into an already displayed +# photoimage. note that the current version of Tk updates the whole +# image everytime we paste, so to get decent performance, we split +# the image into a set of tiles. +# + +from Tkinter import * +from PIL import Image, ImageTk +import sys + +# +# painter widget + +class PaintCanvas(Canvas): + def __init__(self, master, image): + Canvas.__init__(self, master, width=image.size[0], height=image.size[1]) + + # fill the canvas + self.tile = {} + self.tilesize = tilesize = 32 + xsize, ysize = image.size + for x in range(0, xsize, tilesize): + for y in range(0, ysize, tilesize): + box = x, y, min(xsize, x+tilesize), min(ysize, y+tilesize) + tile = ImageTk.PhotoImage(image.crop(box)) + self.create_image(x, y, image=tile, anchor=NW) + self.tile[(x,y)] = box, tile + + self.image = image + + self.bind("", self.paint) + + def paint(self, event): + xy = event.x - 10, event.y - 10, event.x + 10, event.y + 10 + im = self.image.crop(xy) + + # process the image in some fashion + im = im.convert("L") + + self.image.paste(im, xy) + self.repair(xy) + + def repair(self, box): + # update canvas + dx = box[0] % self.tilesize + dy = box[1] % self.tilesize + for x in range(box[0]-dx, box[2]+1, self.tilesize): + for y in range(box[1]-dy, box[3]+1, self.tilesize): + try: + xy, tile = self.tile[(x, y)] + tile.paste(self.image.crop(xy)) + except KeyError: + pass # outside the image + self.update_idletasks() + +# +# main + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "RGB": + im = im.convert("RGB") + +PaintCanvas(root, im).pack() + +root.mainloop() diff --git a/Scripts/pilconvert.py b/Scripts/pilconvert.py new file mode 100644 index 000000000..1c688f7c9 --- /dev/null +++ b/Scripts/pilconvert.py @@ -0,0 +1,96 @@ +#! /usr/local/bin/python +# +# The Python Imaging Library. +# $Id$ +# +# convert image files +# +# History: +# 0.1 96-04-20 fl Created +# 0.2 96-10-04 fl Use draft mode when converting images +# 0.3 96-12-30 fl Optimize output (PNG, JPEG) +# 0.4 97-01-18 fl Made optimize an option (PNG, JPEG) +# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) +# + +import site +import getopt, string, sys + +from PIL import Image + +def usage(): + print "PIL Convert 0.5/1998-12-30 -- convert image files" + print "Usage: pilconvert [option] infile outfile" + print + print "Options:" + print + print " -c convert to format (default is given by extension)" + print + print " -g convert to greyscale" + print " -p convert to palette image (using standard palette)" + print " -r convert to rgb" + print + print " -o optimize output (trade speed for size)" + print " -q set compression quality (0-100, JPEG only)" + print + print " -f list supported file formats" + sys.exit(1) + +if len(sys.argv) == 1: + usage() + +try: + opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") +except getopt.error, v: + print v + sys.exit(1) + +format = None +convert = None + +options = { } + +for o, a in opt: + + if o == "-f": + Image.init() + id = Image.ID[:] + id.sort() + print "Supported formats (* indicates output format):" + for i in id: + if Image.SAVE.has_key(i): + print i+"*", + else: + print i, + sys.exit(1) + + elif o == "-c": + format = a + + if o == "-g": + convert = "L" + elif o == "-p": + convert = "P" + elif o == "-r": + convert = "RGB" + + elif o == "-o": + options["optimize"] = 1 + elif o == "-q": + options["quality"] = string.atoi(a) + +if len(argv) != 2: + usage() + +try: + im = Image.open(argv[0]) + if convert and im.mode != convert: + im.draft(convert, im.size) + im = im.convert(convert) + if format: + apply(im.save, (argv[1], format), options) + else: + apply(im.save, (argv[1],), options) +except: + print "cannot convert image", + print "(%s:%s)" % (sys.exc_type, sys.exc_value) diff --git a/Scripts/pildriver.py b/Scripts/pildriver.py new file mode 100644 index 000000000..5dd575a3f --- /dev/null +++ b/Scripts/pildriver.py @@ -0,0 +1,524 @@ +#!/usr/bin/env python +"""PILdriver, an image-processing calculator using PIL. + +An instance of class PILDriver is essentially a software stack machine +(Polish-notation interpreter) for sequencing PIL image +transformations. The state of the instance is the interpreter stack. + +The only method one will normally invoke after initialization is the +`execute' method. This takes an argument list of tokens, pushes them +onto the instance's stack, and then tries to clear the stack by +successive evaluation of PILdriver operators. Any part of the stack +not cleaned off persists and is part of the evaluation context for +the next call of the execute method. + +PILDriver doesn't catch any exceptions, on the theory that these +are actually diagnostic information that should be interpreted by +the calling code. + +When called as a script, the command-line arguments are passed to +a PILDriver instance. If there are no command-line arguments, the +module runs an interactive interpreter, each line of which is split into +space-separated tokens and passed to the execute method. + +In the method descriptions below, a first line beginning with the string +`usage:' means this method can be invoked with the token that follows +it. Following <>-enclosed arguments describe how the method interprets +the entries on the stack. Each argument specification begins with a +type specification: either `int', `float', `string', or `image'. + +All operations consume their arguments off the stack (use `dup' to +keep copies around). Use `verbose 1' to see the stack state displayed +before each operation. + +Usage examples: + + `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion +of its upper-left-hand corner and displays the cropped portion. + + `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it +30 degrees, and saves the result as rotated.png (in PNG format). +""" +# by Eric S. Raymond +# $Id$ + +# TO DO: +# 1. Add PILFont capabilities, once that's documented. +# 2. Add PILDraw operations. +# 3. Add support for composing and decomposing multiple-image files. +# + +from PIL import Image +import string + +class PILDriver: + + verbose = 0 + + def do_verbose(self): + """usage: verbose + + Set verbosity flag from top of stack. + """ + self.verbose = self.do_pop() + + # The evaluation stack (internal only) + + stack = [] # Stack of pending operations + + def push(self, item): + "Push an argument onto the evaluation stack." + self.stack = [item] + self.stack + + def top(self): + "Return the top-of-stack element." + return self.stack[0] + + # Stack manipulation (callable) + + def do_clear(self): + """usage: clear + + Clear the stack. + """ + self.stack = [] + + def do_pop(self): + """usage: pop + + Discard the top element on the stack. + """ + top = self.stack[0] + self.stack = self.stack[1:] + return top + + def do_dup(self): + """usage: dup + + Duplicate the top-of-stack item. + """ + if hasattr(self, 'format'): # If it's an image, do a real copy + dup = self.stack[0].copy() + else: + dup = self.stack[0] + self.stack = [dup] + self.stack + + def do_swap(self): + """usage: swap + + Swap the top-of-stack item with the next one down. + """ + self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] + + # Image module functions (callable) + + def do_new(self): + """usage: new : + + Create and push a greyscale image of given size and color. + """ + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + color = int(self.do_pop()) + self.push(Image.new("L", (xsize, ysize), color)) + + def do_open(self): + """usage: open + + Open the indicated image, read it, push the image on the stack. + """ + self.push(Image.open(self.do_pop())) + + def do_blend(self): + """usage: blend + + Replace two images and an alpha with the blended image. + """ + image1 = self.do_pop() + image2 = self.do_pop() + alpha = float(self.do_pop()) + self.push(Image.blend(image1, image2, alpha)) + + def do_composite(self): + """usage: composite + + Replace two images and a mask with their composite. + """ + image1 = self.do_pop() + image2 = self.do_pop() + mask = self.do_pop() + self.push(Image.composite(image1, image2, mask)) + + def do_merge(self): + """usage: merge [ [ []]] + + Merge top-of stack images in a way described by the mode. + """ + mode = self.do_pop() + bandlist = [] + for band in mode: + bandlist.append(self.do_pop()) + self.push(Image.merge(mode, bandlist)) + + # Image class methods + + def do_convert(self): + """usage: convert + + Convert the top image to the given mode. + """ + mode = self.do_pop() + image = self.do_pop() + self.push(image.convert(mode)) + + def do_copy(self): + """usage: copy + + Make and push a true copy of the top image. + """ + self.dup() + + def do_crop(self): + """usage: crop + + Crop and push a rectangular region from the current image. + """ + left = int(self.do_pop()) + upper = int(self.do_pop()) + right = int(self.do_pop()) + lower = int(self.do_pop()) + image = self.do_pop() + self.push(image.crop((left, upper, right, lower))) + + def do_draft(self): + """usage: draft + + Configure the loader for a given mode and size. + """ + mode = self.do_pop() + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + self.push(self.draft(mode, (xsize, ysize))) + + def do_filter(self): + """usage: filter + + Process the top image with the given filter. + """ + import ImageFilter + filter = eval("ImageFilter." + string.upper(self.do_pop())) + image = self.do_pop() + self.push(image.filter(filter)) + + def do_getbbox(self): + """usage: getbbox + + Push left, upper, right, and lower pixel coordinates of the top image. + """ + bounding_box = self.do_pop().getbbox() + self.push(bounding_box[3]) + self.push(bounding_box[2]) + self.push(bounding_box[1]) + self.push(bounding_box[0]) + + def do_getextrema(self): + """usage: extrema + + Push minimum and maximum pixel values of the top image. + """ + extrema = self.do_pop().extrema() + self.push(extrema[1]) + self.push(extrema[0]) + + def do_offset(self): + """usage: offset + + Offset the pixels in the top image. + """ + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + image = self.do_pop() + self.push(image.offset(xoff, yoff)) + + def do_paste(self): + """usage: paste + + Paste figure image into ground with upper left at given offsets. + """ + figure = self.do_pop() + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + ground = self.do_pop() + if figure.mode == "RGBA": + ground.paste(figure, (xoff, yoff), figure) + else: + ground.paste(figure, (xoff, yoff)) + self.push(ground) + + def do_resize(self): + """usage: resize + + Resize the top image. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + image = self.do_pop() + self.push(image.resize((xsize, ysize))) + + def do_rotate(self): + """usage: rotate + + Rotate image through a given angle + """ + angle = int(self.do_pop()) + image = self.do_pop() + self.push(image.rotate(angle)) + + def do_save(self): + """usage: save + + Save image with default options. + """ + filename = self.do_pop() + image = self.do_pop() + image.save(filename) + + def do_save2(self): + """usage: save2 + + Save image with specified options. + """ + filename = self.do_pop() + options = self.do_pop() + image = self.do_pop() + image.save(filename, None, options) + + def do_show(self): + """usage: show + + Display and pop the top image. + """ + self.do_pop().show() + + def do_thumbnail(self): + """usage: thumbnail + + Modify the top image in the stack to contain a thumbnail of itself. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + self.top().thumbnail((xsize, ysize)) + + def do_transpose(self): + """usage: transpose + + Transpose the top image. + """ + transpose = string.upper(self.do_pop()) + image = self.do_pop() + self.push(image.transpose(transpose)) + + # Image attributes + + def do_format(self): + """usage: format + + Push the format of the top image onto the stack. + """ + self.push(self.pop().format) + + def do_mode(self): + """usage: mode + + Push the mode of the top image onto the stack. + """ + self.push(self.pop().mode) + + def do_size(self): + """usage: size + + Push the image size on the stack as (y, x). + """ + size = self.pop().size + self.push(size[0]) + self.push(size[1]) + + # ImageChops operations + + def do_invert(self): + """usage: invert + + Invert the top image. + """ + import ImageChops + self.push(ImageChops.invert(self.do_pop())) + + def do_lighter(self): + """usage: lighter + + Pop the two top images, push an image of the lighter pixels of both. + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.lighter(image1, image2)) + + def do_darker(self): + """usage: darker + + Pop the two top images, push an image of the darker pixels of both. + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.darker(image1, image2)) + + def do_difference(self): + """usage: difference + + Pop the two top images, push the difference image + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.difference(image1, image2)) + + def do_multiply(self): + """usage: multiply + + Pop the two top images, push the multiplication image. + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.multiply(image1, image2)) + + def do_screen(self): + """usage: screen + + Pop the two top images, superimpose their inverted versions. + """ + import ImageChops + image2 = self.do_pop() + image1 = self.do_pop() + self.push(ImageChops.screen(image1, image2)) + + def do_add(self): + """usage: add + + Pop the two top images, produce the scaled sum with offset. + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.add(image1, image2, scale, offset)) + + def do_subtract(self): + """usage: subtract + + Pop the two top images, produce the scaled difference with offset. + """ + import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.subtract(image1, image2, scale, offset)) + + # ImageEnhance classes + + def do_color(self): + """usage: color + + Enhance color in the top image. + """ + import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_contrast(self): + """usage: contrast + + Enhance contrast in the top image. + """ + import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_brightness(self): + """usage: brightness + + Enhance brightness in the top image. + """ + import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_sharpness(self): + """usage: sharpness + + Enhance sharpness in the top image. + """ + import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + # The interpreter loop + + def execute(self, list): + "Interpret a list of PILDriver commands." + list.reverse() + while len(list) > 0: + self.push(list[0]) + list = list[1:] + if self.verbose: + print "Stack: " + `self.stack` + top = self.top() + if type(top) != type(""): + continue; + funcname = "do_" + top + if not hasattr(self, funcname): + continue + else: + self.do_pop() + func = getattr(self, funcname) + func() + +if __name__ == '__main__': + import sys + try: + import readline + except ImportError: + pass # not available on all platforms + + # If we see command-line arguments, interpret them as a stack state + # and execute. Otherwise go interactive. + + driver = PILDriver() + if len(sys.argv[1:]) > 0: + driver.execute(sys.argv[1:]) + else: + print "PILDriver says hello." + while 1: + try: + line = raw_input('pildriver> '); + except EOFError: + print "\nPILDriver says goodbye." + break + driver.execute(string.split(line)) + print driver.stack + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: diff --git a/Scripts/pilfile.py b/Scripts/pilfile.py new file mode 100644 index 000000000..695725796 --- /dev/null +++ b/Scripts/pilfile.py @@ -0,0 +1,94 @@ +#! /usr/local/bin/python +# +# The Python Imaging Library. +# $Id$ +# +# a utility to identify image files +# +# this script identifies image files, extracting size and +# pixel mode information for known file formats. Note that +# you don't need the PIL C extension to use this module. +# +# History: +# 0.0 1995-09-01 fl Created +# 0.1 1996-05-18 fl Modified options, added debugging mode +# 0.2 1996-12-29 fl Added verify mode +# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later) +# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks +# + +import site +import getopt, glob, sys + +from PIL import Image + +if len(sys.argv) == 1: + print "PIL File 0.4/2003-09-30 -- identify image files" + print "Usage: pilfile [option] files..." + print "Options:" + print " -f list supported file formats" + print " -i show associated info and tile data" + print " -v verify file headers" + print " -q quiet, don't warn for unidentified/missing/broken files" + sys.exit(1) + +try: + opt, args = getopt.getopt(sys.argv[1:], "fqivD") +except getopt.error, v: + print v + sys.exit(1) + +verbose = quiet = verify = 0 + +for o, a in opt: + if o == "-f": + Image.init() + id = Image.ID[:] + id.sort() + print "Supported formats:" + for i in id: + print i, + sys.exit(1) + elif o == "-i": + verbose = 1 + elif o == "-q": + quiet = 1 + elif o == "-v": + verify = 1 + elif o == "-D": + Image.DEBUG = Image.DEBUG + 1 + +def globfix(files): + # expand wildcards where necessary + if sys.platform == "win32": + out = [] + for file in files: + if glob.has_magic(file): + out.extend(glob.glob(file)) + else: + out.append(file) + return out + return files + +for file in globfix(args): + try: + im = Image.open(file) + print "%s:" % file, im.format, "%dx%d" % im.size, im.mode, + if verbose: + print im.info, im.tile, + print + if verify: + try: + im.verify() + except: + if not quiet: + print "failed to verify image", + print "(%s:%s)" % (sys.exc_type, sys.exc_value) + except IOError, v: + if not quiet: + print file, "failed:", v + except: + import traceback + if not quiet: + print file, "failed:", "unexpected error" + traceback.print_exc(file=sys.stdout) diff --git a/Scripts/pilfont.py b/Scripts/pilfont.py new file mode 100644 index 000000000..df08d4c08 --- /dev/null +++ b/Scripts/pilfont.py @@ -0,0 +1,54 @@ +# +# The Python Imaging Library +# $Id$ +# +# PIL raster font compiler +# +# history: +# 1997-08-25 fl created +# 2002-03-10 fl use "from PIL import" +# + +VERSION = "0.4" + +import site +import glob, os, sys + +# drivers +from PIL import BdfFontFile +from PIL import PcfFontFile + +if len(sys.argv) <= 1: + print "PILFONT", VERSION, "-- PIL font compiler." + print + print "Usage: pilfont fontfiles..." + print + print "Convert given font files to the PIL raster font format." + print "This version of pilfont supports X BDF and PCF fonts." + sys.exit(1) + +files = [] +for f in sys.argv[1:]: + files = files + glob.glob(f) + +for f in files: + + print f + "...", + + try: + + fp = open(f, "rb") + + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + p.save(f) + + except (SyntaxError, IOError): + print "failed" + + else: + print "OK" diff --git a/Scripts/pilprint.py b/Scripts/pilprint.py new file mode 100644 index 000000000..a98b39f7d --- /dev/null +++ b/Scripts/pilprint.py @@ -0,0 +1,93 @@ +#! /usr/local/bin/python +# +# The Python Imaging Library. +# $Id$ +# +# print image files to postscript printer +# +# History: +# 0.1 1996-04-20 fl Created +# 0.2 1996-10-04 fl Use draft mode when converting. +# 0.3 2003-05-06 fl Fixed a typo or two. +# + +VERSION = "pilprint 0.3/2003-05-05" + +from PIL import Image +from PIL import PSDraw + +letter = ( 1.0*72, 1.0*72, 7.5*72, 10.0*72 ) + +def description(file, image): + import os + title = os.path.splitext(os.path.split(file)[1])[0] + format = " (%dx%d " + if image.format: + format = " (" + image.format + " %dx%d " + return title + format % image.size + image.mode + ")" + +import getopt, os, sys + +if len(sys.argv) == 1: + print "PIL Print 0.2a1/96-10-04 -- print image files" + print "Usage: pilprint files..." + print "Options:" + print " -c colour printer (default is monochrome)" + print " -p print via lpr (default is stdout)" + print " -P same as -p but use given printer" + sys.exit(1) + +try: + opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") +except getopt.error, v: + print v + sys.exit(1) + +printer = None # print to stdout +monochrome = 1 # reduce file size for most common case + +for o, a in opt: + if o == "-d": + # debug: show available drivers + Image.init() + print Image.ID + sys.exit(1) + elif o == "-c": + # colour printer + monochrome = 0 + elif o == "-p": + # default printer channel + printer = "lpr" + elif o == "-P": + # printer channel + printer = "lpr -P%s" % a + +for file in argv: + try: + + im = Image.open(file) + + title = description(file, im) + + if monochrome and im.mode not in ["1", "L"]: + im.draft("L", im.size) + im = im.convert("L") + + if printer: + fp = os.popen(printer, "w") + else: + fp = sys.stdout + + ps = PSDraw.PSDraw(fp) + + ps.begin_document() + ps.setfont("Helvetica-Narrow-Bold", 18) + ps.text((letter[0], letter[3]+24), title) + ps.setfont("Helvetica-Narrow-Bold", 8) + ps.text((letter[0], letter[1]-30), VERSION) + ps.image(letter, im) + ps.end_document() + + except: + print "cannot print image", + print "(%s:%s)" % (sys.exc_type, sys.exc_value) diff --git a/Scripts/player.py b/Scripts/player.py new file mode 100644 index 000000000..9ca4500bf --- /dev/null +++ b/Scripts/player.py @@ -0,0 +1,121 @@ +# +# The Python Imaging Library +# $Id$ +# + +from Tkinter import * +from PIL import Image, ImageTk +import sys + + +Image.DEBUG = 0 + + +# -------------------------------------------------------------------- +# experimental: support ARG animation scripts + +import ArgImagePlugin + +def applet_hook(animation, images): + app = animation(animation_display, images) + app.run() + +ArgImagePlugin.APPLET_HOOK = applet_hook + +class AppletDisplay: + def __init__(self, ui): + self.__ui = ui + def paste(self, im, bbox): + self.__ui.image.paste(im, bbox) + def update(self): + self.__ui.update_idletasks() + +# -------------------------------------------------------------------- +# an image animation player + +class UI(Label): + + def __init__(self, master, im): + if type(im) == type([]): + # list of images + self.im = im[1:] + im = self.im[0] + else: + # sequence + self.im = im + + if im.mode == "1": + self.image = ImageTk.BitmapImage(im, foreground="white") + else: + self.image = ImageTk.PhotoImage(im) + + # APPLET SUPPORT (very crude, and not 100% safe) + global animation_display + animation_display = AppletDisplay(self) + + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + self.update() + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + def next(self): + + if type(self.im) == type([]): + + try: + im = self.im[0] + del self.im[0] + self.image.paste(im) + except IndexError: + return # end of list + + else: + + try: + im = self.im + im.seek(im.tell() + 1) + self.image.paste(im) + except EOFError: + return # end of file + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + self.update_idletasks() + + +# -------------------------------------------------------------------- +# script interface + +if __name__ == "__main__": + + if not sys.argv[1:]: + print "Syntax: python player.py imagefile(s)" + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + if len(sys.argv) > 2: + # list of images + print "loading..." + im = [] + for filename in sys.argv[1:]: + im.append(Image.open(filename)) + else: + # sequence + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/Scripts/thresholder.py b/Scripts/thresholder.py new file mode 100644 index 000000000..eb5109872 --- /dev/null +++ b/Scripts/thresholder.py @@ -0,0 +1,68 @@ +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates how a 1-bit BitmapImage can be used +# as a dynamically updated overlay +# + +from Tkinter import * +from PIL import Image, ImageTk +import sys + +# +# an image viewer + +class UI(Frame): + def __init__(self, master, im, value = 128): + Frame.__init__(self, master) + + self.image = im + self.value = value + + self.canvas = Canvas(self, width=im.size[0], height=im.size[1]) + self.backdrop = ImageTk.PhotoImage(im) + self.canvas.create_image(0, 0, image=self.backdrop, anchor=NW) + self.canvas.pack() + + scale = Scale(self, orient=HORIZONTAL, from_=0, to=255, + resolution=1, command=self.update, length=256) + scale.set(value) + scale.bind("", self.redraw) + scale.pack() + + # uncomment the following line for instant feedback (might + # be too slow on some platforms) + # self.redraw() + + def update(self, value): + self.value = eval(value) + + self.redraw() + + def redraw(self, event = None): + + # create overlay (note the explicit conversion to mode "1") + im = self.image.point(lambda v,t=self.value: v>=t, "1") + self.overlay = ImageTk.BitmapImage(im, foreground="green") + + # update canvas + self.canvas.delete("overlay") + self.canvas.create_image(0, 0, image=self.overlay, anchor=NW, + tags="overlay") + +# -------------------------------------------------------------------- +# main + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "L": + im = im.convert("L") + +# im.thumbnail((320,200)) + +UI(root, im).pack() + +root.mainloop() diff --git a/Scripts/viewer.py b/Scripts/viewer.py new file mode 100644 index 000000000..6e4dc8b67 --- /dev/null +++ b/Scripts/viewer.py @@ -0,0 +1,46 @@ +# +# The Python Imaging Library +# $Id$ +# + +from Tkinter import * +from PIL import Image, ImageTk + +# +# an image viewer + +class UI(Label): + + def __init__(self, master, im): + + if im.mode == "1": + # bitmap image + self.image = ImageTk.BitmapImage(im, foreground="white") + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + else: + # photo image + self.image = ImageTk.PhotoImage(im) + Label.__init__(self, master, image=self.image, bd=0) + +# +# script interface + +if __name__ == "__main__": + + import sys + + if not sys.argv[1:]: + print "Syntax: python viewer.py imagefile" + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/Tk/booster.txt b/Tk/booster.txt new file mode 100644 index 000000000..2d787983b --- /dev/null +++ b/Tk/booster.txt @@ -0,0 +1,108 @@ +==================================================================== +The Photoimage Booster Patch (for Windows 95/NT) +==================================================================== + + This patch kit boosts performance for 16/24-bit displays. The +first patch is required on Tk 4.2 (where it fixes the problems for +16-bit displays) and later versions, with the exception for Tk 8.0b1 +where Sun added something similar themselves, only to remove it in +8.0b2. By installing both patches, Tk's PhotoImage handling becomes +much faster on both 16-bit and 24-bit displays. The patch has been +tested with Tk 4.2 and 8.0. + + Here's a benchmark, made with a sample program which loads two +512x512 greyscale PGM's, and two 512x512 colour PPM's, and displays +each of them in a separate toplevel windows. Tcl/Tk was compiled +with Visual C 4.0, and run on a P100 under Win95. Image load times +are not included in the timings: + + 8-bit 16-bit 24-bit +-------------------------------------------------------------------- +1. original 4.2 code 5.52 s 8.57 s 3.79 s +2. booster patch 5.49 s 1.87 s 1.82 s + + speedup None 4.6x 2.1x + +==================================================================== + +Here's the patches: + +1. For portability and speed, the best thing under Windows is to +treat 16-bit displays as if they were 24-bit. The Windows device +drivers take care of the rest. + + ---------------------------------------------------------------- + If you have Tk 4.1 or Tk 8.0b1, you don't have to apply this + patch! It only applies to Tk 4.2, Tk 8.0a[12] and Tk 8.0b2. + ---------------------------------------------------------------- + +In win/tkWinImage.c, change the following line in XCreateImage: + + imagePtr->bits_per_pixel = depth; + +to + +/* ==================================================================== */ +/* The tk photo image booster patch -- patch section 1 */ +/* ==================================================================== */ + + if (visual->class == TrueColor) + /* true colour is stored as 3 bytes: (blue, green, red) */ + imagePtr->bits_per_pixel = 24; + else + imagePtr->bits_per_pixel = depth; + +/* ==================================================================== */ + + +2. The DitherInstance implementation is not good. It's especially +bad on highend truecolour displays. IMO, it should be rewritten from +scratch (some other day...). + + Anyway, the following band-aid makes the situation a little bit +better under Windows. This hack trades some marginal quality (no +dithering on 16-bit displays) for a dramatic performance boost. +Requires patch 1, unless you're using Tk 4.1 or Tk 8.0b1. + +In generic/tkImgPhoto.c, add the #ifdef section to the DitherInstance +function: + + for (; height > 0; height -= nLines) { + if (nLines > height) { + nLines = height; + } + dstLinePtr = (unsigned char *) imagePtr->data; + yEnd = yStart + nLines; + +/* ==================================================================== */ +/* The tk photo image booster patch -- patch section 2 */ +/* ==================================================================== */ + +#ifdef __WIN32__ + if (colorPtr->visualInfo.class == TrueColor + && instancePtr->gamma == 1.0) { + /* Windows hicolor/truecolor booster */ + for (y = yStart; y < yEnd; ++y) { + destBytePtr = dstLinePtr; + srcPtr = srcLinePtr; + for (x = xStart; x < xEnd; ++x) { + destBytePtr[0] = srcPtr[2]; + destBytePtr[1] = srcPtr[1]; + destBytePtr[2] = srcPtr[0]; + destBytePtr += 3; srcPtr += 3; + } + srcLinePtr += lineLength; + dstLinePtr += bytesPerLine; + } + } else +#endif + +/* ==================================================================== */ + + for (y = yStart; y < yEnd; ++y) { + srcPtr = srcLinePtr; + errPtr = errLinePtr; + destBytePtr = dstLinePtr; + +==================================================================== +last updated: 97-07-03/fl diff --git a/Tk/install.txt b/Tk/install.txt new file mode 100644 index 000000000..e24d36301 --- /dev/null +++ b/Tk/install.txt @@ -0,0 +1,41 @@ +==================================================================== +Using PIL With Tkinter +==================================================================== + +Starting with 1.0 final (release candidate 2 and later, to be +precise), PIL can attach itself to Tkinter in flight. As a result, +you no longer need to rebuild the Tkinter extension to be able to +use PIL. + +However, if you cannot get the this to work on your platform, you +can do it in the old way: + +* Adding Tkinter support + +1. Compile Python's _tkinter.c with the WITH_APPINIT and WITH_PIL + flags set, and link it with tkImaging.c and tkappinit.c. To + do this, copy the former to the Modules directory, and edit + the _tkinter line in Setup (or Setup.in) according to the + instructions in that file. + + NOTE: if you have an old Python version, the tkappinit.c + file is not included by default. If this is the case, you + will have to add the following lines to tkappinit.c, after + the MOREBUTTONS stuff: + + { + extern void TkImaging_Init(Tcl_Interp* interp); + TkImaging_Init(interp); + } + + This registers a Tcl command called "PyImagingPhoto", which is + use to communicate between PIL and Tk's PhotoImage handler. + + You must also change the _tkinter line in Setup (or Setup.in) + to something like: + + _tkinter _tkinter.c tkImaging.c tkappinit.c -DWITH_APPINIT + -I/usr/local/include -L/usr/local/lib -ltk8.0 -ltcl8.0 -lX11 + + + diff --git a/Tk/pilbitmap.txt b/Tk/pilbitmap.txt new file mode 100644 index 000000000..e7c46c65f --- /dev/null +++ b/Tk/pilbitmap.txt @@ -0,0 +1,149 @@ +==================================================================== +The PIL Bitmap Booster Patch +==================================================================== + +The pilbitmap booster patch greatly improves performance of the +ImageTk.BitmapImage constructor. Unfortunately, the design of Tk +doesn't allow us to do this from the tkImaging interface module, so +you have to patch the Tk sources. + +Once installed, the ImageTk module will automatically detect this +patch. + +(Note: this patch has been tested with Tk 8.0 on Win32 only, but it +should work just fine on other platforms as well). + +1. To the beginning of TkGetBitmapData (in generic/tkImgBmap.c), add + the following stuff: + +------------------------------------------------------------------------ + int width, height, numBytes, hotX, hotY; + char *p, *end, *expandedFileName; + ParseInfo pi; + char *data = NULL; + Tcl_DString buffer; + +/* ==================================================================== */ +/* The pilbitmap booster patch -- patch section */ +/* ==================================================================== */ + + char *PILGetBitmapData(); + + if (string) { + /* Is this a PIL bitmap reference? */ + data = PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr); + if (data) + return data; + } + +/* ==================================================================== */ + + pi.string = string; + if (string == NULL) { + if (Tcl_IsSafe(interp)) { +------------------------------------------------------------------------ + + +2. Append the following to the same file (you may wish to include +Imaging.h instead of copying the struct declaration...) + +------------------------------------------------------------------------ + +/* ==================================================================== */ +/* The pilbitmap booster patch -- code section */ +/* ==================================================================== */ + +/* Imaging declaration boldly copied from Imaging.h (!) */ + +typedef struct ImagingInstance *Imaging; /* a.k.a. ImagingImage :-) */ + +typedef unsigned char UINT8; +typedef int INT32; + +struct ImagingInstance { + + /* Format */ + char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ + int type; /* Always 0 in this version */ + int depth; /* Always 8 in this version */ + int bands; /* Number of bands (1, 3, or 4) */ + int xsize; /* Image dimension. */ + int ysize; + + /* Colour palette (for "P" images only) */ + void* palette; + + /* Data pointers */ + UINT8 **image8; /* Set for 8-bit image (pixelsize=1). */ + INT32 **image32; /* Set for 32-bit image (pixelsize=4). */ + + /* Internals */ + char **image; /* Actual raster data. */ + char *block; /* Set if data is allocated in a single block. */ + + int pixelsize; /* Size of a pixel, in bytes (1 or 4) */ + int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ + + /* Virtual methods */ + void (*im_delete)(Imaging *); + +}; + +/* The pilbitmap booster patch allows you to pass PIL images to the + Tk bitmap decoder. Passing images this way is much more efficient + than using the "tobitmap" method. */ + +char * +PILGetBitmapData(string, widthPtr, heightPtr, hotXPtr, hotYPtr) + char *string; + int *widthPtr, *heightPtr; + int *hotXPtr, *hotYPtr; +{ + char* data; + char* p; + int y; + Imaging im; + + if (strncmp(string, "PIL:", 4) != 0) + return NULL; + + im = (Imaging) atol(string + 4); + + if (strcmp(im->mode, "1") != 0 && strcmp(im->mode, "L") != 0) + return NULL; + + data = p = (char *) ckalloc((unsigned) ((im->xsize+7)/8) * im->ysize); + + for (y = 0; y < im->ysize; y++) { + char* in = im->image8[y]; + int i, m, b; + b = 0; m = 1; + for (i = 0; i < im->xsize; i++) { + if (in[i] != 0) + b |= m; + m <<= 1; + if (m == 256){ + *p++ = b; + b = 0; m = 1; + } + } + if (m != 1) + *p++ = b; + } + + *widthPtr = im->xsize; + *heightPtr = im->ysize; + *hotXPtr = -1; + *hotYPtr = -1; + + return data; +} + +/* ==================================================================== */ + +------------------------------------------------------------------------ + +3. Recompile Tk and relink the _tkinter module (where necessary). + +==================================================================== +Last updated: 97-05-17/fl diff --git a/Tk/tkImaging.c b/Tk/tkImaging.c new file mode 100644 index 000000000..5963ee24c --- /dev/null +++ b/Tk/tkImaging.c @@ -0,0 +1,265 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * TK interface for Python Imaging objects + * + * Copies (parts of) a named display memory to a photo image object. + * Also contains code to create an display memory. Under Tk, a + * display memory is simply an "L" or "RGB" image memory that is + * allocated in a single block. + * + * To use this module, import the _imagingtk module (ImageTk does + * this for you). + * + * If you're using Python in an embedded context, you can add the + * following lines to your Tcl_AppInit function (in tkappinit.c) + * instead. Put them after the calls to Tcl_Init and Tk_Init: + * + * { + * extern void TkImaging_Init(Tcl_Interp* interp); + * TkImaging_Init(interp); + * } + * + * This registers a Tcl command called "PyImagingPhoto", which is used + * to communicate between PIL and Tk's PhotoImage handler. + * + * Compile and link tkImaging.c with tkappinit.c and _tkinter (see the + * Setup file for details on how to use tkappinit.c). Note that + * _tkinter.c must be compiled with WITH_APPINIT. + * + * History: + * 1995-09-12 fl Created + * 1996-04-08 fl Ready for release + * 1997-05-09 fl Use command instead of image type + * 2001-03-18 fl Initialize alpha layer pointer (struct changed in 8.3) + * 2003-04-23 fl Fixed building for Tk 8.4.1 and later (Jack Jansen) + * 2004-06-24 fl Fixed building for Tk 8.4.6 and later. + * + * Copyright (c) 1997-2004 by Secret Labs AB + * Copyright (c) 1995-2004 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + +#define TK (TK_MAJOR_VERSION*10 + TK_MINOR_VERSION) + +/* This is needed for (at least) Tk 8.4.6 and later, to avoid warnings + for the Tcl_CreateCommand command. */ +#define USE_COMPAT_CONST + +#include "tk.h" + +#include "Imaging.h" + +#include + + +static Imaging +ImagingFind(const char* name) +{ + long id; + + /* FIXME: use CObject instead? */ + id = atol(name); + if (!id) + return NULL; + + return (Imaging) id; +} + + +static int +PyImagingPhotoPut(ClientData clientdata, Tcl_Interp* interp, + int argc, char **argv) +{ + Imaging im; + Tk_PhotoHandle photo; + Tk_PhotoImageBlock block; + + if (argc != 3) { + Tcl_AppendResult(interp, "usage: ", argv[0], + " destPhoto srcImage", (char *) NULL); + return TCL_ERROR; + } + + /* get Tcl PhotoImage handle */ + photo = Tk_FindPhoto(interp, argv[1]); + if (photo == NULL) { + Tcl_AppendResult( + interp, "destination photo must exist", (char *) NULL + ); + return TCL_ERROR; + } + + /* get PIL Image handle */ + im = ImagingFind(argv[2]); + if (!im) { + Tcl_AppendResult(interp, "bad name", (char*) NULL); + return TCL_ERROR; + } + if (!im->block) { + Tcl_AppendResult(interp, "bad display memory", (char*) NULL); + return TCL_ERROR; + } + + /* Active region */ +#if 0 + if (src_xoffset + xsize > im->xsize) + xsize = im->xsize - src_xoffset; + if (src_yoffset + ysize > im->ysize) + ysize = im->ysize - src_yoffset; + if (xsize < 0 || ysize < 0 + || src_xoffset >= im->xsize + || src_yoffset >= im->ysize) + return TCL_OK; +#endif + + /* Mode */ + + if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) { + block.pixelSize = 1; + block.offset[0] = block.offset[1] = block.offset[2] = 0; + } else if (strncmp(im->mode, "RGB", 3) == 0) { + block.pixelSize = 4; + block.offset[0] = 0; + block.offset[1] = 1; + block.offset[2] = 2; + if (strcmp(im->mode, "RGBA") == 0) + block.offset[3] = 3; /* alpha (or reserved, under 8.2) */ + else + block.offset[3] = 0; /* no alpha */ + } else { + Tcl_AppendResult(interp, "Bad mode", (char*) NULL); + return TCL_ERROR; + } + + block.width = im->xsize; + block.height = im->ysize; + block.pitch = im->linesize; + block.pixelPtr = (unsigned char*) im->block; +#if 0 + block.pixelPtr = (unsigned char*) im->block + + src_yoffset * im->linesize + + src_xoffset * im->pixelsize; +#endif + +#if TK < 84 /* < 8.4.0 */ + if (strcmp(im->mode, "RGBA") == 0) { + /* Copy non-transparent pixels to photo image */ + int x, y; + Tk_PhotoImageBlock run; + + /* Clear current contents */ + Tk_PhotoBlank(photo); + + /* Setup run descriptor */ + run.height = 1; + run.pitch = block.pitch; + run.pixelSize = block.pixelSize; + run.offset[0] = 0; + run.offset[1] = 1; + run.offset[2] = 2; + run.offset[3] = 0; /* no alpha (or reserved, under 8.2) */ + + /* Copy opaque runs to photo image */ + for (y = 0; y < block.height; y++) { + unsigned char* p = block.pixelPtr + y*block.pitch; + unsigned char* s = p; + int w = 0; + for (x = 0; x < block.width; x++) { + if (p[3]) { + /* opaque: add pixel to current run */ + if (w == 0) + s = p; + w = w + 1; + } else if (s) { + /* copy run to photo image */ + if (w > 0) { + run.width = w; + run.pixelPtr = s; + Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1); + } + w = 0; + } + p += block.pixelSize; + } + if (w > 0) { + /* copy final run, if any */ + run.width = w; + run.pixelPtr = s; + Tk_PhotoPutBlock(photo, &run, x-w, y, run.width, 1); + } + } + + } else + + /* Copy opaque block to photo image, and leave the rest to TK */ + Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height); + +#else /* Tk 8.4 and newer */ +#if TK < 85 /* Tk 8.4 */ + Tk_PhotoPutBlock(photo, &block, 0, 0, block.width, block.height, + TK_PHOTO_COMPOSITE_SET); + if (strcmp(im->mode, "RGBA") == 0) + /* Tk workaround: we need apply ToggleComplexAlphaIfNeeded */ + /* (fixed in Tk 8.5a3) */ + Tk_PhotoSetSize(photo, block.width, block.height); +#else /* Tk 8.5 */ + Tk_PhotoPutBlock(interp, photo, &block, 0, 0, block.width, block.height, + TK_PHOTO_COMPOSITE_SET); +#endif +#endif + + return TCL_OK; +} + + +static int +PyImagingPhotoGet(ClientData clientdata, Tcl_Interp* interp, + int argc, char **argv) +{ + Tk_PhotoHandle photo; + Tk_PhotoImageBlock block; + + if (argc != 2) { + Tcl_AppendResult(interp, "usage: ", argv[0], + " srcPhoto", (char *) NULL); + return TCL_ERROR; + } + + /* get Tcl PhotoImage handle */ + photo = Tk_FindPhoto(interp, argv[1]); + if (photo == NULL) { + Tcl_AppendResult( + interp, "source photo must exist", (char *) NULL + ); + return TCL_ERROR; + } + + Tk_PhotoGetImage(photo, &block); + + printf("pixelPtr = %p\n", block.pixelPtr); + printf("width = %d\n", block.width); + printf("height = %d\n", block.height); + printf("pitch = %d\n", block.pitch); + printf("pixelSize = %d\n", block.pixelSize); + printf("offset = %d %d %d %d\n", block.offset[0], block.offset[1], + block.offset[2], block.offset[3]); + + Tcl_AppendResult( + interp, "this function is not yet supported", (char *) NULL + ); + + return TCL_ERROR; +} + + +void +TkImaging_Init(Tcl_Interp* interp) +{ + Tcl_CreateCommand(interp, "PyImagingPhoto", PyImagingPhotoPut, + (ClientData) 0, (Tcl_CmdDeleteProc*) NULL); + Tcl_CreateCommand(interp, "PyImagingPhotoGet", PyImagingPhotoGet, + (ClientData) 0, (Tcl_CmdDeleteProc*) NULL); +} diff --git a/_imaging.c b/_imaging.c new file mode 100644 index 000000000..c21897dec --- /dev/null +++ b/_imaging.c @@ -0,0 +1,3281 @@ +/* + * The Python Imaging Library. + * + * the imaging library bindings + * + * history: + * 1995-09-24 fl Created + * 1996-03-24 fl Ready for first public release (release 0.0) + * 1996-03-25 fl Added fromstring (for Jack's "img" library) + * 1996-03-28 fl Added channel operations + * 1996-03-31 fl Added point operation + * 1996-04-08 fl Added new/new_block/new_array factories + * 1996-04-13 fl Added decoders + * 1996-05-04 fl Added palette hack + * 1996-05-12 fl Compile cleanly as C++ + * 1996-05-19 fl Added matrix conversions, gradient fills + * 1996-05-27 fl Added display_mode + * 1996-07-22 fl Added getbbox, offset + * 1996-07-23 fl Added sequence semantics + * 1996-08-13 fl Added logical operators, point mode + * 1996-08-16 fl Modified paste interface + * 1996-09-06 fl Added putdata methods, use abstract interface + * 1996-11-01 fl Added xbm encoder + * 1996-11-04 fl Added experimental path stuff, draw_lines, etc + * 1996-12-10 fl Added zip decoder, crc32 interface + * 1996-12-14 fl Added modulo arithmetics + * 1996-12-29 fl Added zip encoder + * 1997-01-03 fl Added fli and msp decoders + * 1997-01-04 fl Added experimental sun_rle and tga_rle decoders + * 1997-01-05 fl Added gif encoder, getpalette hack + * 1997-02-23 fl Added histogram mask + * 1997-05-12 fl Minor tweaks to match the IFUNC95 interface + * 1997-05-21 fl Added noise generator, spread effect + * 1997-06-05 fl Added mandelbrot generator + * 1997-08-02 fl Modified putpalette to coerce image mode if necessary + * 1998-01-11 fl Added INT32 support + * 1998-01-22 fl Fixed draw_points to draw the last point too + * 1998-06-28 fl Added getpixel, getink, draw_ink + * 1998-07-12 fl Added getextrema + * 1998-07-17 fl Added point conversion to arbitrary formats + * 1998-09-21 fl Added support for resampling filters + * 1998-09-22 fl Added support for quad transform + * 1998-12-29 fl Added support for arcs, chords, and pieslices + * 1999-01-10 fl Added some experimental arrow graphics stuff + * 1999-02-06 fl Added draw_bitmap, font acceleration stuff + * 2001-04-17 fl Fixed some egcs compiler nits + * 2001-09-17 fl Added screen grab primitives (win32) + * 2002-03-09 fl Added stretch primitive + * 2002-03-10 fl Fixed filter handling in rotate + * 2002-06-06 fl Added I, F, and RGB support to putdata + * 2002-06-08 fl Added rankfilter + * 2002-06-09 fl Added support for user-defined filter kernels + * 2002-11-19 fl Added clipboard grab primitives (win32) + * 2002-12-11 fl Added draw context + * 2003-04-26 fl Tweaks for Python 2.3 beta 1 + * 2003-05-21 fl Added createwindow primitive (win32) + * 2003-09-13 fl Added thread section hooks + * 2003-09-15 fl Added expand helper + * 2003-09-26 fl Added experimental LA support + * 2004-02-21 fl Handle zero-size images in quantize + * 2004-06-05 fl Added ptr attribute (used to access Imaging objects) + * 2004-06-05 fl Don't crash when fetching pixels from zero-wide images + * 2004-09-17 fl Added getcolors + * 2004-10-04 fl Added modefilter + * 2005-10-02 fl Added access proxy + * 2006-06-18 fl Always draw last point in polyline + * + * Copyright (c) 1997-2006 by Secret Labs AB + * Copyright (c) 1995-2006 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + + +#include "Python.h" + +#include "Imaging.h" + + +/* Configuration stuff. Feel free to undef things you don't need. */ +#define WITH_IMAGECHOPS /* ImageChops support */ +#define WITH_IMAGEDRAW /* ImageDraw support */ +#define WITH_MAPPING /* use memory mapping to read some file formats */ +#define WITH_IMAGEPATH /* ImagePath stuff */ +#define WITH_ARROW /* arrow graphics stuff (experimental) */ +#define WITH_EFFECTS /* special effects */ +#define WITH_QUANTIZE /* quantization support */ +#define WITH_RANKFILTER /* rank filter */ +#define WITH_MODEFILTER /* mode filter */ +#define WITH_THREADING /* "friendly" threading support */ +#define WITH_UNSHARPMASK /* Kevin Cazabon's unsharpmask module */ + +#define WITH_DEBUG /* extra debugging interfaces */ + +/* PIL Plus extensions */ +#undef WITH_CRACKCODE /* pil plus */ + +#undef VERBOSE + +#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) + +#define B16(p, i) ((((int)p[(i)]) << 8) + p[(i)+1]) +#define L16(p, i) ((((int)p[(i)+1]) << 8) + p[(i)]) +#define S16(v) ((v) < 32768 ? (v) : ((v) - 65536)) + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#if PY_VERSION_HEX < 0x02050000 +#define Py_ssize_t int +#define ssizeargfunc intargfunc +#define ssizessizeargfunc intintargfunc +#define ssizeobjargproc intobjargproc +#define ssizessizeobjargproc intintobjargproc +#endif + +/* -------------------------------------------------------------------- */ +/* OBJECT ADMINISTRATION */ +/* -------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + Imaging image; + ImagingAccess access; +} ImagingObject; + +staticforward PyTypeObject Imaging_Type; + +#ifdef WITH_IMAGEDRAW + +typedef struct +{ + /* to write a character, cut out sxy from glyph data, place + at current position plus dxy, and advance by (dx, dy) */ + int dx, dy; + int dx0, dy0, dx1, dy1; + int sx0, sy0, sx1, sy1; +} Glyph; + +typedef struct { + PyObject_HEAD + ImagingObject* ref; + Imaging bitmap; + int ysize; + int baseline; + Glyph glyphs[256]; +} ImagingFontObject; + +staticforward PyTypeObject ImagingFont_Type; + +typedef struct { + PyObject_HEAD + ImagingObject* image; + UINT8 ink[4]; + int blend; +} ImagingDrawObject; + +staticforward PyTypeObject ImagingDraw_Type; + +#endif + +typedef struct { + PyObject_HEAD + ImagingObject* image; + int readonly; +} PixelAccessObject; + +staticforward PyTypeObject PixelAccess_Type; + +PyObject* +PyImagingNew(Imaging imOut) +{ + ImagingObject* imagep; + + if (!imOut) + return NULL; + + imagep = PyObject_New(ImagingObject, &Imaging_Type); + if (imagep == NULL) { + ImagingDelete(imOut); + return NULL; + } + +#ifdef VERBOSE + printf("imaging %p allocated\n", imagep); +#endif + + imagep->image = imOut; + imagep->access = ImagingAccessNew(imOut); + + return (PyObject*) imagep; +} + +static void +_dealloc(ImagingObject* imagep) +{ + +#ifdef VERBOSE + printf("imaging %p deleted\n", imagep); +#endif + + if (imagep->access) + ImagingAccessDelete(imagep->image, imagep->access); + ImagingDelete(imagep->image); + PyObject_Del(imagep); +} + +#define PyImaging_Check(op) ((op)->ob_type == &Imaging_Type) + +Imaging PyImaging_AsImaging(PyObject *op) +{ + if (!PyImaging_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + + return ((ImagingObject *)op)->image; +} + + +/* -------------------------------------------------------------------- */ +/* THREAD HANDLING */ +/* -------------------------------------------------------------------- */ + +void ImagingSectionEnter(ImagingSectionCookie* cookie) +{ +#ifdef WITH_THREADING + *cookie = (PyThreadState *) PyEval_SaveThread(); +#endif +} + +void ImagingSectionLeave(ImagingSectionCookie* cookie) +{ +#ifdef WITH_THREADING + PyEval_RestoreThread((PyThreadState*) *cookie); +#endif +} + +/* -------------------------------------------------------------------- */ +/* BUFFER HANDLING */ +/* -------------------------------------------------------------------- */ +/* Python compatibility API */ + +#if PY_VERSION_HEX < 0x02020000 + +int PyImaging_CheckBuffer(PyObject *buffer) +{ + PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; + if (procs && procs->bf_getreadbuffer && procs->bf_getsegcount && + procs->bf_getsegcount(buffer, NULL) == 1) + return 1; + return 0; +} + +int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +{ + PyBufferProcs *procs = buffer->ob_type->tp_as_buffer; + return procs->bf_getreadbuffer(buffer, 0, ptr); +} + +#else + +int PyImaging_CheckBuffer(PyObject* buffer) +{ + return PyObject_CheckReadBuffer(buffer); +} + +int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr) +{ + /* must call check_buffer first! */ +#if PY_VERSION_HEX < 0x02050000 + int n = 0; +#else + Py_ssize_t n = 0; +#endif + PyObject_AsReadBuffer(buffer, ptr, &n); + return (int) n; +} + +#endif + +/* -------------------------------------------------------------------- */ +/* EXCEPTION REROUTING */ +/* -------------------------------------------------------------------- */ + +/* error messages */ +static const char* must_be_sequence = "argument must be a sequence"; +static const char* wrong_mode = "unrecognized image mode"; +static const char* wrong_raw_mode = "unrecognized raw mode"; +static const char* outside_image = "image index out of range"; +static const char* outside_palette = "palette index out of range"; +static const char* no_palette = "image has no palette"; +static const char* readonly = "image is readonly"; +/* static const char* no_content = "image has no content"; */ + +void * +ImagingError_IOError(void) +{ + PyErr_SetString(PyExc_IOError, "error when accessing file"); + return NULL; +} + +void * +ImagingError_MemoryError(void) +{ + return PyErr_NoMemory(); +} + +void * +ImagingError_Mismatch(void) +{ + PyErr_SetString(PyExc_ValueError, "images do not match"); + return NULL; +} + +void * +ImagingError_ModeError(void) +{ + PyErr_SetString(PyExc_ValueError, "image has wrong mode"); + return NULL; +} + +void * +ImagingError_ValueError(const char *message) +{ + PyErr_SetString( + PyExc_ValueError, + (message) ? (char*) message : "unrecognized argument value" + ); + return NULL; +} + +void +ImagingError_Clear(void) +{ + PyErr_Clear(); +} + +/* -------------------------------------------------------------------- */ +/* HELPERS */ +/* -------------------------------------------------------------------- */ + +static int +getbands(const char* mode) +{ + Imaging im; + int bands; + + /* FIXME: add primitive to libImaging to avoid extra allocation */ + im = ImagingNew(mode, 0, 0); + if (!im) + return -1; + + bands = im->bands; + + ImagingDelete(im); + + return bands; +} + +#define TYPE_UINT8 (0x100|sizeof(UINT8)) +#define TYPE_INT32 (0x200|sizeof(INT32)) +#define TYPE_FLOAT32 (0x300|sizeof(FLOAT32)) +#define TYPE_DOUBLE (0x400|sizeof(double)) + +static void* +getlist(PyObject* arg, int* length, const char* wrong_length, int type) +{ + int i, n; + void* list; + + if (!PySequence_Check(arg)) { + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; + } + + n = PyObject_Length(arg); + if (length && wrong_length && n != *length) { + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; + } + + list = malloc(n * (type & 0xff)); + if (!list) + return PyErr_NoMemory(); + + switch (type) { + case TYPE_UINT8: + if (PyList_Check(arg)) { + for (i = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(arg, i); + int temp = PyInt_AsLong(op); + ((UINT8*)list)[i] = CLIP(temp); + } + } else { + for (i = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(arg, i); + int temp = PyInt_AsLong(op); + Py_XDECREF(op); + ((UINT8*)list)[i] = CLIP(temp); + } + } + break; + case TYPE_INT32: + if (PyList_Check(arg)) { + for (i = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(arg, i); + int temp = PyInt_AsLong(op); + ((INT32*)list)[i] = temp; + } + } else { + for (i = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(arg, i); + int temp = PyInt_AsLong(op); + Py_XDECREF(op); + ((INT32*)list)[i] = temp; + } + } + break; + case TYPE_FLOAT32: + if (PyList_Check(arg)) { + for (i = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(arg, i); + double temp = PyFloat_AsDouble(op); + ((FLOAT32*)list)[i] = (FLOAT32) temp; + } + } else { + for (i = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(arg, i); + double temp = PyFloat_AsDouble(op); + Py_XDECREF(op); + ((FLOAT32*)list)[i] = (FLOAT32) temp; + } + } + break; + case TYPE_DOUBLE: + if (PyList_Check(arg)) { + for (i = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(arg, i); + double temp = PyFloat_AsDouble(op); + ((double*)list)[i] = temp; + } + } else { + for (i = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(arg, i); + double temp = PyFloat_AsDouble(op); + Py_XDECREF(op); + ((double*)list)[i] = temp; + } + } + break; + } + + if (length) + *length = n; + + PyErr_Clear(); + + return list; +} + +static inline PyObject* +getpixel(Imaging im, ImagingAccess access, int x, int y) +{ + union { + UINT8 b[4]; + INT16 h; + INT32 i; + FLOAT32 f; + } pixel; + + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; + } + + access->get_pixel(im, x, y, &pixel); + + switch (im->type) { + case IMAGING_TYPE_UINT8: + switch (im->bands) { + case 1: + return PyInt_FromLong(pixel.b[0]); + case 2: + return Py_BuildValue("ii", pixel.b[0], pixel.b[1]); + case 3: + return Py_BuildValue("iii", pixel.b[0], pixel.b[1], pixel.b[2]); + case 4: + return Py_BuildValue("iiii", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]); + } + break; + case IMAGING_TYPE_INT32: + return PyInt_FromLong(pixel.i); + case IMAGING_TYPE_FLOAT32: + return PyFloat_FromDouble(pixel.f); + case IMAGING_TYPE_SPECIAL: + if (strncmp(im->mode, "I;16", 4) == 0) + return PyInt_FromLong(pixel.h); + break; + } + + /* unknown type */ + Py_INCREF(Py_None); + return Py_None; +} + +static char* +getink(PyObject* color, Imaging im, char* ink) +{ + int r, g, b, a; + double f; + + /* fill ink buffer (four bytes) with something that can + be cast to either UINT8 or INT32 */ + + switch (im->type) { + case IMAGING_TYPE_UINT8: + /* unsigned integer */ + if (im->bands == 1) { + /* unsigned integer, single layer */ + r = PyInt_AsLong(color); + if (r == -1 && PyErr_Occurred()) + return NULL; + ink[0] = CLIP(r); + ink[1] = ink[2] = ink[3] = 0; + } else { + a = 255; + if (PyInt_Check(color)) { + r = PyInt_AS_LONG(color); + /* compatibility: ABGR */ + a = (UINT8) (r >> 24); + b = (UINT8) (r >> 16); + g = (UINT8) (r >> 8); + r = (UINT8) r; + } else { + if (im->bands == 2) { + if (!PyArg_ParseTuple(color, "i|i", &r, &a)) + return NULL; + g = b = r; + } else { + if (!PyArg_ParseTuple(color, "iii|i", &r, &g, &b, &a)) + return NULL; + } + } + ink[0] = CLIP(r); + ink[1] = CLIP(g); + ink[2] = CLIP(b); + ink[3] = CLIP(a); + } + return ink; + case IMAGING_TYPE_INT32: + /* signed integer */ + r = PyInt_AsLong(color); + if (r == -1 && PyErr_Occurred()) + return NULL; + *(INT32*) ink = r; + return ink; + case IMAGING_TYPE_FLOAT32: + /* floating point */ + f = PyFloat_AsDouble(color); + if (f == -1.0 && PyErr_Occurred()) + return NULL; + *(FLOAT32*) ink = (FLOAT32) f; + return ink; + case IMAGING_TYPE_SPECIAL: + if (strncmp(im->mode, "I;16", 4) == 0) { + r = PyInt_AsLong(color); + if (r == -1 && PyErr_Occurred()) + return NULL; + ink[0] = (UINT8) r; + ink[1] = (UINT8) (r >> 8); + ink[2] = ink[3] = 0; + return ink; + } + } + + PyErr_SetString(PyExc_ValueError, wrong_mode); + return NULL; +} + +/* -------------------------------------------------------------------- */ +/* FACTORIES */ +/* -------------------------------------------------------------------- */ + +static PyObject* +_fill(PyObject* self, PyObject* args) +{ + char* mode; + int xsize, ysize; + PyObject* color; + char buffer[4]; + Imaging im; + + xsize = ysize = 256; + color = NULL; + + if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) + return NULL; + + im = ImagingNew(mode, xsize, ysize); + if (!im) + return NULL; + + if (color) { + if (!getink(color, im, buffer)) { + ImagingDelete(im); + return NULL; + } + } else + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; + + (void) ImagingFill(im, buffer); + + return PyImagingNew(im); +} + +static PyObject* +_new(PyObject* self, PyObject* args) +{ + char* mode; + int xsize, ysize; + + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + return NULL; + + return PyImagingNew(ImagingNew(mode, xsize, ysize)); +} + +static PyObject* +_new_array(PyObject* self, PyObject* args) +{ + char* mode; + int xsize, ysize; + + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + return NULL; + + return PyImagingNew(ImagingNewArray(mode, xsize, ysize)); +} + +static PyObject* +_new_block(PyObject* self, PyObject* args) +{ + char* mode; + int xsize, ysize; + + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + return NULL; + + return PyImagingNew(ImagingNewBlock(mode, xsize, ysize)); +} + +static PyObject* +_getcount(PyObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, ":getcount")) + return NULL; + + return PyInt_FromLong(ImagingNewCount); +} + +static PyObject* +_linear_gradient(PyObject* self, PyObject* args) +{ + char* mode; + + if (!PyArg_ParseTuple(args, "s", &mode)) + return NULL; + + return PyImagingNew(ImagingFillLinearGradient(mode)); +} + +static PyObject* +_radial_gradient(PyObject* self, PyObject* args) +{ + char* mode; + + if (!PyArg_ParseTuple(args, "s", &mode)) + return NULL; + + return PyImagingNew(ImagingFillRadialGradient(mode)); +} + +static PyObject* +_open_ppm(PyObject* self, PyObject* args) +{ + char* filename; + + if (!PyArg_ParseTuple(args, "s", &filename)) + return NULL; + + return PyImagingNew(ImagingOpenPPM(filename)); +} + +static PyObject* +_blend(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep1; + ImagingObject* imagep2; + double alpha; + + alpha = 0.5; + if (!PyArg_ParseTuple(args, "O!O!|d", + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2, + &alpha)) + return NULL; + + return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, + (float) alpha)); +} + +/* -------------------------------------------------------------------- */ +/* METHODS */ +/* -------------------------------------------------------------------- */ + +static PyObject* +_convert(ImagingObject* self, PyObject* args) +{ + char* mode; + int dither = 0; + ImagingObject *paletteimage = NULL; + + if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) + return NULL; + if (paletteimage != NULL) { + if (!PyImaging_Check(paletteimage)) { + PyObject_Print((PyObject *)paletteimage, stderr, 0); + PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'"); + return NULL; + } + if (paletteimage->image->palette == NULL) { + PyErr_SetString(PyExc_ValueError, "null palette"); + return NULL; + } + } + + return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither)); +} + +static PyObject* +_convert2(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep1; + ImagingObject* imagep2; + if (!PyArg_ParseTuple(args, "O!O!", + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; + + if (!ImagingConvert2(imagep1->image, imagep2->image)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_convert_matrix(ImagingObject* self, PyObject* args) +{ + char* mode; + float m[12]; + if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m+0, m+1, m+2, m+3)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, + m+0, m+1, m+2, m+3, + m+4, m+5, m+6, m+7, + m+8, m+9, m+10, m+11)) + return NULL; + } + + return PyImagingNew(ImagingConvertMatrix(self->image, mode, m)); +} + +static PyObject* +_copy(ImagingObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + + return PyImagingNew(ImagingCopy(self->image)); +} + +static PyObject* +_copy2(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep1; + ImagingObject* imagep2; + if (!PyArg_ParseTuple(args, "O!O!", + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; + + if (!ImagingCopy2(imagep1->image, imagep2->image)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_crop(ImagingObject* self, PyObject* args) +{ + int x0, y0, x1, y1; + if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) + return NULL; + + return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1)); +} + +static PyObject* +_expand(ImagingObject* self, PyObject* args) +{ + int x, y; + int mode = 0; + if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode)) + return NULL; + + return PyImagingNew(ImagingExpand(self->image, x, y, mode)); +} + +static PyObject* +_filter(ImagingObject* self, PyObject* args) +{ + PyObject* imOut; + int kernelsize; + FLOAT32* kerneldata; + + int xsize, ysize; + float divisor, offset; + PyObject* kernel = NULL; + if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize, + &divisor, &offset, &kernel)) + return NULL; + + /* get user-defined kernel */ + kerneldata = getlist(kernel, &kernelsize, NULL, TYPE_FLOAT32); + if (!kerneldata) + return NULL; + if (kernelsize != xsize * ysize) { + free(kerneldata); + return ImagingError_ValueError("bad kernel size"); + } + + imOut = PyImagingNew( + ImagingFilter(self->image, xsize, ysize, kerneldata, offset, divisor) + ); + + free(kerneldata); + + return imOut; +} + +#ifdef WITH_UNSHARPMASK +static PyObject* +_gaussian_blur(ImagingObject* self, PyObject* args) +{ + Imaging imIn; + Imaging imOut; + + float radius = 0; + if (!PyArg_ParseTuple(args, "f", &radius)) + return NULL; + + imIn = self->image; + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + + if (!ImagingGaussianBlur(imIn, imOut, radius)) + return NULL; + + return PyImagingNew(imOut); +} +#endif + +static PyObject* +_getpalette(ImagingObject* self, PyObject* args) +{ + PyObject* palette; + int palettesize = 256; + int bits; + ImagingShuffler pack; + + char* mode = "RGB"; + char* rawmode = "RGB"; + if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) + return NULL; + + if (!self->image->palette) { + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; + } + + pack = ImagingFindPacker(mode, rawmode, &bits); + if (!pack) { + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; + } + + palette = PyString_FromStringAndSize(NULL, palettesize * bits / 8); + if (!palette) + return NULL; + + pack((UINT8*) PyString_AsString(palette), + self->image->palette->palette, palettesize); + + return palette; +} + +static inline int +_getxy(PyObject* xy, int* x, int *y) +{ + PyObject* value; + + if (!PyTuple_Check(xy) || PyTuple_GET_SIZE(xy) != 2) + goto badarg; + + value = PyTuple_GET_ITEM(xy, 0); + if (PyInt_Check(value)) + *x = PyInt_AS_LONG(value); + else if (PyFloat_Check(value)) + *x = (int) PyFloat_AS_DOUBLE(value); + else + goto badval; + + value = PyTuple_GET_ITEM(xy, 1); + if (PyInt_Check(value)) + *y = PyInt_AS_LONG(value); + else if (PyFloat_Check(value)) + *y = (int) PyFloat_AS_DOUBLE(value); + else + goto badval; + + return 0; + + badarg: + PyErr_SetString( + PyExc_TypeError, + "argument must be sequence of length 2" + ); + return -1; + + badval: + PyErr_SetString( + PyExc_TypeError, + "an integer is required" + ); + return -1; +} + +static PyObject* +_getpixel(ImagingObject* self, PyObject* args) +{ + PyObject* xy; + int x, y; + + if (PyTuple_GET_SIZE(args) != 1) { + PyErr_SetString( + PyExc_TypeError, + "argument 1 must be sequence of length 2" + ); + return NULL; + } + + xy = PyTuple_GET_ITEM(args, 0); + + if (_getxy(xy, &x, &y)) + return NULL; + + if (self->access == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + return getpixel(self->image, self->access, x, y); +} + +static PyObject* +_histogram(ImagingObject* self, PyObject* args) +{ + ImagingHistogram h; + PyObject* list; + int i; + union { + UINT8 u[2]; + INT32 i[2]; + FLOAT32 f[2]; + } extrema; + void* ep; + int i0, i1; + double f0, f1; + + PyObject* extremap = NULL; + ImagingObject* maskp = NULL; + if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp)) + return NULL; + + if (extremap) { + ep = &extrema; + switch (self->image->type) { + case IMAGING_TYPE_UINT8: + if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) + return NULL; + /* FIXME: clip */ + extrema.u[0] = i0; + extrema.u[1] = i1; + break; + case IMAGING_TYPE_INT32: + if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1)) + return NULL; + extrema.i[0] = i0; + extrema.i[1] = i1; + break; + case IMAGING_TYPE_FLOAT32: + if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1)) + return NULL; + extrema.f[0] = (FLOAT32) f0; + extrema.f[1] = (FLOAT32) f1; + break; + default: + ep = NULL; + break; + } + } else + ep = NULL; + + h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep); + + if (!h) + return NULL; + + /* Build an integer list containing the histogram */ + list = PyList_New(h->bands * 256); + for (i = 0; i < h->bands * 256; i++) { + PyObject* item; + item = PyInt_FromLong(h->histogram[i]); + if (item == NULL) { + Py_DECREF(list); + list = NULL; + break; + } + PyList_SetItem(list, i, item); + } + + ImagingHistogramDelete(h); + + return list; +} + +#ifdef WITH_MODEFILTER +static PyObject* +_modefilter(ImagingObject* self, PyObject* args) +{ + int size; + if (!PyArg_ParseTuple(args, "i", &size)) + return NULL; + + return PyImagingNew(ImagingModeFilter(self->image, size)); +} +#endif + +static PyObject* +_offset(ImagingObject* self, PyObject* args) +{ + int xoffset, yoffset; + if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) + return NULL; + + return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset)); +} + +static PyObject* +_paste(ImagingObject* self, PyObject* args) +{ + int status; + char ink[4]; + + PyObject* source; + int x0, y0, x1, y1; + ImagingObject* maskp = NULL; + if (!PyArg_ParseTuple(args, "O(iiii)|O!", + &source, + &x0, &y0, &x1, &y1, + &Imaging_Type, &maskp)) + return NULL; + + if (PyImaging_Check(source)) + status = ImagingPaste( + self->image, PyImaging_AsImaging(source), + (maskp) ? maskp->image : NULL, + x0, y0, x1, y1 + ); + + else { + if (!getink(source, self->image, ink)) + return NULL; + status = ImagingFill2( + self->image, ink, + (maskp) ? maskp->image : NULL, + x0, y0, x1, y1 + ); + } + + if (status < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_point(ImagingObject* self, PyObject* args) +{ + static const char* wrong_number = "wrong number of lut entries"; + + int n, i; + int bands; + Imaging im; + + PyObject* list; + char* mode; + if (!PyArg_ParseTuple(args, "Oz", &list, &mode)) + return NULL; + + if (mode && !strcmp(mode, "F")) { + FLOAT32* data; + + /* map from 8-bit data to floating point */ + n = 256; + data = getlist(list, &n, wrong_number, TYPE_FLOAT32); + if (!data) + return NULL; + im = ImagingPoint(self->image, mode, (void*) data); + free(data); + + } else if (!strcmp(self->image->mode, "I") && mode && !strcmp(mode, "L")) { + UINT8* data; + + /* map from 16-bit subset of 32-bit data to 8-bit */ + /* FIXME: support arbitrary number of entries (requires API change) */ + n = 65536; + data = getlist(list, &n, wrong_number, TYPE_UINT8); + if (!data) + return NULL; + im = ImagingPoint(self->image, mode, (void*) data); + free(data); + + } else { + INT32* data; + UINT8 lut[1024]; + + if (mode) { + bands = getbands(mode); + if (bands < 0) + return NULL; + } else + bands = self->image->bands; + + /* map to integer data */ + n = 256 * bands; + data = getlist(list, &n, wrong_number, TYPE_INT32); + if (!data) + return NULL; + + if (mode && !strcmp(mode, "I")) + im = ImagingPoint(self->image, mode, (void*) data); + else if (mode && bands > 1) { + for (i = 0; i < 256; i++) { + lut[i*4] = CLIP(data[i]); + lut[i*4+1] = CLIP(data[i+256]); + lut[i*4+2] = CLIP(data[i+512]); + if (n > 768) + lut[i*4+3] = CLIP(data[i+768]); + } + im = ImagingPoint(self->image, mode, (void*) lut); + } else { + /* map individual bands */ + for (i = 0; i < n; i++) + lut[i] = CLIP(data[i]); + im = ImagingPoint(self->image, mode, (void*) lut); + } + free(data); + } + + return PyImagingNew(im); +} + +static PyObject* +_point_transform(ImagingObject* self, PyObject* args) +{ + double scale = 1.0; + double offset = 0.0; + if (!PyArg_ParseTuple(args, "|dd", &scale, &offset)) + return NULL; + + return PyImagingNew(ImagingPointTransform(self->image, scale, offset)); +} + +static PyObject* +_putdata(ImagingObject* self, PyObject* args) +{ + Imaging image; + int n, i, x, y; + + PyObject* data; + double scale = 1.0; + double offset = 0.0; + if (!PyArg_ParseTuple(args, "O|dd", &data, &scale, &offset)) + return NULL; + + if (!PySequence_Check(data)) { + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; + } + + image = self->image; + + n = PyObject_Length(data); + if (n > (int) (image->xsize * image->ysize)) { + PyErr_SetString(PyExc_TypeError, "too many data entries"); + return NULL; + } + + if (image->image8) { + if (PyString_Check(data)) { + unsigned char* p; + p = (unsigned char*) PyString_AS_STRING((PyStringObject*) data); + if (scale == 1.0 && offset == 0.0) + /* Plain string data */ + for (i = y = 0; i < n; i += image->xsize, y++) { + x = n - i; + if (x > (int) image->xsize) + x = image->xsize; + memcpy(image->image8[y], p+i, x); + } + else + /* Scaled and clipped string data */ + for (i = x = y = 0; i < n; i++) { + image->image8[y][x] = CLIP((int) (p[i] * scale + offset)); + if (++x >= (int) image->xsize) + x = 0, y++; + } + } else { + if (scale == 1.0 && offset == 0.0) { + /* Clipped data */ + if (PyList_Check(data)) { + for (i = x = y = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(data, i); + image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); + if (++x >= (int) image->xsize) + x = 0, y++; + } + } else { + for (i = x = y = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(data, i); + image->image8[y][x] = (UINT8) CLIP(PyInt_AsLong(op)); + Py_XDECREF(op); + if (++x >= (int) image->xsize) + x = 0, y++; + } + } + } else { + if (PyList_Check(data)) { + /* Scaled and clipped data */ + for (i = x = y = 0; i < n; i++) { + PyObject *op = PyList_GET_ITEM(data, i); + image->image8[y][x] = CLIP( + (int) (PyFloat_AsDouble(op) * scale + offset)); + if (++x >= (int) image->xsize) + x = 0, y++; + } + } else { + for (i = x = y = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(data, i); + image->image8[y][x] = CLIP( + (int) (PyFloat_AsDouble(op) * scale + offset)); + Py_XDECREF(op); + if (++x >= (int) image->xsize) + x = 0, y++; + } + } + } + PyErr_Clear(); /* Avoid weird exceptions */ + } + } else { + /* 32-bit images */ + switch (image->type) { + case IMAGING_TYPE_INT32: + for (i = x = y = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(data, i); + IMAGING_PIXEL_INT32(image, x, y) = + (INT32) (PyFloat_AsDouble(op) * scale + offset); + Py_XDECREF(op); + if (++x >= (int) image->xsize) + x = 0, y++; + } + PyErr_Clear(); /* Avoid weird exceptions */ + break; + case IMAGING_TYPE_FLOAT32: + for (i = x = y = 0; i < n; i++) { + PyObject *op = PySequence_GetItem(data, i); + IMAGING_PIXEL_FLOAT32(image, x, y) = + (FLOAT32) (PyFloat_AsDouble(op) * scale + offset); + Py_XDECREF(op); + if (++x >= (int) image->xsize) + x = 0, y++; + } + PyErr_Clear(); /* Avoid weird exceptions */ + break; + default: + for (i = x = y = 0; i < n; i++) { + char ink[4]; + PyObject *op = PySequence_GetItem(data, i); + if (!op || !getink(op, image, ink)) { + Py_DECREF(op); + return NULL; + } + /* FIXME: what about scale and offset? */ + image->image32[y][x] = *((INT32*) ink); + Py_XDECREF(op); + if (++x >= (int) image->xsize) + x = 0, y++; + } + PyErr_Clear(); /* Avoid weird exceptions */ + break; + } + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WITH_QUANTIZE + +#include "Quant.h" +static PyObject* +_quantize(ImagingObject* self, PyObject* args) +{ + int colours = 256; + int method = 0; + int kmeans = 0; + if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) + return NULL; + + if (!self->image->xsize || !self->image->ysize) { + /* no content; return an empty image */ + return PyImagingNew( + ImagingNew("P", self->image->xsize, self->image->ysize) + ); + } + + return PyImagingNew(ImagingQuantize(self->image, colours, method, kmeans)); +} +#endif + +static PyObject* +_putpalette(ImagingObject* self, PyObject* args) +{ + ImagingShuffler unpack; + int bits; + + char* rawmode; + UINT8* palette; + int palettesize; + if (!PyArg_ParseTuple(args, "ss#", &rawmode, &palette, &palettesize)) + return NULL; + + if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) { + PyErr_SetString(PyExc_ValueError, wrong_mode); + return NULL; + } + + unpack = ImagingFindUnpacker("RGB", rawmode, &bits); + if (!unpack) { + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; + } + + ImagingPaletteDelete(self->image->palette); + + strcpy(self->image->mode, "P"); + + self->image->palette = ImagingPaletteNew("RGB"); + + unpack(self->image->palette->palette, palette, palettesize * 8 / bits); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_putpalettealpha(ImagingObject* self, PyObject* args) +{ + int index; + int alpha = 0; + if (!PyArg_ParseTuple(args, "i|i", &index, &alpha)) + return NULL; + + if (!self->image->palette) { + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; + } + + if (index < 0 || index >= 256) { + PyErr_SetString(PyExc_ValueError, outside_palette); + return NULL; + } + + strcpy(self->image->palette->mode, "RGBA"); + self->image->palette->palette[index*4+3] = (UINT8) alpha; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_putpixel(ImagingObject* self, PyObject* args) +{ + Imaging im; + char ink[4]; + + int x, y; + PyObject* color; + if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) + return NULL; + + im = self->image; + + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; + } + + if (!getink(color, im, ink)) + return NULL; + + if (self->access) + self->access->put_pixel(im, x, y, ink); + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WITH_RANKFILTER +static PyObject* +_rankfilter(ImagingObject* self, PyObject* args) +{ + int size, rank; + if (!PyArg_ParseTuple(args, "ii", &size, &rank)) + return NULL; + + return PyImagingNew(ImagingRankFilter(self->image, size, rank)); +} +#endif + +static PyObject* +_resize(ImagingObject* self, PyObject* args) +{ + Imaging imIn; + Imaging imOut; + + int xsize, ysize; + int filter = IMAGING_TRANSFORM_NEAREST; + if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) + return NULL; + + imIn = self->image; + + imOut = ImagingNew(imIn->mode, xsize, ysize); + if (imOut) + (void) ImagingResize(imOut, imIn, filter); + + return PyImagingNew(imOut); +} + +static PyObject* +_rotate(ImagingObject* self, PyObject* args) +{ + Imaging imOut; + Imaging imIn; + + double theta; + int filter = IMAGING_TRANSFORM_NEAREST; + if (!PyArg_ParseTuple(args, "d|i", &theta, &filter)) + return NULL; + + imIn = self->image; + + theta = fmod(theta, 360.0); + if (theta < 0.0) + theta += 360; + + if (filter && imIn->type != IMAGING_TYPE_SPECIAL) { + /* Rotate with resampling filter */ + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + (void) ImagingRotate(imOut, imIn, theta, filter); + } else if (theta == 90.0 || theta == 270.0) { + /* Use fast version */ + imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); + if (imOut) { + if (theta == 90.0) + (void) ImagingRotate90(imOut, imIn); + else + (void) ImagingRotate270(imOut, imIn); + } + } else { + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + if (imOut) { + if (theta == 0.0) + /* No rotation: simply copy the input image */ + (void) ImagingCopy2(imOut, imIn); + else if (theta == 180.0) + /* Use fast version */ + (void) ImagingRotate180(imOut, imIn); + else + /* Use ordinary version */ + (void) ImagingRotate(imOut, imIn, theta, 0); + } + } + + return PyImagingNew(imOut); +} + +#define IS_RGB(mode)\ + (!strcmp(mode, "RGB") || !strcmp(mode, "RGBA") || !strcmp(mode, "RGBX")) + +static PyObject* +im_setmode(ImagingObject* self, PyObject* args) +{ + /* attempt to modify the mode of an image in place */ + + Imaging im; + + char* mode; + int modelen; + if (!PyArg_ParseTuple(args, "s#:setmode", &mode, &modelen)) + return NULL; + + im = self->image; + + /* move all logic in here to the libImaging primitive */ + + if (!strcmp(im->mode, mode)) { + ; /* same mode; always succeeds */ + } else if (IS_RGB(im->mode) && IS_RGB(mode)) { + /* color to color */ + strcpy(im->mode, mode); + im->bands = modelen; + if (!strcmp(mode, "RGBA")) + (void) ImagingFillBand(im, 3, 255); + } else { + /* trying doing an in-place conversion */ + if (!ImagingConvertInPlace(im, mode)) + return NULL; + } + + if (self->access) + ImagingAccessDelete(im, self->access); + self->access = ImagingAccessNew(im); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_stretch(ImagingObject* self, PyObject* args) +{ + Imaging imIn; + Imaging imTemp; + Imaging imOut; + + int xsize, ysize; + int filter = IMAGING_TRANSFORM_NEAREST; + if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) + return NULL; + + imIn = self->image; + + /* two-pass resize: minimize size of intermediate image */ + if (imIn->xsize * ysize < xsize * imIn->ysize) + imTemp = ImagingNew(imIn->mode, imIn->xsize, ysize); + else + imTemp = ImagingNew(imIn->mode, xsize, imIn->ysize); + if (!imTemp) + return NULL; + + /* first pass */ + if (!ImagingStretch(imTemp, imIn, filter)) { + ImagingDelete(imTemp); + return NULL; + } + + imOut = ImagingNew(imIn->mode, xsize, ysize); + if (!imOut) { + ImagingDelete(imTemp); + return NULL; + } + + /* second pass */ + if (!ImagingStretch(imOut, imTemp, filter)) { + ImagingDelete(imOut); + ImagingDelete(imTemp); + return NULL; + } + + ImagingDelete(imTemp); + + return PyImagingNew(imOut); +} + +static PyObject* +_transform2(ImagingObject* self, PyObject* args) +{ + static const char* wrong_number = "wrong number of matrix entries"; + + Imaging imIn; + Imaging imOut; + int n; + double *a; + + ImagingObject* imagep; + int x0, y0, x1, y1; + int method; + PyObject* data; + int filter = IMAGING_TRANSFORM_NEAREST; + int fill = 1; + if (!PyArg_ParseTuple(args, "(iiii)O!iO|ii", + &x0, &y0, &x1, &y1, + &Imaging_Type, &imagep, + &method, &data, + &filter, &fill)) + return NULL; + + switch (method) { + case IMAGING_TRANSFORM_AFFINE: + n = 6; + break; + case IMAGING_TRANSFORM_PERSPECTIVE: + n = 8; + break; + case IMAGING_TRANSFORM_QUAD: + n = 8; + break; + default: + n = -1; /* force error */ + } + + a = getlist(data, &n, wrong_number, TYPE_DOUBLE); + if (!a) + return NULL; + + imOut = self->image; + imIn = imagep->image; + + /* FIXME: move transform dispatcher into libImaging */ + + switch (method) { + case IMAGING_TRANSFORM_AFFINE: + imOut = ImagingTransformAffine( + imOut, imIn, x0, y0, x1, y1, a, filter, 1 + ); + break; + case IMAGING_TRANSFORM_PERSPECTIVE: + imOut = ImagingTransformPerspective( + imOut, imIn, x0, y0, x1, y1, a, filter, 1 + ); + break; + case IMAGING_TRANSFORM_QUAD: + imOut = ImagingTransformQuad( + imOut, imIn, x0, y0, x1, y1, a, filter, 1 + ); + break; + default: + (void) ImagingError_ValueError("bad transform method"); + } + + free(a); + + if (!imOut) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_transpose(ImagingObject* self, PyObject* args) +{ + Imaging imIn; + Imaging imOut; + + int op; + if (!PyArg_ParseTuple(args, "i", &op)) + return NULL; + + imIn = self->image; + + switch (op) { + case 0: /* flip left right */ + case 1: /* flip top bottom */ + case 3: /* rotate 180 */ + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + break; + case 2: /* rotate 90 */ + case 4: /* rotate 270 */ + imOut = ImagingNew(imIn->mode, imIn->ysize, imIn->xsize); + break; + default: + PyErr_SetString(PyExc_ValueError, "No such transpose operation"); + return NULL; + } + + if (imOut) + switch (op) { + case 0: + (void) ImagingFlipLeftRight(imOut, imIn); + break; + case 1: + (void) ImagingFlipTopBottom(imOut, imIn); + break; + case 2: + (void) ImagingRotate90(imOut, imIn); + break; + case 3: + (void) ImagingRotate180(imOut, imIn); + break; + case 4: + (void) ImagingRotate270(imOut, imIn); + break; + } + + return PyImagingNew(imOut); +} + +#ifdef WITH_UNSHARPMASK +static PyObject* +_unsharp_mask(ImagingObject* self, PyObject* args) +{ + Imaging imIn; + Imaging imOut; + + float radius; + int percent, threshold; + if (!PyArg_ParseTuple(args, "fii", &radius, &percent, &threshold)) + return NULL; + + + imIn = self->image; + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + + if (!ImagingUnsharpMask(imIn, imOut, radius, percent, threshold)) + return NULL; + + return PyImagingNew(imOut); +} +#endif + +/* -------------------------------------------------------------------- */ + +static PyObject* +_isblock(ImagingObject* self, PyObject* args) +{ + return PyInt_FromLong((long) self->image->block); +} + +static PyObject* +_getbbox(ImagingObject* self, PyObject* args) +{ + int bbox[4]; + if (!ImagingGetBBox(self->image, bbox)) { + Py_INCREF(Py_None); + return Py_None; + } + + return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]); +} + +static PyObject* +_getcolors(ImagingObject* self, PyObject* args) +{ + ImagingColorItem* items; + int i, colors; + PyObject* out; + + int maxcolors = 256; + if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) + return NULL; + + items = ImagingGetColors(self->image, maxcolors, &colors); + if (!items) + return NULL; + + if (colors > maxcolors) { + out = Py_None; + Py_INCREF(out); + } else { + out = PyList_New(colors); + for (i = 0; i < colors; i++) { + ImagingColorItem* v = &items[i]; + PyObject* item = Py_BuildValue( + "iN", v->count, getpixel(self->image, self->access, v->x, v->y) + ); + PyList_SetItem(out, i, item); + } + } + + free(items); + + return out; +} + +static PyObject* +_getextrema(ImagingObject* self, PyObject* args) +{ + union { + UINT8 u[2]; + INT32 i[2]; + FLOAT32 f[2]; + } extrema; + int status; + + status = ImagingGetExtrema(self->image, &extrema); + if (status < 0) + return NULL; + + if (status) + switch (self->image->type) { + case IMAGING_TYPE_UINT8: + return Py_BuildValue("ii", extrema.u[0], extrema.u[1]); + case IMAGING_TYPE_INT32: + return Py_BuildValue("ii", extrema.i[0], extrema.i[1]); + case IMAGING_TYPE_FLOAT32: + return Py_BuildValue("dd", extrema.f[0], extrema.f[1]); + } + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_getprojection(ImagingObject* self, PyObject* args) +{ + unsigned char* xprofile; + unsigned char* yprofile; + PyObject* result; + + xprofile = malloc(self->image->xsize); + yprofile = malloc(self->image->ysize); + + if (xprofile == NULL || yprofile == NULL) { + free(xprofile); + free(yprofile); + return PyErr_NoMemory(); + } + + ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); + + result = Py_BuildValue("s#s#", xprofile, self->image->xsize, + yprofile, self->image->ysize); + + free(xprofile); + free(yprofile); + + return result; +} + +/* -------------------------------------------------------------------- */ + +static PyObject* +_getband(ImagingObject* self, PyObject* args) +{ + int band; + + if (!PyArg_ParseTuple(args, "i", &band)) + return NULL; + + return PyImagingNew(ImagingGetBand(self->image, band)); +} + +static PyObject* +_fillband(ImagingObject* self, PyObject* args) +{ + int band; + int color; + + if (!PyArg_ParseTuple(args, "ii", &band, &color)) + return NULL; + + if (!ImagingFillBand(self->image, band, color)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_putband(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + int band; + if (!PyArg_ParseTuple(args, "O!i", + &Imaging_Type, &imagep, + &band)) + return NULL; + + if (!ImagingPutBand(self->image, imagep->image, band)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +/* -------------------------------------------------------------------- */ + +#ifdef WITH_IMAGECHOPS + +static PyObject* +_chop_invert(ImagingObject* self, PyObject* args) +{ + return PyImagingNew(ImagingNegative(self->image)); +} + +static PyObject* +_chop_lighter(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopLighter(self->image, imagep->image)); +} + +static PyObject* +_chop_darker(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopDarker(self->image, imagep->image)); +} + +static PyObject* +_chop_difference(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopDifference(self->image, imagep->image)); +} + +static PyObject* +_chop_multiply(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopMultiply(self->image, imagep->image)); +} + +static PyObject* +_chop_screen(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopScreen(self->image, imagep->image)); +} + +static PyObject* +_chop_add(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + float scale; + int offset; + + scale = 1.0; + offset = 0; + + if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, + &scale, &offset)) + return NULL; + + return PyImagingNew(ImagingChopAdd(self->image, imagep->image, + scale, offset)); +} + +static PyObject* +_chop_subtract(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + float scale; + int offset; + + scale = 1.0; + offset = 0; + + if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, + &scale, &offset)) + return NULL; + + return PyImagingNew(ImagingChopSubtract(self->image, imagep->image, + scale, offset)); +} + +static PyObject* +_chop_and(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopAnd(self->image, imagep->image)); +} + +static PyObject* +_chop_or(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopOr(self->image, imagep->image)); +} + +static PyObject* +_chop_xor(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopXor(self->image, imagep->image)); +} + +static PyObject* +_chop_add_modulo(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image)); +} + +static PyObject* +_chop_subtract_modulo(ImagingObject* self, PyObject* args) +{ + ImagingObject* imagep; + + if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) + return NULL; + + return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image)); +} + +#endif + + +/* -------------------------------------------------------------------- */ + +#ifdef WITH_IMAGEDRAW + +static PyObject* +_font_new(PyObject* self_, PyObject* args) +{ + ImagingFontObject *self; + int i, y0, y1; + static const char* wrong_length = "descriptor table has wrong size"; + + ImagingObject* imagep; + unsigned char* glyphdata; + int glyphdata_length; + if (!PyArg_ParseTuple(args, "O!s#", + &Imaging_Type, &imagep, + &glyphdata, &glyphdata_length)) + return NULL; + + if (glyphdata_length != 256 * 20) { + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; + } + + self = PyObject_New(ImagingFontObject, &ImagingFont_Type); + if (self == NULL) + return NULL; + + /* glyph bitmap */ + self->bitmap = imagep->image; + + y0 = y1 = 0; + + /* glyph glyphs */ + for (i = 0; i < 256; i++) { + self->glyphs[i].dx = S16(B16(glyphdata, 0)); + self->glyphs[i].dy = S16(B16(glyphdata, 2)); + self->glyphs[i].dx0 = S16(B16(glyphdata, 4)); + self->glyphs[i].dy0 = S16(B16(glyphdata, 6)); + self->glyphs[i].dx1 = S16(B16(glyphdata, 8)); + self->glyphs[i].dy1 = S16(B16(glyphdata, 10)); + self->glyphs[i].sx0 = S16(B16(glyphdata, 12)); + self->glyphs[i].sy0 = S16(B16(glyphdata, 14)); + self->glyphs[i].sx1 = S16(B16(glyphdata, 16)); + self->glyphs[i].sy1 = S16(B16(glyphdata, 18)); + if (self->glyphs[i].dy0 < y0) + y0 = self->glyphs[i].dy0; + if (self->glyphs[i].dy1 > y1) + y1 = self->glyphs[i].dy1; + glyphdata += 20; + } + + self->baseline = -y0; + self->ysize = y1 - y0; + + /* keep a reference to the bitmap object */ + Py_INCREF(imagep); + self->ref = imagep; + + return (PyObject*) self; +} + +static void +_font_dealloc(ImagingFontObject* self) +{ + Py_XDECREF(self->ref); + PyObject_Del(self); +} + +static inline int +textwidth(ImagingFontObject* self, const unsigned char* text) +{ + int xsize; + + for (xsize = 0; *text; text++) + xsize += self->glyphs[*text].dx; + + return xsize; +} + +static PyObject* +_font_getmask(ImagingFontObject* self, PyObject* args) +{ + Imaging im; + Imaging bitmap; + int x, b; + int status; + Glyph* glyph; + + unsigned char* text; + char* mode = ""; + if (!PyArg_ParseTuple(args, "s|s:getmask", &text, &mode)) + return NULL; + + im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize); + if (!im) + return NULL; + + b = 0; + (void) ImagingFill(im, &b); + + b = self->baseline; + for (x = 0; *text; text++) { + glyph = &self->glyphs[*text]; + bitmap = ImagingCrop( + self->bitmap, + glyph->sx0, glyph->sy0, glyph->sx1, glyph->sy1 + ); + if (!bitmap) + goto failed; + status = ImagingPaste( + im, bitmap, NULL, + glyph->dx0+x, glyph->dy0+b, glyph->dx1+x, glyph->dy1+b + ); + ImagingDelete(bitmap); + if (status < 0) + goto failed; + x = x + glyph->dx; + b = b + glyph->dy; + } + + return PyImagingNew(im); + + failed: + ImagingDelete(im); + return NULL; +} + +static PyObject* +_font_getsize(ImagingFontObject* self, PyObject* args) +{ + unsigned char* text; + if (!PyArg_ParseTuple(args, "s:getsize", &text)) + return NULL; + + return Py_BuildValue("ii", textwidth(self, text), self->ysize); +} + +static struct PyMethodDef _font_methods[] = { + {"getmask", (PyCFunction)_font_getmask, 1}, + {"getsize", (PyCFunction)_font_getsize, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_font_getattr(ImagingFontObject* self, char* name) +{ + return Py_FindMethod(_font_methods, (PyObject*) self, name); +} + +/* -------------------------------------------------------------------- */ + +static PyObject* +_draw_new(PyObject* self_, PyObject* args) +{ + ImagingDrawObject *self; + + ImagingObject* imagep; + int blend = 0; + if (!PyArg_ParseTuple(args, "O!|i", &Imaging_Type, &imagep, &blend)) + return NULL; + + self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type); + if (self == NULL) + return NULL; + + /* keep a reference to the image object */ + Py_INCREF(imagep); + self->image = imagep; + + self->ink[0] = self->ink[1] = self->ink[2] = self->ink[3] = 0; + + self->blend = blend; + + return (PyObject*) self; +} + +static void +_draw_dealloc(ImagingDrawObject* self) +{ + Py_XDECREF(self->image); + PyObject_Del(self); +} + +extern int PyPath_Flatten(PyObject* data, double **xy); + +static PyObject* +_draw_ink(ImagingDrawObject* self, PyObject* args) +{ + INT32 ink = 0; + PyObject* color; + char* mode = NULL; /* not used in this release */ + if (!PyArg_ParseTuple(args, "O|s", &color, &mode)) + return NULL; + + if (!getink(color, self->image->image, (char*) &ink)) + return NULL; + + return PyInt_FromLong((int) ink); +} + +static PyObject* +_draw_arc(ImagingDrawObject* self, PyObject* args) +{ + int x0, y0, x1, y1; + int ink; + int start, end; + int op = 0; + if (!PyArg_ParseTuple(args, "(iiii)iii|i", + &x0, &y0, &x1, &y1, + &start, &end, &ink)) + return NULL; + + if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end, + &ink, op) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_bitmap(ImagingDrawObject* self, PyObject* args) +{ + double *xy; + int n; + + PyObject *data; + ImagingObject* bitmap; + int ink; + if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 1) { + PyErr_SetString( + PyExc_TypeError, + "coordinate list must contain exactly 1 coordinate" + ); + return NULL; + } + + n = ImagingDrawBitmap( + self->image->image, (int) xy[0], (int) xy[1], bitmap->image, + &ink, self->blend + ); + + free(xy); + + if (n < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_chord(ImagingDrawObject* self, PyObject* args) +{ + int x0, y0, x1, y1; + int ink, fill; + int start, end; + if (!PyArg_ParseTuple(args, "(iiii)iiii", + &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) + return NULL; + + if (ImagingDrawChord(self->image->image, x0, y0, x1, y1, + start, end, &ink, fill, self->blend) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_ellipse(ImagingDrawObject* self, PyObject* args) +{ + double* xy; + int n; + + PyObject* data; + int ink; + int fill = 0; + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 2) { + PyErr_SetString( + PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; + } + + n = ImagingDrawEllipse( + self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3], + &ink, fill, self->blend + ); + + free(xy); + + if (n < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_line(ImagingDrawObject* self, PyObject* args) +{ + int x0, y0, x1, y1; + int ink; + if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink)) + return NULL; + + if (ImagingDrawLine(self->image->image, x0, y0, x1, y1, + &ink, self->blend) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_lines(ImagingDrawObject* self, PyObject* args) +{ + double *xy; + int i, n; + + PyObject *data; + int ink; + int width = 0; + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + + if (width <= 1) { + double *p = NULL; + for (i = 0; i < n-1; i++) { + p = &xy[i+i]; + if (ImagingDrawLine( + self->image->image, + (int) p[0], (int) p[1], (int) p[2], (int) p[3], + &ink, self->blend) < 0) { + free(xy); + return NULL; + } + } + if (p) /* draw last point */ + ImagingDrawPoint( + self->image->image, + (int) p[2], (int) p[3], + &ink, self->blend + ); + } else { + for (i = 0; i < n-1; i++) { + double *p = &xy[i+i]; + if (ImagingDrawWideLine( + self->image->image, + (int) p[0], (int) p[1], (int) p[2], (int) p[3], + &ink, width, self->blend) < 0) { + free(xy); + return NULL; + } + } + } + + free(xy); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_point(ImagingDrawObject* self, PyObject* args) +{ + int x, y; + int ink; + if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink)) + return NULL; + + if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_points(ImagingDrawObject* self, PyObject* args) +{ + double *xy; + int i, n; + + PyObject *data; + int ink; + if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + + for (i = 0; i < n; i++) { + double *p = &xy[i+i]; + if (ImagingDrawPoint(self->image->image, (int) p[0], (int) p[1], + &ink, self->blend) < 0) { + free(xy); + return NULL; + } + } + + free(xy); + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WITH_ARROW + +/* from outline.c */ +extern ImagingOutline PyOutline_AsOutline(PyObject* outline); + +static PyObject* +_draw_outline(ImagingDrawObject* self, PyObject* args) +{ + ImagingOutline outline; + + PyObject* outline_; + int ink; + int fill = 0; + if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) + return NULL; + + outline = PyOutline_AsOutline(outline_); + if (!outline) { + PyErr_SetString(PyExc_TypeError, "expected outline object"); + return NULL; + } + + if (ImagingDrawOutline(self->image->image, outline, + &ink, fill, self->blend) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +static PyObject* +_draw_pieslice(ImagingDrawObject* self, PyObject* args) +{ + int x0, y0, x1, y1; + int ink, fill; + int start, end; + if (!PyArg_ParseTuple(args, "(iiii)iiii", + &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) + return NULL; + + if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1, + start, end, &ink, fill, self->blend) < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_polygon(ImagingDrawObject* self, PyObject* args) +{ + double *xy; + int *ixy; + int n, i; + + PyObject* data; + int ink; + int fill = 0; + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n < 2) { + PyErr_SetString( + PyExc_TypeError, + "coordinate list must contain at least 2 coordinates" + ); + return NULL; + } + + /* Copy list of vertices to array */ + ixy = (int*) malloc(n * 2 * sizeof(int)); + + for (i = 0; i < n; i++) { + ixy[i+i] = (int) xy[i+i]; + ixy[i+i+1] = (int) xy[i+i+1]; + } + + free(xy); + + if (ImagingDrawPolygon(self->image->image, n, ixy, + &ink, fill, self->blend) < 0) { + free(ixy); + return NULL; + } + + free(ixy); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw_rectangle(ImagingDrawObject* self, PyObject* args) +{ + double* xy; + int n; + + PyObject* data; + int ink; + int fill = 0; + if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) + return NULL; + + n = PyPath_Flatten(data, &xy); + if (n < 0) + return NULL; + if (n != 2) { + PyErr_SetString( + PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; + } + + n = ImagingDrawRectangle( + self->image->image, (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], &ink, fill, self->blend + ); + + free(xy); + + if (n < 0) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef _draw_methods[] = { +#ifdef WITH_IMAGEDRAW + /* Graphics (ImageDraw) */ + {"draw_line", (PyCFunction)_draw_line, 1}, + {"draw_lines", (PyCFunction)_draw_lines, 1}, +#ifdef WITH_ARROW + {"draw_outline", (PyCFunction)_draw_outline, 1}, +#endif + {"draw_polygon", (PyCFunction)_draw_polygon, 1}, + {"draw_rectangle", (PyCFunction)_draw_rectangle, 1}, + {"draw_point", (PyCFunction)_draw_point, 1}, + {"draw_points", (PyCFunction)_draw_points, 1}, + {"draw_arc", (PyCFunction)_draw_arc, 1}, + {"draw_bitmap", (PyCFunction)_draw_bitmap, 1}, + {"draw_chord", (PyCFunction)_draw_chord, 1}, + {"draw_ellipse", (PyCFunction)_draw_ellipse, 1}, + {"draw_pieslice", (PyCFunction)_draw_pieslice, 1}, + {"draw_ink", (PyCFunction)_draw_ink, 1}, +#endif + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_draw_getattr(ImagingDrawObject* self, char* name) +{ + return Py_FindMethod(_draw_methods, (PyObject*) self, name); +} + +#endif + + +static PyObject* +pixel_access_new(ImagingObject* imagep, PyObject* args) +{ + PixelAccessObject *self; + + int readonly = 0; + if (!PyArg_ParseTuple(args, "|i", &readonly)) + return NULL; + + self = PyObject_New(PixelAccessObject, &PixelAccess_Type); + if (self == NULL) + return NULL; + + /* keep a reference to the image object */ + Py_INCREF(imagep); + self->image = imagep; + + self->readonly = readonly; + + return (PyObject*) self; +} + +static void +pixel_access_dealloc(PixelAccessObject* self) +{ + Py_XDECREF(self->image); + PyObject_Del(self); +} + +static PyObject * +pixel_access_getitem(PixelAccessObject *self, PyObject *xy) +{ + int x, y; + if (_getxy(xy, &x, &y)) + return NULL; + + return getpixel(self->image->image, self->image->access, x, y); +} + +static int +pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) +{ + Imaging im = self->image->image; + char ink[4]; + int x, y; + + if (self->readonly) { + (void) ImagingError_ValueError(readonly); + return -1; + } + + if (_getxy(xy, &x, &y)) + return -1; + + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { + PyErr_SetString(PyExc_IndexError, outside_image); + return -1; + } + + if (!color) /* FIXME: raise exception? */ + return 0; + + if (!getink(color, im, ink)) + return -1; + + self->image->access->put_pixel(im, x, y, ink); + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* EFFECTS (experimental) */ +/* -------------------------------------------------------------------- */ + +#ifdef WITH_EFFECTS + +static PyObject* +_effect_mandelbrot(ImagingObject* self, PyObject* args) +{ + int xsize = 512; + int ysize = 512; + double extent[4]; + int quality = 100; + + extent[0] = -3; extent[1] = -2.5; + extent[2] = 2; extent[3] = 2.5; + + if (!PyArg_ParseTuple(args, "|(ii)(dddd)i", &xsize, &ysize, + &extent[0], &extent[1], &extent[2], &extent[3], + &quality)) + return NULL; + + return PyImagingNew(ImagingEffectMandelbrot(xsize, ysize, extent, quality)); +} + +static PyObject* +_effect_noise(ImagingObject* self, PyObject* args) +{ + int xsize, ysize; + float sigma = 128; + if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) + return NULL; + + return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma)); +} + +static PyObject* +_effect_spread(ImagingObject* self, PyObject* args) +{ + int dist; + + if (!PyArg_ParseTuple(args, "i", &dist)) + return NULL; + + return PyImagingNew(ImagingEffectSpread(self->image, dist)); +} + +#endif + +/* -------------------------------------------------------------------- */ +/* UTILITIES */ +/* -------------------------------------------------------------------- */ + +static PyObject* +_crc32(PyObject* self, PyObject* args) +{ + unsigned char* buffer; + int bytes; + int hi, lo; + UINT32 crc; + + hi = lo = 0; + + if (!PyArg_ParseTuple(args, "s#|(ii)", &buffer, &bytes, &hi, &lo)) + return NULL; + + crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF); + + crc = ImagingCRC32(crc, (unsigned char *)buffer, bytes); + + return Py_BuildValue("ii", (crc >> 16) & 0xFFFF, crc & 0xFFFF); +} + +static PyObject* +_getcodecstatus(PyObject* self, PyObject* args) +{ + int status; + char* msg; + + if (!PyArg_ParseTuple(args, "i", &status)) + return NULL; + + switch (status) { + case IMAGING_CODEC_OVERRUN: + msg = "buffer overrun"; break; + case IMAGING_CODEC_BROKEN: + msg = "broken data stream"; break; + case IMAGING_CODEC_UNKNOWN: + msg = "unrecognized data stream contents"; break; + case IMAGING_CODEC_CONFIG: + msg = "codec configuration error"; break; + case IMAGING_CODEC_MEMORY: + msg = "out of memory"; break; + default: + Py_INCREF(Py_None); + return Py_None; + } + + return PyString_FromString(msg); +} + +/* -------------------------------------------------------------------- */ +/* DEBUGGING HELPERS */ +/* -------------------------------------------------------------------- */ + + +#ifdef WITH_DEBUG + +static PyObject* +_save_ppm(ImagingObject* self, PyObject* args) +{ + char* filename; + + if (!PyArg_ParseTuple(args, "s", &filename)) + return NULL; + + if (!ImagingSavePPM(self->image, filename)) + return NULL; + + Py_INCREF(Py_None); + return Py_None; +} + +#endif + +/* -------------------------------------------------------------------- */ + +/* methods */ + +static struct PyMethodDef methods[] = { + + /* Put commonly used methods first */ + {"getpixel", (PyCFunction)_getpixel, 1}, + {"putpixel", (PyCFunction)_putpixel, 1}, + + {"pixel_access", (PyCFunction)pixel_access_new, 1}, + + /* Standard processing methods (Image) */ + {"convert", (PyCFunction)_convert, 1}, + {"convert2", (PyCFunction)_convert2, 1}, + {"convert_matrix", (PyCFunction)_convert_matrix, 1}, + {"copy", (PyCFunction)_copy, 1}, + {"copy2", (PyCFunction)_copy2, 1}, +#ifdef WITH_CRACKCODE + {"crackcode", (PyCFunction)_crackcode, 1}, +#endif + {"crop", (PyCFunction)_crop, 1}, + {"expand", (PyCFunction)_expand, 1}, + {"filter", (PyCFunction)_filter, 1}, + {"histogram", (PyCFunction)_histogram, 1}, +#ifdef WITH_MODEFILTER + {"modefilter", (PyCFunction)_modefilter, 1}, +#endif + {"offset", (PyCFunction)_offset, 1}, + {"paste", (PyCFunction)_paste, 1}, + {"point", (PyCFunction)_point, 1}, + {"point_transform", (PyCFunction)_point_transform, 1}, + {"putdata", (PyCFunction)_putdata, 1}, +#ifdef WITH_QUANTIZE + {"quantize", (PyCFunction)_quantize, 1}, +#endif +#ifdef WITH_RANKFILTER + {"rankfilter", (PyCFunction)_rankfilter, 1}, +#endif + {"resize", (PyCFunction)_resize, 1}, + {"rotate", (PyCFunction)_rotate, 1}, + {"stretch", (PyCFunction)_stretch, 1}, + {"transpose", (PyCFunction)_transpose, 1}, + {"transform2", (PyCFunction)_transform2, 1}, + + {"isblock", (PyCFunction)_isblock, 1}, + + {"getbbox", (PyCFunction)_getbbox, 1}, + {"getcolors", (PyCFunction)_getcolors, 1}, + {"getextrema", (PyCFunction)_getextrema, 1}, + {"getprojection", (PyCFunction)_getprojection, 1}, + + {"getband", (PyCFunction)_getband, 1}, + {"putband", (PyCFunction)_putband, 1}, + {"fillband", (PyCFunction)_fillband, 1}, + + {"setmode", (PyCFunction)im_setmode, 1}, + + {"getpalette", (PyCFunction)_getpalette, 1}, + {"putpalette", (PyCFunction)_putpalette, 1}, + {"putpalettealpha", (PyCFunction)_putpalettealpha, 1}, + +#ifdef WITH_IMAGECHOPS + /* Channel operations (ImageChops) */ + {"chop_invert", (PyCFunction)_chop_invert, 1}, + {"chop_lighter", (PyCFunction)_chop_lighter, 1}, + {"chop_darker", (PyCFunction)_chop_darker, 1}, + {"chop_difference", (PyCFunction)_chop_difference, 1}, + {"chop_multiply", (PyCFunction)_chop_multiply, 1}, + {"chop_screen", (PyCFunction)_chop_screen, 1}, + {"chop_add", (PyCFunction)_chop_add, 1}, + {"chop_subtract", (PyCFunction)_chop_subtract, 1}, + {"chop_add_modulo", (PyCFunction)_chop_add_modulo, 1}, + {"chop_subtract_modulo", (PyCFunction)_chop_subtract_modulo, 1}, + {"chop_and", (PyCFunction)_chop_and, 1}, + {"chop_or", (PyCFunction)_chop_or, 1}, + {"chop_xor", (PyCFunction)_chop_xor, 1}, +#endif + +#ifdef WITH_UNSHARPMASK + /* Kevin Cazabon's unsharpmask extension */ + {"gaussian_blur", (PyCFunction)_gaussian_blur, 1}, + {"unsharp_mask", (PyCFunction)_unsharp_mask, 1}, +#endif + +#ifdef WITH_EFFECTS + /* Special effects */ + {"effect_spread", (PyCFunction)_effect_spread, 1}, +#endif + + /* Misc. */ + {"new_array", (PyCFunction)_new_array, 1}, + {"new_block", (PyCFunction)_new_block, 1}, + +#ifdef WITH_DEBUG + {"save_ppm", (PyCFunction)_save_ppm, 1}, +#endif + + {NULL, NULL} /* sentinel */ +}; + + +/* attributes */ + +static PyObject* +_getattr(ImagingObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(methods, (PyObject*) self, name); + if (res) + return res; + PyErr_Clear(); + if (strcmp(name, "mode") == 0) + return PyString_FromString(self->image->mode); + if (strcmp(name, "size") == 0) + return Py_BuildValue("ii", self->image->xsize, self->image->ysize); + if (strcmp(name, "bands") == 0) + return PyInt_FromLong(self->image->bands); + if (strcmp(name, "id") == 0) + return PyInt_FromLong((long) self->image); + if (strcmp(name, "ptr") == 0) + return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL); + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + + +/* basic sequence semantics */ + +static Py_ssize_t +image_length(ImagingObject *self) +{ + Imaging im = self->image; + + return im->xsize * im->ysize; +} + +static PyObject * +image_item(ImagingObject *self, Py_ssize_t i) +{ + int x, y; + Imaging im = self->image; + + if (im->xsize > 0) { + x = i % im->xsize; + y = i / im->xsize; + } else + x = y = 0; /* leave it to getpixel to raise an exception */ + + return getpixel(im, self->access, x, y); +} + +static PySequenceMethods image_as_sequence = { + (inquiry) image_length, /*sq_length*/ + (binaryfunc) NULL, /*sq_concat*/ + (ssizeargfunc) NULL, /*sq_repeat*/ + (ssizeargfunc) image_item, /*sq_item*/ + (ssizessizeargfunc) NULL, /*sq_slice*/ + (ssizeobjargproc) NULL, /*sq_ass_item*/ + (ssizessizeobjargproc) NULL, /*sq_ass_slice*/ +}; + + +/* type description */ + +statichere PyTypeObject Imaging_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingCore", /*tp_name*/ + sizeof(ImagingObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + &image_as_sequence, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0 /*tp_hash*/ +}; + +#ifdef WITH_IMAGEDRAW + +statichere PyTypeObject ImagingFont_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingFont", /*tp_name*/ + sizeof(ImagingFontObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_font_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_font_getattr, /*tp_getattr*/ +}; + +statichere PyTypeObject ImagingDraw_Type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingDraw", /*tp_name*/ + sizeof(ImagingDrawObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_draw_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_draw_getattr, /*tp_getattr*/ +}; + +#endif + +static PyMappingMethods pixel_access_as_mapping = { + (inquiry) NULL, /*mp_length*/ + (binaryfunc) pixel_access_getitem, /*mp_subscript*/ + (objobjargproc) pixel_access_setitem, /*mp_ass_subscript*/ +}; + +/* type description */ + +statichere PyTypeObject PixelAccess_Type = { + PyObject_HEAD_INIT(NULL) + 0, "PixelAccess", sizeof(PixelAccessObject), 0, + /* methods */ + (destructor)pixel_access_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + &pixel_access_as_mapping, /*tp_as_mapping */ + 0 /*tp_hash*/ +}; + +/* -------------------------------------------------------------------- */ + +/* FIXME: this is something of a mess. Should replace this with + pluggable codecs, but not before PIL 1.2 */ + +/* Decoders (in decode.c) */ +extern PyObject* PyImaging_BitDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_FliDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_GifDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_HexDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_JpegDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_MspDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_PcdDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_PcxDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_RawDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_XbmDecoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_ZipDecoderNew(PyObject* self, PyObject* args); + +/* Encoders (in encode.c) */ +extern PyObject* PyImaging_EpsEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_GifEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_JpegEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_PcxEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_RawEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_XbmEncoderNew(PyObject* self, PyObject* args); +extern PyObject* PyImaging_ZipEncoderNew(PyObject* self, PyObject* args); + +/* Display support etc (in display.c) */ +#ifdef WIN32 +extern PyObject* PyImaging_CreateWindowWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_DisplayWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_DisplayModeWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_GrabScreenWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_ListWindowsWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_EventLoopWin32(PyObject* self, PyObject* args); +extern PyObject* PyImaging_DrawWmf(PyObject* self, PyObject* args); +#endif + +/* Experimental path stuff (in path.c) */ +extern PyObject* PyPath_Create(ImagingObject* self, PyObject* args); + +/* Experimental outline stuff (in outline.c) */ +extern PyObject* PyOutline_Create(ImagingObject* self, PyObject* args); + +extern PyObject* PyImaging_Mapper(PyObject* self, PyObject* args); +extern PyObject* PyImaging_MapBuffer(PyObject* self, PyObject* args); + +static PyMethodDef functions[] = { + + /* Object factories */ + {"blend", (PyCFunction)_blend, 1}, + {"fill", (PyCFunction)_fill, 1}, + {"new", (PyCFunction)_new, 1}, + + {"getcount", (PyCFunction)_getcount, 1}, + + /* Functions */ + {"convert", (PyCFunction)_convert2, 1}, + {"copy", (PyCFunction)_copy2, 1}, + + /* Codecs */ + {"bit_decoder", (PyCFunction)PyImaging_BitDecoderNew, 1}, + {"eps_encoder", (PyCFunction)PyImaging_EpsEncoderNew, 1}, + {"fli_decoder", (PyCFunction)PyImaging_FliDecoderNew, 1}, + {"gif_decoder", (PyCFunction)PyImaging_GifDecoderNew, 1}, + {"gif_encoder", (PyCFunction)PyImaging_GifEncoderNew, 1}, + {"hex_decoder", (PyCFunction)PyImaging_HexDecoderNew, 1}, + {"hex_encoder", (PyCFunction)PyImaging_EpsEncoderNew, 1}, /* EPS=HEX! */ +#ifdef HAVE_LIBJPEG + {"jpeg_decoder", (PyCFunction)PyImaging_JpegDecoderNew, 1}, + {"jpeg_encoder", (PyCFunction)PyImaging_JpegEncoderNew, 1}, +#endif + {"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1}, + {"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1}, + {"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1}, + {"pcd_decoder", (PyCFunction)PyImaging_PcdDecoderNew, 1}, + {"pcx_decoder", (PyCFunction)PyImaging_PcxDecoderNew, 1}, + {"pcx_encoder", (PyCFunction)PyImaging_PcxEncoderNew, 1}, + {"raw_decoder", (PyCFunction)PyImaging_RawDecoderNew, 1}, + {"raw_encoder", (PyCFunction)PyImaging_RawEncoderNew, 1}, + {"sun_rle_decoder", (PyCFunction)PyImaging_SunRleDecoderNew, 1}, + {"tga_rle_decoder", (PyCFunction)PyImaging_TgaRleDecoderNew, 1}, + {"xbm_decoder", (PyCFunction)PyImaging_XbmDecoderNew, 1}, + {"xbm_encoder", (PyCFunction)PyImaging_XbmEncoderNew, 1}, +#ifdef HAVE_LIBZ + {"zip_decoder", (PyCFunction)PyImaging_ZipDecoderNew, 1}, + {"zip_encoder", (PyCFunction)PyImaging_ZipEncoderNew, 1}, +#endif + + /* Memory mapping */ +#ifdef WITH_MAPPING +#ifdef WIN32 + {"map", (PyCFunction)PyImaging_Mapper, 1}, +#endif + {"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1}, +#endif + + /* Display support */ +#ifdef WIN32 + {"display", (PyCFunction)PyImaging_DisplayWin32, 1}, + {"display_mode", (PyCFunction)PyImaging_DisplayModeWin32, 1}, + {"grabscreen", (PyCFunction)PyImaging_GrabScreenWin32, 1}, + {"grabclipboard", (PyCFunction)PyImaging_GrabClipboardWin32, 1}, + {"createwindow", (PyCFunction)PyImaging_CreateWindowWin32, 1}, + {"eventloop", (PyCFunction)PyImaging_EventLoopWin32, 1}, + {"listwindows", (PyCFunction)PyImaging_ListWindowsWin32, 1}, + {"drawwmf", (PyCFunction)PyImaging_DrawWmf, 1}, +#endif + + /* Utilities */ + {"crc32", (PyCFunction)_crc32, 1}, + {"getcodecstatus", (PyCFunction)_getcodecstatus, 1}, + + /* Debugging stuff */ + {"open_ppm", (PyCFunction)_open_ppm, 1}, + + /* Special effects (experimental) */ +#ifdef WITH_EFFECTS + {"effect_mandelbrot", (PyCFunction)_effect_mandelbrot, 1}, + {"effect_noise", (PyCFunction)_effect_noise, 1}, + {"linear_gradient", (PyCFunction)_linear_gradient, 1}, + {"radial_gradient", (PyCFunction)_radial_gradient, 1}, + {"wedge", (PyCFunction)_linear_gradient, 1}, /* Compatibility */ +#endif + + /* Drawing support stuff */ +#ifdef WITH_IMAGEDRAW + {"font", (PyCFunction)_font_new, 1}, + {"draw", (PyCFunction)_draw_new, 1}, +#endif + + /* Experimental path stuff */ +#ifdef WITH_IMAGEPATH + {"path", (PyCFunction)PyPath_Create, 1}, +#endif + + /* Experimental arrow graphics stuff */ +#ifdef WITH_ARROW + {"outline", (PyCFunction)PyOutline_Create, 1}, +#endif + + {NULL, NULL} /* sentinel */ +}; + +DL_EXPORT(void) +init_imaging(void) +{ + PyObject* m; + PyObject* d; + + /* Patch object type */ + Imaging_Type.ob_type = &PyType_Type; +#ifdef WITH_IMAGEDRAW + ImagingFont_Type.ob_type = &PyType_Type; + ImagingDraw_Type.ob_type = &PyType_Type; +#endif + PixelAccess_Type.ob_type = &PyType_Type; + + ImagingAccessInit(); + + m = Py_InitModule("_imaging", functions); + d = PyModule_GetDict(m); + +#ifdef HAVE_LIBJPEG + { + extern const char* ImagingJpegVersion(void); + PyDict_SetItemString(d, "jpeglib_version", PyString_FromString(ImagingJpegVersion())); + } +#endif + +#ifdef HAVE_LIBZ + { + extern const char* ImagingZipVersion(void); + PyDict_SetItemString(d, "zlib_version", PyString_FromString(ImagingZipVersion())); + } +#endif +} diff --git a/_imagingcms.c b/_imagingcms.c new file mode 100644 index 000000000..e9a3b00c5 --- /dev/null +++ b/_imagingcms.c @@ -0,0 +1,608 @@ +/* + * pyCMS + * a Python / PIL interface to the littleCMS ICC Color Management System + * Copyright (C) 2002-2003 Kevin Cazabon + * kevin@cazabon.com + * http://www.cazabon.com + * Adapted/reworked for PIL by Fredrik Lundh + * Copyright (c) 2009 Fredrik Lundh + * + * pyCMS home page: http://www.cazabon.com/pyCMS + * littleCMS home page: http://www.littlecms.com + * (littleCMS is Copyright (C) 1998-2001 Marti Maria) + * + * Originally released under LGPL. Graciously donated to PIL in + * March 2009, for distribution under the standard PIL license + */ + +#define COPYRIGHTINFO "\ +pyCMS\n\ +a Python / PIL interface to the littleCMS ICC Color Management System\n\ +Copyright (C) 2002-2003 Kevin Cazabon\n\ +kevin@cazabon.com\n\ +http://www.cazabon.com\n\ +" + +#include "Python.h" +#include "lcms.h" +#include "Imaging.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#if LCMS_VERSION < 117 +#define LCMSBOOL BOOL +#endif + +#ifdef WIN32 +#include +#endif + +#define PYCMSVERSION "0.1.0 pil" + +/* version history */ + +/* + 0.1.0 pil integration & refactoring + 0.0.2 alpha: Minor updates, added interfaces to littleCMS features, Jan 6, 2003 + - fixed some memory holes in how transforms/profiles were created and passed back to Python + due to improper destructor setup for PyCObjects + - added buildProofTransformFromOpenProfiles() function + - eliminated some code redundancy, centralizing several common tasks with internal functions + + 0.0.1 alpha: First public release Dec 26, 2002 + +*/ + +/* known to-do list with current version: + + Verify that PILmode->littleCMStype conversion in findLCMStype is correct for all PIL modes (it probably isn't for the more obscure ones) + + Add support for creating custom RGB profiles on the fly + Add support for checking presence of a specific tag in a profile + Add support for other littleCMS features as required + +*/ + +/* + INTENT_PERCEPTUAL 0 + INTENT_RELATIVE_COLORIMETRIC 1 + INTENT_SATURATION 2 + INTENT_ABSOLUTE_COLORIMETRIC 3 +*/ + +/* -------------------------------------------------------------------- */ +/* wrapper classes */ + +/* a profile represents the ICC characteristics for a specific device */ + +typedef struct { + PyObject_HEAD + cmsHPROFILE profile; +} CmsProfileObject; + +staticforward PyTypeObject CmsProfile_Type; + +#define CmsProfile_Check(op) ((op)->ob_type == &CmsProfile_Type) + +static PyObject* +cms_profile_new(cmsHPROFILE profile) +{ + CmsProfileObject* self; + + self = PyObject_New(CmsProfileObject, &CmsProfile_Type); + if (!self) + return NULL; + + self->profile = profile; + + return (PyObject*) self; +} + +static PyObject* +cms_profile_open(PyObject* self, PyObject* args) +{ + cmsHPROFILE hProfile; + + char* sProfile; + if (!PyArg_ParseTuple(args, "s:profile_open", &sProfile)) + return NULL; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + hProfile = cmsOpenProfileFromFile(sProfile, "r"); + if (!hProfile) { + PyErr_SetString(PyExc_IOError, "cannot open profile file"); + return NULL; + } + + return cms_profile_new(hProfile); +} + +static PyObject* +cms_profile_fromstring(PyObject* self, PyObject* args) +{ + cmsHPROFILE hProfile; + + char* pProfile; + int nProfile; + if (!PyArg_ParseTuple(args, "s#:profile_fromstring", &pProfile, &nProfile)) + return NULL; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + hProfile = cmsOpenProfileFromMem(pProfile, nProfile); + if (!hProfile) + PyErr_SetString(PyExc_IOError, "cannot open profile from string"); + + return cms_profile_new(hProfile); +} + +static void +cms_profile_dealloc(CmsProfileObject* self) +{ + (void) cmsCloseProfile(self->profile); + PyObject_Del(self); +} + +/* a transform represents the mapping between two profiles */ + +typedef struct { + PyObject_HEAD + char mode_in[8]; + char mode_out[8]; + cmsHTRANSFORM transform; +} CmsTransformObject; + +staticforward PyTypeObject CmsTransform_Type; + +#define CmsTransform_Check(op) ((op)->ob_type == &CmsTransform_Type) + +static PyObject* +cms_transform_new(cmsHTRANSFORM transform, char* mode_in, char* mode_out) +{ + CmsTransformObject* self; + + self = PyObject_New(CmsTransformObject, &CmsTransform_Type); + if (!self) + return NULL; + + self->transform = transform; + + strcpy(self->mode_in, mode_in); + strcpy(self->mode_out, mode_out); + + return (PyObject*) self; +} + +static void +cms_transform_dealloc(CmsTransformObject* self) +{ + cmsDeleteTransform(self->transform); + PyObject_Del(self); +} + +/* -------------------------------------------------------------------- */ +/* internal functions */ + +static const char* +findICmode(icColorSpaceSignature cs) +{ + switch (cs) { + case icSigXYZData: return "XYZ"; + case icSigLabData: return "LAB"; + case icSigLuvData: return "LUV"; + case icSigYCbCrData: return "YCbCr"; + case icSigYxyData: return "YXY"; + case icSigRgbData: return "RGB"; + case icSigGrayData: return "L"; + case icSigHsvData: return "HSV"; + case icSigHlsData: return "HLS"; + case icSigCmykData: return "CMYK"; + case icSigCmyData: return "CMY"; + default: return ""; /* other TBA */ + } +} + +static DWORD +findLCMStype(char* PILmode) +{ + if (strcmp(PILmode, "RGB") == 0) { + return TYPE_RGBA_8; + } + else if (strcmp(PILmode, "RGBA") == 0) { + return TYPE_RGBA_8; + } + else if (strcmp(PILmode, "RGBX") == 0) { + return TYPE_RGBA_8; + } + else if (strcmp(PILmode, "RGBA;16B") == 0) { + return TYPE_RGBA_16; + } + else if (strcmp(PILmode, "CMYK") == 0) { + return TYPE_CMYK_8; + } + else if (strcmp(PILmode, "L") == 0) { + return TYPE_GRAY_8; + } + else if (strcmp(PILmode, "L;16") == 0) { + return TYPE_GRAY_16; + } + else if (strcmp(PILmode, "L;16B") == 0) { + return TYPE_GRAY_16_SE; + } + else if (strcmp(PILmode, "YCCA") == 0) { + return TYPE_YCbCr_8; + } + else if (strcmp(PILmode, "YCC") == 0) { + return TYPE_YCbCr_8; + } + + else { + /* take a wild guess... but you probably should fail instead. */ + return TYPE_GRAY_8; /* so there's no buffer overrun... */ + } +} + +static int +pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) +{ + int i; + + if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) + return -1; + + Py_BEGIN_ALLOW_THREADS + + for (i = 0; i < im->ysize; i++) + cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize); + + Py_END_ALLOW_THREADS + + return 0; +} + +static cmsHTRANSFORM +_buildTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, char *sInMode, char *sOutMode, int iRenderingIntent, DWORD cmsFLAGS) +{ + cmsHTRANSFORM hTransform; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + Py_BEGIN_ALLOW_THREADS + + /* create the transform */ + hTransform = cmsCreateTransform(hInputProfile, + findLCMStype(sInMode), + hOutputProfile, + findLCMStype(sOutMode), + iRenderingIntent, cmsFLAGS); + + Py_END_ALLOW_THREADS + + if (!hTransform) + PyErr_SetString(PyExc_ValueError, "cannot build transform"); + + return hTransform; /* if NULL, an exception is set */ +} + +static cmsHTRANSFORM +_buildProofTransform(cmsHPROFILE hInputProfile, cmsHPROFILE hOutputProfile, cmsHPROFILE hProofProfile, char *sInMode, char *sOutMode, int iRenderingIntent, int iProofIntent, DWORD cmsFLAGS) +{ + cmsHTRANSFORM hTransform; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + Py_BEGIN_ALLOW_THREADS + + /* create the transform */ + hTransform = cmsCreateProofingTransform(hInputProfile, + findLCMStype(sInMode), + hOutputProfile, + findLCMStype(sOutMode), + hProofProfile, + iRenderingIntent, + iProofIntent, + cmsFLAGS); + + Py_END_ALLOW_THREADS + + if (!hTransform) + PyErr_SetString(PyExc_ValueError, "cannot build proof transform"); + + return hTransform; /* if NULL, an exception is set */ +} + +/* -------------------------------------------------------------------- */ +/* Python callable functions */ + +static PyObject * +buildTransform(PyObject *self, PyObject *args) { + CmsProfileObject *pInputProfile; + CmsProfileObject *pOutputProfile; + char *sInMode; + char *sOutMode; + int iRenderingIntent = 0; + int cmsFLAGS = 0; + + cmsHTRANSFORM transform = NULL; + + if (!PyArg_ParseTuple(args, "O!O!ss|ii:buildTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &sInMode, &sOutMode, &iRenderingIntent, &cmsFLAGS)) + return NULL; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + transform = _buildTransform(pInputProfile->profile, pOutputProfile->profile, sInMode, sOutMode, iRenderingIntent, cmsFLAGS); + + if (!transform) + return NULL; + + return cms_transform_new(transform, sInMode, sOutMode); +} + +static PyObject * +buildProofTransform(PyObject *self, PyObject *args) +{ + CmsProfileObject *pInputProfile; + CmsProfileObject *pOutputProfile; + CmsProfileObject *pProofProfile; + char *sInMode; + char *sOutMode; + int iRenderingIntent = 0; + int iProofIntent = 0; + int cmsFLAGS = 0; + + cmsHTRANSFORM transform = NULL; + + if (!PyArg_ParseTuple(args, "O!O!O!ss|iii:buildProofTransform", &CmsProfile_Type, &pInputProfile, &CmsProfile_Type, &pOutputProfile, &CmsProfile_Type, &pProofProfile, &sInMode, &sOutMode, &iRenderingIntent, &iProofIntent, &cmsFLAGS)) + return NULL; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + transform = _buildProofTransform(pInputProfile->profile, pOutputProfile->profile, pProofProfile->profile, sInMode, sOutMode, iRenderingIntent, iProofIntent, cmsFLAGS); + + if (!transform) + return NULL; + + return cms_transform_new(transform, sInMode, sOutMode); + +} + +static PyObject * +cms_transform_apply(CmsTransformObject *self, PyObject *args) +{ + long idIn; + long idOut; + Imaging im; + Imaging imOut; + + int result; + + if (!PyArg_ParseTuple(args, "ll:apply", &idIn, &idOut)) + return NULL; + + im = (Imaging) idIn; + imOut = (Imaging) idOut; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + result = pyCMSdoTransform(im, imOut, self->transform); + + return Py_BuildValue("i", result); +} + +/* -------------------------------------------------------------------- */ +/* Python-Callable On-The-Fly profile creation functions */ + +static PyObject * +createProfile(PyObject *self, PyObject *args) +{ + char *sColorSpace; + cmsHPROFILE hProfile; + int iColorTemp = 0; + LPcmsCIExyY whitePoint = NULL; + LCMSBOOL result; + + if (!PyArg_ParseTuple(args, "s|i:createProfile", &sColorSpace, &iColorTemp)) + return NULL; + + cmsErrorAction(LCMS_ERROR_IGNORE); + + if (strcmp(sColorSpace, "LAB") == 0) { + if (iColorTemp > 0) { + result = cmsWhitePointFromTemp(iColorTemp, whitePoint); + if (!result) { + PyErr_SetString(PyExc_ValueError, "ERROR: Could not calculate white point from color temperature provided, must be integer in degrees Kelvin"); + return NULL; + } + hProfile = cmsCreateLabProfile(whitePoint); + } else + hProfile = cmsCreateLabProfile(NULL); + } + else if (strcmp(sColorSpace, "XYZ") == 0) + hProfile = cmsCreateXYZProfile(); + else if (strcmp(sColorSpace, "sRGB") == 0) + hProfile = cmsCreate_sRGBProfile(); + else + hProfile = NULL; + + if (!hProfile) { + PyErr_SetString(PyExc_ValueError, "failed to create requested color space"); + return NULL; + } + + return cms_profile_new(hProfile); +} + +/* -------------------------------------------------------------------- */ +/* profile methods */ + +static PyObject * +cms_profile_is_intent_supported(CmsProfileObject *self, PyObject *args) +{ + LCMSBOOL result; + + int intent; + int direction; + if (!PyArg_ParseTuple(args, "ii:is_intent_supported", &intent, &direction)) + return NULL; + + result = cmsIsIntentSupported(self->profile, intent, direction); + + /* printf("cmsIsIntentSupported(%p, %d, %d) => %d\n", self->profile, intent, direction, result); */ + + return PyInt_FromLong(result != 0); +} + +#ifdef WIN32 +static PyObject * +cms_get_display_profile_win32(PyObject* self, PyObject* args) +{ + char filename[MAX_PATH]; + DWORD filename_size; + BOOL ok; + + int handle = 0; + int is_dc = 0; + if (!PyArg_ParseTuple(args, "|ii:get_display_profile", &handle, &is_dc)) + return NULL; + + filename_size = sizeof(filename); + + if (is_dc) { + ok = GetICMProfile((HDC) handle, &filename_size, filename); + } else { + HDC dc = GetDC((HWND) handle); + ok = GetICMProfile(dc, &filename_size, filename); + ReleaseDC((HWND) handle, dc); + } + + if (ok) + return PyString_FromStringAndSize(filename, filename_size-1); + + Py_INCREF(Py_None); + return Py_None; +} +#endif + +/* -------------------------------------------------------------------- */ +/* Python interface setup */ + +static PyMethodDef pyCMSdll_methods[] = { + + {"profile_open", cms_profile_open, 1}, + {"profile_fromstring", cms_profile_fromstring, 1}, + + /* profile and transform functions */ + {"buildTransform", buildTransform, 1}, + {"buildProofTransform", buildProofTransform, 1}, + {"createProfile", createProfile, 1}, + + /* platform specific tools */ +#ifdef WIN32 + {"get_display_profile_win32", cms_get_display_profile_win32, 1}, +#endif + + {NULL, NULL} +}; + +static struct PyMethodDef cms_profile_methods[] = { + {"is_intent_supported", (PyCFunction) cms_profile_is_intent_supported, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +cms_profile_getattr(CmsProfileObject* self, char* name) +{ + if (!strcmp(name, "product_name")) + return PyString_FromString(cmsTakeProductName(self->profile)); + if (!strcmp(name, "product_desc")) + return PyString_FromString(cmsTakeProductDesc(self->profile)); + if (!strcmp(name, "product_info")) + return PyString_FromString(cmsTakeProductInfo(self->profile)); + if (!strcmp(name, "rendering_intent")) + return PyInt_FromLong(cmsTakeRenderingIntent(self->profile)); + if (!strcmp(name, "pcs")) + return PyString_FromString(findICmode(cmsGetPCS(self->profile))); + if (!strcmp(name, "color_space")) + return PyString_FromString(findICmode(cmsGetColorSpace(self->profile))); + /* FIXME: add more properties (creation_datetime etc) */ + + return Py_FindMethod(cms_profile_methods, (PyObject*) self, name); +} + +statichere PyTypeObject CmsProfile_Type = { + PyObject_HEAD_INIT(NULL) + 0, "CmsProfile", sizeof(CmsProfileObject), 0, + /* methods */ + (destructor) cms_profile_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc) cms_profile_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0 /*tp_hash*/ +}; + +static struct PyMethodDef cms_transform_methods[] = { + {"apply", (PyCFunction) cms_transform_apply, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +cms_transform_getattr(CmsTransformObject* self, char* name) +{ + if (!strcmp(name, "inputMode")) + return PyString_FromString(self->mode_in); + if (!strcmp(name, "outputMode")) + return PyString_FromString(self->mode_out); + + return Py_FindMethod(cms_transform_methods, (PyObject*) self, name); +} + +statichere PyTypeObject CmsTransform_Type = { + PyObject_HEAD_INIT(NULL) + 0, "CmsTransform", sizeof(CmsTransformObject), 0, + /* methods */ + (destructor) cms_transform_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc) cms_transform_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + 0, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0 /*tp_hash*/ +}; + +DL_EXPORT(void) +init_imagingcms(void) +{ + PyObject *m; + PyObject *d; + PyObject *v; + + /* Patch up object types */ + CmsProfile_Type.ob_type = &PyType_Type; + CmsTransform_Type.ob_type = &PyType_Type; + + m = Py_InitModule("_imagingcms", pyCMSdll_methods); + d = PyModule_GetDict(m); + +#if PY_VERSION_HEX >= 0x02020000 + v = PyString_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); +#else + { + char buffer[100]; + sprintf(buffer, "%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100); + v = PyString_FromString(buffer); + } +#endif + PyDict_SetItemString(d, "littlecms_version", v); +} diff --git a/_imagingft.c b/_imagingft.c new file mode 100644 index 000000000..935808718 --- /dev/null +++ b/_imagingft.c @@ -0,0 +1,502 @@ +/* + * PIL FreeType Driver + * + * a FreeType 2.X driver for PIL + * + * history: + * 2001-02-17 fl Created (based on old experimental freetype 1.0 code) + * 2001-04-18 fl Fixed some egcs compiler nits + * 2002-11-08 fl Added unicode support; more font metrics, etc + * 2003-05-20 fl Fixed compilation under 1.5.2 and newer non-unicode builds + * 2003-09-27 fl Added charmap encoding support + * 2004-05-15 fl Fixed compilation for FreeType 2.1.8 + * 2004-09-10 fl Added support for monochrome bitmaps + * 2006-06-18 fl Fixed glyph bearing calculation + * 2007-12-23 fl Fixed crash in family/style attribute fetch + * 2008-01-02 fl Handle Unicode filenames properly + * + * Copyright (c) 1998-2007 by Secret Labs AB + */ + +#include "Python.h" +#include "Imaging.h" + +#if !defined(USE_FREETYPE_2_0) +/* undef/comment out to use freetype 2.0 */ +#define USE_FREETYPE_2_1 +#endif + +#if defined(USE_FREETYPE_2_1) +/* freetype 2.1 and newer */ +#include +#include FT_FREETYPE_H +#else +/* freetype 2.0 */ +#include +#endif + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#if PY_VERSION_HEX >= 0x01060000 +#if PY_VERSION_HEX < 0x02020000 || defined(Py_USING_UNICODE) +/* defining this enables unicode support (default under 1.6a1 and later) */ +#define HAVE_UNICODE +#endif +#endif + +#if !defined(Py_RETURN_NONE) +#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None +#endif + +#if !defined(FT_LOAD_TARGET_MONO) +#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME +#endif + +/* -------------------------------------------------------------------- */ +/* error table */ + +#undef FTERRORS_H +#undef __FTERRORS_H__ + +#define FT_ERRORDEF( e, v, s ) { e, s }, +#define FT_ERROR_START_LIST { +#define FT_ERROR_END_LIST { 0, 0 } }; + +struct { + int code; + const char* message; +} ft_errors[] = + +#include + +/* -------------------------------------------------------------------- */ +/* font objects */ + +static FT_Library library; + +typedef struct { + PyObject_HEAD + FT_Face face; +} FontObject; + +staticforward PyTypeObject Font_Type; + +/* round a 26.6 pixel coordinate to the nearest larger integer */ +#define PIXEL(x) ((((x)+63) & -64)>>6) + +static PyObject* +geterror(int code) +{ + int i; + + for (i = 0; ft_errors[i].message; i++) + if (ft_errors[i].code == code) { + PyErr_SetString(PyExc_IOError, ft_errors[i].message); + return NULL; + } + + PyErr_SetString(PyExc_IOError, "unknown freetype error"); + return NULL; +} + +static PyObject* +getfont(PyObject* self_, PyObject* args, PyObject* kw) +{ + /* create a font object from a file name and a size (in pixels) */ + + FontObject* self; + int error; + + char* filename; + int size; + int index = 0; + unsigned char* encoding = NULL; + static char* kwlist[] = { + "filename", "size", "index", "encoding", NULL + }; + +#if defined(HAVE_UNICODE) && PY_VERSION_HEX >= 0x02020000 + if (!PyArg_ParseTupleAndKeywords(args, kw, "eti|is", kwlist, + Py_FileSystemDefaultEncoding, &filename, + &size, &index, &encoding)) + return NULL; +#else + if (!PyArg_ParseTupleAndKeywords(args, kw, "si|is", kwlist, + &filename, &size, &index, &encoding)) + return NULL; +#endif + + if (!library) { + PyErr_SetString( + PyExc_IOError, + "failed to initialize FreeType library" + ); + return NULL; + } + + self = PyObject_New(FontObject, &Font_Type); + if (!self) + return NULL; + + error = FT_New_Face(library, filename, index, &self->face); + + if (!error) + error = FT_Set_Pixel_Sizes(self->face, 0, size); + + if (!error && encoding && strlen((char*) encoding) == 4) { + FT_Encoding encoding_tag = FT_MAKE_TAG( + encoding[0], encoding[1], encoding[2], encoding[3] + ); + error = FT_Select_Charmap(self->face, encoding_tag); + } + + if (error) { + PyObject_Del(self); + return geterror(error); + } + + return (PyObject*) self; +} + +static int +font_getchar(PyObject* string, int index, FT_ULong* char_out) +{ +#if defined(HAVE_UNICODE) + if (PyUnicode_Check(string)) { + Py_UNICODE* p = PyUnicode_AS_UNICODE(string); + int size = PyUnicode_GET_SIZE(string); + if (index >= size) + return 0; + *char_out = p[index]; + return 1; + } +#endif + if (PyString_Check(string)) { + unsigned char* p = (unsigned char*) PyString_AS_STRING(string); + int size = PyString_GET_SIZE(string); + if (index >= size) + return 0; + *char_out = (unsigned char) p[index]; + return 1; + } + return 0; +} + +static PyObject* +font_getsize(FontObject* self, PyObject* args) +{ + int i, x; + FT_ULong ch; + FT_Face face; + int xoffset; + FT_Bool kerning = FT_HAS_KERNING(self->face); + FT_UInt last_index = 0; + + /* calculate size and bearing for a given string */ + + PyObject* string; + if (!PyArg_ParseTuple(args, "O:getsize", &string)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + face = NULL; + xoffset = 0; + + for (x = i = 0; font_getchar(string, i, &ch); i++) { + int index, error; + face = self->face; + index = FT_Get_Char_Index(face, ch); + if (kerning && last_index && index) { + FT_Vector delta; + FT_Get_Kerning(self->face, last_index, index, ft_kerning_default, + &delta); + x += delta.x; + } + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + if (error) + return geterror(error); + if (i == 0) + xoffset = face->glyph->metrics.horiBearingX; + x += face->glyph->metrics.horiAdvance; + last_index = index; + } + + if (face) { + int offset; + /* left bearing */ + if (xoffset < 0) + x -= xoffset; + else + xoffset = 0; + /* right bearing */ + offset = face->glyph->metrics.horiAdvance - + face->glyph->metrics.width - + face->glyph->metrics.horiBearingX; + if (offset < 0) + x -= offset; + } + + return Py_BuildValue( + "(ii)(ii)", + PIXEL(x), PIXEL(self->face->size->metrics.height), + PIXEL(xoffset), 0 + ); +} + +static PyObject* +font_getabc(FontObject* self, PyObject* args) +{ + FT_ULong ch; + FT_Face face; + double a, b, c; + + /* calculate ABC values for a given string */ + + PyObject* string; + if (!PyArg_ParseTuple(args, "O:getabc", &string)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + if (font_getchar(string, 0, &ch)) { + int index, error; + face = self->face; + index = FT_Get_Char_Index(face, ch); + error = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + if (error) + return geterror(error); + a = face->glyph->metrics.horiBearingX / 64.0; + b = face->glyph->metrics.width / 64.0; + c = (face->glyph->metrics.horiAdvance - + face->glyph->metrics.horiBearingX - + face->glyph->metrics.width) / 64.0; + } else + a = b = c = 0.0; + + return Py_BuildValue("ddd", a, b, c); +} + +static PyObject* +font_render(FontObject* self, PyObject* args) +{ + int i, x, y; + Imaging im; + int index, error, ascender; + int load_flags; + unsigned char *source; + FT_ULong ch; + FT_GlyphSlot glyph; + FT_Bool kerning = FT_HAS_KERNING(self->face); + FT_UInt last_index = 0; + + /* render string into given buffer (the buffer *must* have + the right size, or this will crash) */ + PyObject* string; + long id; + int mask = 0; + if (!PyArg_ParseTuple(args, "Ol|i:render", &string, &id, &mask)) + return NULL; + +#if defined(HAVE_UNICODE) + if (!PyUnicode_Check(string) && !PyString_Check(string)) { +#else + if (!PyString_Check(string)) { +#endif + PyErr_SetString(PyExc_TypeError, "expected string"); + return NULL; + } + + im = (Imaging) id; + + load_flags = FT_LOAD_RENDER; + if (mask) + load_flags |= FT_LOAD_TARGET_MONO; + + for (x = i = 0; font_getchar(string, i, &ch); i++) { + if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) + x = -PIXEL(self->face->glyph->metrics.horiBearingX); + index = FT_Get_Char_Index(self->face, ch); + if (kerning && last_index && index) { + FT_Vector delta; + FT_Get_Kerning(self->face, last_index, index, ft_kerning_default, + &delta); + x += delta.x >> 6; + } + error = FT_Load_Glyph(self->face, index, load_flags); + if (error) + return geterror(error); + glyph = self->face->glyph; + if (mask) { + /* use monochrome mask (on palette images, etc) */ + int xx, x0, x1; + source = (unsigned char*) glyph->bitmap.buffer; + ascender = PIXEL(self->face->size->metrics.ascender); + xx = x + glyph->bitmap_left; + x0 = 0; + x1 = glyph->bitmap.width; + if (xx < 0) + x0 = -xx; + if (xx + x1 > im->xsize) + x1 = im->xsize - xx; + for (y = 0; y < glyph->bitmap.rows; y++) { + int yy = y + ascender - glyph->bitmap_top; + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + unsigned char *target = im->image8[yy] + xx; + int i, j, m = 128; + for (i = j = 0; j < x1; j++) { + if (j >= x0 && (source[i] & m)) + target[j] = 255; + if (!(m >>= 1)) { + m = 128; + i++; + } + } + } + source += glyph->bitmap.pitch; + } + } else { + /* use antialiased rendering */ + int xx, x0, x1; + source = (unsigned char*) glyph->bitmap.buffer; + ascender = PIXEL(self->face->size->metrics.ascender); + xx = x + glyph->bitmap_left; + x0 = 0; + x1 = glyph->bitmap.width; + if (xx < 0) + x0 = -xx; + if (xx + x1 > im->xsize) + x1 = im->xsize - xx; + for (y = 0; y < glyph->bitmap.rows; y++) { + int yy = y + ascender - glyph->bitmap_top; + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + int i; + unsigned char *target = im->image8[yy] + xx; + for (i = x0; i < x1; i++) { + if (target[i] < source[i]) + target[i] = source[i]; + } + } + source += glyph->bitmap.pitch; + } + } + x += PIXEL(glyph->metrics.horiAdvance); + last_index = index; + } + + Py_RETURN_NONE; +} + +static void +font_dealloc(FontObject* self) +{ + FT_Done_Face(self->face); + PyObject_Del(self); +} + +static PyMethodDef font_methods[] = { + {"render", (PyCFunction) font_render, METH_VARARGS}, + {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, + {"getabc", (PyCFunction) font_getabc, METH_VARARGS}, + {NULL, NULL} +}; + +static PyObject* +font_getattr(FontObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(font_methods, (PyObject*) self, name); + + if (res) + return res; + + PyErr_Clear(); + + /* attributes */ + if (!strcmp(name, "family")) { + if (self->face->family_name) + return PyString_FromString(self->face->family_name); + Py_RETURN_NONE; + } + if (!strcmp(name, "style")) { + if (self->face->style_name) + return PyString_FromString(self->face->style_name); + Py_RETURN_NONE; + } + if (!strcmp(name, "ascent")) + return PyInt_FromLong(PIXEL(self->face->size->metrics.ascender)); + if (!strcmp(name, "descent")) + return PyInt_FromLong(-PIXEL(self->face->size->metrics.descender)); + + if (!strcmp(name, "glyphs")) + /* number of glyphs provided by this font */ + return PyInt_FromLong(self->face->num_glyphs); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject Font_Type = { + PyObject_HEAD_INIT(NULL) + 0, "Font", sizeof(FontObject), 0, + /* methods */ + (destructor)font_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + (getattrfunc)font_getattr, /* tp_getattr */ +}; + +static PyMethodDef _functions[] = { + {"getfont", (PyCFunction) getfont, METH_VARARGS|METH_KEYWORDS}, + {NULL, NULL} +}; + +DL_EXPORT(void) +init_imagingft(void) +{ + PyObject* m; + PyObject* d; + PyObject* v; + int major, minor, patch; + + /* Patch object type */ + Font_Type.ob_type = &PyType_Type; + + m = Py_InitModule("_imagingft", _functions); + d = PyModule_GetDict(m); + + if (FT_Init_FreeType(&library)) + return; /* leave it uninitalized */ + + FT_Library_Version(library, &major, &minor, &patch); + +#if PY_VERSION_HEX >= 0x02020000 + v = PyString_FromFormat("%d.%d.%d", major, minor, patch); +#else + { + char buffer[100]; + sprintf(buffer, "%d.%d.%d", major, minor, patch); + v = PyString_FromString(buffer); + } +#endif + PyDict_SetItemString(d, "freetype2_version", v); +} diff --git a/_imagingmath.c b/_imagingmath.c new file mode 100644 index 000000000..928986bb3 --- /dev/null +++ b/_imagingmath.c @@ -0,0 +1,284 @@ +/* + * The Python Imaging Library + * + * a simple math add-on for the Python Imaging Library + * + * history: + * 1999-02-15 fl Created + * 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 + * + * Copyright (c) 1999-2005 by Secret Labs AB + * Copyright (c) 2005 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + +#include "Python.h" + +#include "Imaging.h" + +#include "math.h" +#include "float.h" + +#define MAX_INT32 2147483647.0 +#define MIN_INT32 -2147483648.0 + +#if defined(_MSC_VER) && _MSC_VER < 1500 +/* python 2.1/2.2/2.3 = VC98 = VER 1200 */ +/* python 2.4/2.5 = VS.NET 2003 = VER 1310 */ +/* python 2.6 = VS 9.0 = VER 1500 */ +#define powf(a, b) ((float) pow((double) (a), (double) (b))) +#endif + +#define UNOP(name, op, type)\ +void name(Imaging out, Imaging im1)\ +{\ + int x, y;\ + for (y = 0; y < out->ysize; y++) {\ + type* p0 = (type*) out->image[y];\ + type* p1 = (type*) im1->image[y];\ + for (x = 0; x < out->xsize; x++) {\ + *p0 = op(type, *p1);\ + p0++; p1++;\ + }\ + }\ +} + +#define BINOP(name, op, type)\ +void name(Imaging out, Imaging im1, Imaging im2)\ +{\ + int x, y;\ + for (y = 0; y < out->ysize; y++) {\ + type* p0 = (type*) out->image[y];\ + type* p1 = (type*) im1->image[y];\ + type* p2 = (type*) im2->image[y];\ + for (x = 0; x < out->xsize; x++) {\ + *p0 = op(type, *p1, *p2);\ + p0++; p1++; p2++;\ + }\ + }\ +} + +#define NEG(type, v1) -(v1) +#define INVERT(type, v1) ~(v1) + +#define ADD(type, v1, v2) (v1)+(v2) +#define SUB(type, v1, v2) (v1)-(v2) +#define MUL(type, v1, v2) (v1)*(v2) + +#define MIN(type, v1, v2) ((v1)<(v2))?(v1):(v2) +#define MAX(type, v1, v2) ((v1)>(v2))?(v1):(v2) + +#define AND(type, v1, v2) (v1)&(v2) +#define OR(type, v1, v2) (v1)|(v2) +#define XOR(type, v1, v2) (v1)^(v2) +#define LSHIFT(type, v1, v2) (v1)<<(v2) +#define RSHIFT(type, v1, v2) (v1)>>(v2) + +#define ABS_I(type, v1) abs((v1)) +#define ABS_F(type, v1) fabs((v1)) + +/* -------------------------------------------------------------------- + * some day, we should add FPE protection mechanisms. see pyfpe.h for + * details. + * + * PyFPE_START_PROTECT("Error in foobar", return 0) + * PyFPE_END_PROTECT(result) + */ + +#define DIV_I(type, v1, v2) ((v2)!=0)?(v1)/(v2):0 +#define DIV_F(type, v1, v2) ((v2)!=0.0F)?(v1)/(v2):0.0F + +#define MOD_I(type, v1, v2) ((v2)!=0)?(v1)%(v2):0 +#define MOD_F(type, v1, v2) ((v2)!=0.0F)?fmod((v1),(v2)):0.0F + +static int powi(int x, int y) +{ + double v = pow(x, y) + 0.5; + if (errno == EDOM) + return 0; + if (v < MIN_INT32) + v = MIN_INT32; + else if (v > MAX_INT32) + v = MAX_INT32; + return (int) v; +} + +#define POW_I(type, v1, v2) powi(v1, v2) +#define POW_F(type, v1, v2) powf(v1, v2) /* FIXME: EDOM handling */ + +#define DIFF_I(type, v1, v2) abs((v1)-(v2)) +#define DIFF_F(type, v1, v2) fabs((v1)-(v2)) + +#define EQ(type, v1, v2) (v1)==(v2) +#define NE(type, v1, v2) (v1)!=(v2) +#define LT(type, v1, v2) (v1)<(v2) +#define LE(type, v1, v2) (v1)<=(v2) +#define GT(type, v1, v2) (v1)>(v2) +#define GE(type, v1, v2) (v1)>=(v2) + +UNOP(abs_I, ABS_I, INT32) +UNOP(neg_I, NEG, INT32) + +BINOP(add_I, ADD, INT32) +BINOP(sub_I, SUB, INT32) +BINOP(mul_I, MUL, INT32) +BINOP(div_I, DIV_I, INT32) +BINOP(mod_I, MOD_I, INT32) +BINOP(pow_I, POW_I, INT32) +BINOP(diff_I, DIFF_I, INT32) + +UNOP(invert_I, INVERT, INT32) +BINOP(and_I, AND, INT32) +BINOP(or_I, OR, INT32) +BINOP(xor_I, XOR, INT32) +BINOP(lshift_I, LSHIFT, INT32) +BINOP(rshift_I, RSHIFT, INT32) + +BINOP(min_I, MIN, INT32) +BINOP(max_I, MAX, INT32) + +BINOP(eq_I, EQ, INT32) +BINOP(ne_I, NE, INT32) +BINOP(lt_I, LT, INT32) +BINOP(le_I, LE, INT32) +BINOP(gt_I, GT, INT32) +BINOP(ge_I, GE, INT32) + +UNOP(abs_F, ABS_F, FLOAT32) +UNOP(neg_F, NEG, FLOAT32) + +BINOP(add_F, ADD, FLOAT32) +BINOP(sub_F, SUB, FLOAT32) +BINOP(mul_F, MUL, FLOAT32) +BINOP(div_F, DIV_F, FLOAT32) +BINOP(mod_F, MOD_F, FLOAT32) +BINOP(pow_F, POW_F, FLOAT32) +BINOP(diff_F, DIFF_F, FLOAT32) + +BINOP(min_F, MIN, FLOAT32) +BINOP(max_F, MAX, FLOAT32) + +BINOP(eq_F, EQ, FLOAT32) +BINOP(ne_F, NE, FLOAT32) +BINOP(lt_F, LT, FLOAT32) +BINOP(le_F, LE, FLOAT32) +BINOP(gt_F, GT, FLOAT32) +BINOP(ge_F, GE, FLOAT32) + +static PyObject * +_unop(PyObject* self, PyObject* args) +{ + Imaging out; + Imaging im1; + void (*unop)(Imaging, Imaging); + + long op, i0, i1; + if (!PyArg_ParseTuple(args, "lll", &op, &i0, &i1)) + return NULL; + + out = (Imaging) i0; + im1 = (Imaging) i1; + + unop = (void*) op; + + unop(out, im1); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_binop(PyObject* self, PyObject* args) +{ + Imaging out; + Imaging im1; + Imaging im2; + void (*binop)(Imaging, Imaging, Imaging); + + long op, i0, i1, i2; + if (!PyArg_ParseTuple(args, "llll", &op, &i0, &i1, &i2)) + return NULL; + + out = (Imaging) i0; + im1 = (Imaging) i1; + im2 = (Imaging) i2; + + binop = (void*) op; + + binop(out, im1, im2); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef _functions[] = { + {"unop", _unop, 1}, + {"binop", _binop, 1}, + {NULL, NULL} +}; + +static void +install(PyObject *d, char* name, void* value) +{ + PyObject *v = PyInt_FromLong((long) value); + if (!v || PyDict_SetItemString(d, name, v)) + PyErr_Clear(); + Py_XDECREF(v); +} + +DL_EXPORT(void) +init_imagingmath(void) +{ + PyObject* m; + PyObject* d; + + m = Py_InitModule("_imagingmath", _functions); + d = PyModule_GetDict(m); + + install(d, "abs_I", abs_I); + install(d, "neg_I", neg_I); + install(d, "add_I", add_I); + install(d, "sub_I", sub_I); + install(d, "diff_I", diff_I); + install(d, "mul_I", mul_I); + install(d, "div_I", div_I); + install(d, "mod_I", mod_I); + install(d, "min_I", min_I); + install(d, "max_I", max_I); + install(d, "pow_I", pow_I); + + install(d, "invert_I", invert_I); + install(d, "and_I", and_I); + install(d, "or_I", or_I); + install(d, "xor_I", xor_I); + install(d, "lshift_I", lshift_I); + install(d, "rshift_I", rshift_I); + + install(d, "eq_I", eq_I); + install(d, "ne_I", ne_I); + install(d, "lt_I", lt_I); + install(d, "le_I", le_I); + install(d, "gt_I", gt_I); + install(d, "ge_I", ge_I); + + install(d, "abs_F", abs_F); + install(d, "neg_F", neg_F); + install(d, "add_F", add_F); + install(d, "sub_F", sub_F); + install(d, "diff_F", diff_F); + install(d, "mul_F", mul_F); + install(d, "div_F", div_F); + install(d, "mod_F", mod_F); + install(d, "min_F", min_F); + install(d, "max_F", max_F); + install(d, "pow_F", pow_F); + + install(d, "eq_F", eq_F); + install(d, "ne_F", ne_F); + install(d, "lt_F", lt_F); + install(d, "le_F", le_F); + install(d, "gt_F", gt_F); + install(d, "ge_F", ge_F); + +} diff --git a/_imagingtk.c b/_imagingtk.c new file mode 100644 index 000000000..6165a2acb --- /dev/null +++ b/_imagingtk.c @@ -0,0 +1,70 @@ +/* + * The Python Imaging Library. + * + * tkinter hooks + * + * history: + * 99-07-26 fl created + * 99-08-15 fl moved to its own support module + * + * Copyright (c) Secret Labs AB 1999. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Python.h" +#include "Imaging.h" + +#include "tk.h" + +/* must link with Tk/tkImaging.c */ +extern void TkImaging_Init(Tcl_Interp* interp); + +/* copied from _tkinter.c (this isn't as bad as it may seem: for new + versions, we use _tkinter's interpaddr hook instead, and all older + versions use this structure layout) */ + +typedef struct { + PyObject_HEAD + Tcl_Interp* interp; +} TkappObject; + +static PyObject* +_tkinit(PyObject* self, PyObject* args) +{ + Tcl_Interp* interp; + + long arg; + int is_interp; + if (!PyArg_ParseTuple(args, "li", &arg, &is_interp)) + return NULL; + + if (is_interp) + interp = (Tcl_Interp*) arg; + else { + TkappObject* app; + /* Do it the hard way. This will break if the TkappObject + layout changes */ + app = (TkappObject*) arg; + interp = app->interp; + } + + /* This will bomb if interp is invalid... */ + TkImaging_Init(interp); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef functions[] = { + /* Tkinter interface stuff */ + {"tkinit", (PyCFunction)_tkinit, 1}, + {NULL, NULL} /* sentinel */ +}; + +DL_EXPORT(void) +init_imagingtk(void) +{ + Py_InitModule("_imagingtk", functions); +} diff --git a/decode.c b/decode.c new file mode 100644 index 000000000..6ea8d9c3f --- /dev/null +++ b/decode.c @@ -0,0 +1,689 @@ +/* + * The Python Imaging Library. + * + * standard decoder interfaces for the Imaging library + * + * history: + * 1996-03-28 fl Moved from _imagingmodule.c + * 1996-04-15 fl Support subregions in setimage + * 1996-04-19 fl Allocate decoder buffer (where appropriate) + * 1996-05-02 fl Added jpeg decoder + * 1996-05-12 fl Compile cleanly as C++ + * 1996-05-16 fl Added hex decoder + * 1996-05-26 fl Added jpeg configuration parameters + * 1996-12-14 fl Added zip decoder + * 1996-12-30 fl Plugged potential memory leak for tiled images + * 1997-01-03 fl Added fli and msp decoders + * 1997-01-04 fl Added sun_rle and tga_rle decoders + * 1997-05-31 fl Added bitfield decoder + * 1998-09-11 fl Added orientation and pixelsize fields to tga_rle decoder + * 1998-12-29 fl Added mode/rawmode argument to decoders + * 1998-12-30 fl Added mode argument to *all* decoders + * 2002-06-09 fl Added stride argument to pcx decoder + * + * Copyright (c) 1997-2002 by Secret Labs AB. + * Copyright (c) 1995-2002 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + +/* FIXME: make these pluggable! */ + +#include "Python.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#include "Imaging.h" + +#include "Gif.h" +#include "Lzw.h" +#include "Raw.h" +#include "Bit.h" + + +/* -------------------------------------------------------------------- */ +/* Common */ +/* -------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + int (*decode)(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); + struct ImagingCodecStateInstance state; + Imaging im; + PyObject* lock; +} ImagingDecoderObject; + +staticforward PyTypeObject ImagingDecoderType; + +static ImagingDecoderObject* +PyImaging_DecoderNew(int contextsize) +{ + ImagingDecoderObject *decoder; + void *context; + + ImagingDecoderType.ob_type = &PyType_Type; + + decoder = PyObject_New(ImagingDecoderObject, &ImagingDecoderType); + if (decoder == NULL) + return NULL; + + /* Clear the decoder state */ + memset(&decoder->state, 0, sizeof(decoder->state)); + + /* Allocate decoder context */ + if (contextsize > 0) { + context = (void*) calloc(1, contextsize); + if (!context) { + Py_DECREF(decoder); + (void) PyErr_NoMemory(); + return NULL; + } + } else + context = 0; + + /* Initialize decoder context */ + decoder->state.context = context; + + /* Target image */ + decoder->lock = NULL; + decoder->im = NULL; + + return decoder; +} + +static void +_dealloc(ImagingDecoderObject* decoder) +{ + free(decoder->state.buffer); + free(decoder->state.context); + Py_XDECREF(decoder->lock); + PyObject_Del(decoder); +} + +static PyObject* +_decode(ImagingDecoderObject* decoder, PyObject* args) +{ + UINT8* buffer; + int bufsize, status; + + if (!PyArg_ParseTuple(args, "s#", &buffer, &bufsize)) + return NULL; + + status = decoder->decode(decoder->im, &decoder->state, buffer, bufsize); + + return Py_BuildValue("ii", status, decoder->state.errcode); +} + +extern Imaging PyImaging_AsImaging(PyObject *op); + +static PyObject* +_setimage(ImagingDecoderObject* decoder, PyObject* args) +{ + PyObject* op; + Imaging im; + ImagingCodecState state; + int x0, y0, x1, y1; + + x0 = y0 = x1 = y1 = 0; + + /* FIXME: should publish the ImagingType descriptor */ + if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) + return NULL; + im = PyImaging_AsImaging(op); + if (!im) + return NULL; + + decoder->im = im; + + state = &decoder->state; + + /* Setup decoding tile extent */ + if (x0 == 0 && x1 == 0) { + state->xsize = im->xsize; + state->ysize = im->ysize; + } else { + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; + } + + if (state->xsize <= 0 || + state->xsize + state->xoff > (int) im->xsize || + state->ysize <= 0 || + state->ysize + state->yoff > (int) im->ysize) { + PyErr_SetString(PyExc_ValueError, "tile cannot extend outside image"); + return NULL; + } + + /* Allocate memory buffer (if bits field is set) */ + if (state->bits > 0) { + if (!state->bytes) + state->bytes = (state->bits * state->xsize+7)/8; + state->buffer = (UINT8*) malloc(state->bytes); + if (!state->buffer) + return PyErr_NoMemory(); + } + + /* Keep a reference to the image object, to make sure it doesn't + go away before we do */ + Py_INCREF(op); + Py_XDECREF(decoder->lock); + decoder->lock = op; + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef methods[] = { + {"decode", (PyCFunction)_decode, 1}, + {"setimage", (PyCFunction)_setimage, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_getattr(ImagingDecoderObject* self, char* name) +{ + return Py_FindMethod(methods, (PyObject*) self, name); +} + +statichere PyTypeObject ImagingDecoderType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingDecoder", /*tp_name*/ + sizeof(ImagingDecoderObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_hash*/ +}; + +/* -------------------------------------------------------------------- */ + +int +get_unpacker(ImagingDecoderObject* decoder, const char* mode, + const char* rawmode) +{ + int bits; + ImagingShuffler unpack; + + unpack = ImagingFindUnpacker(mode, rawmode, &bits); + if (!unpack) { + Py_DECREF(decoder); + PyErr_SetString(PyExc_ValueError, "unknown raw mode"); + return -1; + } + + decoder->state.shuffle = unpack; + decoder->state.bits = bits; + + return 0; +} + + +/* -------------------------------------------------------------------- */ +/* BIT (packed fields) */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_BitDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + int bits = 8; + int pad = 8; + int fill = 0; + int sign = 0; + int ystep = 1; + if (!PyArg_ParseTuple(args, "s|iiiii", &mode, &bits, &pad, &fill, + &sign, &ystep)) + return NULL; + + if (strcmp(mode, "F") != 0) { + PyErr_SetString(PyExc_ValueError, "bad image mode"); + return NULL; + } + + decoder = PyImaging_DecoderNew(sizeof(BITSTATE)); + if (decoder == NULL) + return NULL; + + decoder->decode = ImagingBitDecode; + + decoder->state.ystep = ystep; + + ((BITSTATE*)decoder->state.context)->bits = bits; + ((BITSTATE*)decoder->state.context)->pad = pad; + ((BITSTATE*)decoder->state.context)->fill = fill; + ((BITSTATE*)decoder->state.context)->sign = sign; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* FLI */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_FliDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + decoder->decode = ImagingFliDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* GIF */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_GifDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + int bits = 8; + int interlace = 0; + if (!PyArg_ParseTuple(args, "s|ii", &mode, &bits, &interlace)) + return NULL; + + if (strcmp(mode, "L") != 0 && strcmp(mode, "P") != 0) { + PyErr_SetString(PyExc_ValueError, "bad image mode"); + return NULL; + } + + decoder = PyImaging_DecoderNew(sizeof(GIFDECODERSTATE)); + if (decoder == NULL) + return NULL; + + decoder->decode = ImagingGifDecode; + + ((GIFDECODERSTATE*)decoder->state.context)->bits = bits; + ((GIFDECODERSTATE*)decoder->state.context)->interlace = interlace; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* HEX */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_HexDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + return NULL; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingHexDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* LZW */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_TiffLzwDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + int filter = 0; + if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &filter)) + return NULL; + + decoder = PyImaging_DecoderNew(sizeof(LZWSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingLzwDecode; + + ((LZWSTATE*)decoder->state.context)->filter = filter; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* MSP */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_MspDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, "1", "1") < 0) + return NULL; + + decoder->decode = ImagingMspDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* PackBits */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_PackbitsDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + return NULL; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingPackbitsDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* PCD */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_PcdDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + /* Unpack from PhotoYCC to RGB */ + if (get_unpacker(decoder, "RGB", "YCC;P") < 0) + return NULL; + + decoder->decode = ImagingPcdDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* PCX */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_PcxDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + int stride; + if (!PyArg_ParseTuple(args, "ssi", &mode, &rawmode, &stride)) + return NULL; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->state.bytes = stride; + + decoder->decode = ImagingPcxDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* RAW */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_RawDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + int stride = 0; + int ystep = 1; + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) + return NULL; + + decoder = PyImaging_DecoderNew(sizeof(RAWSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingRawDecode; + + decoder->state.ystep = ystep; + + ((RAWSTATE*)decoder->state.context)->stride = stride; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* SUN RLE */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_SunRleDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + if (!PyArg_ParseTuple(args, "ss", &mode, &rawmode)) + return NULL; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingSunRleDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* TGA RLE */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_TgaRleDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + int ystep = 1; + int depth = 8; + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &ystep, &depth)) + return NULL; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingTgaRleDecode; + + decoder->state.ystep = ystep; + decoder->state.count = depth / 8; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* XBM */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_XbmDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + decoder = PyImaging_DecoderNew(0); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, "1", "1;R") < 0) + return NULL; + + decoder->decode = ImagingXbmDecode; + + return (PyObject*) decoder; +} + + +/* -------------------------------------------------------------------- */ +/* ZIP */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBZ + +#include "Zip.h" + +PyObject* +PyImaging_ZipDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; + int interlaced = 0; + if (!PyArg_ParseTuple(args, "ss|i", &mode, &rawmode, &interlaced)) + return NULL; + + decoder = PyImaging_DecoderNew(sizeof(ZIPSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingZipDecode; + + ((ZIPSTATE*)decoder->state.context)->interlaced = interlaced; + + return (PyObject*) decoder; +} +#endif + + +/* -------------------------------------------------------------------- */ +/* JPEG */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBJPEG + +/* We better define this decoder last in this file, so the following + undef's won't mess things up for the Imaging library proper. */ + +#undef HAVE_PROTOTYPES +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT8 +#undef INT16 +#undef INT32 + +#include "Jpeg.h" + +PyObject* +PyImaging_JpegDecoderNew(PyObject* self, PyObject* args) +{ + ImagingDecoderObject* decoder; + + char* mode; + char* rawmode; /* what we wan't from the decoder */ + char* jpegmode; /* what's in the file */ + int scale = 1; + int draft = 0; + if (!PyArg_ParseTuple(args, "ssz|ii", &mode, &rawmode, &jpegmode, + &scale, &draft)) + return NULL; + + if (!jpegmode) + jpegmode = ""; + + decoder = PyImaging_DecoderNew(sizeof(JPEGSTATE)); + if (decoder == NULL) + return NULL; + + if (get_unpacker(decoder, mode, rawmode) < 0) + return NULL; + + decoder->decode = ImagingJpegDecode; + + strncpy(((JPEGSTATE*)decoder->state.context)->rawmode, rawmode, 8); + strncpy(((JPEGSTATE*)decoder->state.context)->jpegmode, jpegmode, 8); + + ((JPEGSTATE*)decoder->state.context)->scale = scale; + ((JPEGSTATE*)decoder->state.context)->draft = draft; + + return (PyObject*) decoder; +} +#endif diff --git a/display.c b/display.c new file mode 100644 index 000000000..1a08fadd5 --- /dev/null +++ b/display.c @@ -0,0 +1,836 @@ +/* + * The Python Imaging Library. + * + * display support (and other windows-related stuff) + * + * History: + * 1996-05-13 fl Windows DIB support + * 1996-05-21 fl Added palette stuff + * 1996-05-28 fl Added display_mode stuff + * 1997-09-21 fl Added draw primitive + * 2001-09-17 fl Added ImagingGrabScreen (from _grabscreen.c) + * 2002-05-12 fl Added ImagingListWindows + * 2002-11-19 fl Added clipboard support + * 2002-11-25 fl Added GetDC/ReleaseDC helpers + * 2003-05-21 fl Added create window support (including window callback) + * 2003-09-05 fl Added fromstring/tostring methods + * 2009-03-14 fl Added WMF support (from pilwmf) + * + * Copyright (c) 1997-2003 by Secret Labs AB. + * Copyright (c) 1996-1997 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Python.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#include "Imaging.h" + +/* -------------------------------------------------------------------- */ +/* Windows DIB support */ + +#ifdef WIN32 + +#include "ImDib.h" + +typedef struct { + PyObject_HEAD + ImagingDIB dib; +} ImagingDisplayObject; + +staticforward PyTypeObject ImagingDisplayType; + +static ImagingDisplayObject* +_new(const char* mode, int xsize, int ysize) +{ + ImagingDisplayObject *display; + + display = PyObject_New(ImagingDisplayObject, &ImagingDisplayType); + if (display == NULL) + return NULL; + + display->dib = ImagingNewDIB(mode, xsize, ysize); + if (!display->dib) { + Py_DECREF(display); + return NULL; + } + + return display; +} + +static void +_delete(ImagingDisplayObject* display) +{ + if (display->dib) + ImagingDeleteDIB(display->dib); + PyObject_Del(display); +} + +static PyObject* +_expose(ImagingDisplayObject* display, PyObject* args) +{ + int hdc; + if (!PyArg_ParseTuple(args, "i", &hdc)) + return NULL; + + ImagingExposeDIB(display->dib, hdc); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_draw(ImagingDisplayObject* display, PyObject* args) +{ + int hdc; + int dst[4]; + int src[4]; + if (!PyArg_ParseTuple(args, "i(iiii)(iiii)", &hdc, + dst+0, dst+1, dst+2, dst+3, + src+0, src+1, src+2, src+3)) + return NULL; + + ImagingDrawDIB(display->dib, hdc, dst, src); + + Py_INCREF(Py_None); + return Py_None; +} + +extern Imaging PyImaging_AsImaging(PyObject *op); + +static PyObject* +_paste(ImagingDisplayObject* display, PyObject* args) +{ + Imaging im; + + PyObject* op; + int xy[4]; + xy[0] = xy[1] = xy[2] = xy[3] = 0; + if (!PyArg_ParseTuple(args, "O|(iiii)", &op, xy+0, xy+1, xy+2, xy+3)) + return NULL; + im = PyImaging_AsImaging(op); + if (!im) + return NULL; + + if (xy[2] <= xy[0]) + xy[2] = xy[0] + im->xsize; + if (xy[3] <= xy[1]) + xy[3] = xy[1] + im->ysize; + + ImagingPasteDIB(display->dib, im, xy); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_query_palette(ImagingDisplayObject* display, PyObject* args) +{ + int hdc; + int status; + + if (!PyArg_ParseTuple(args, "i", &hdc)) + return NULL; + + status = ImagingQueryPaletteDIB(display->dib, hdc); + + return Py_BuildValue("i", status); +} + +static PyObject* +_getdc(ImagingDisplayObject* display, PyObject* args) +{ + int window; + HDC dc; + + if (!PyArg_ParseTuple(args, "i", &window)) + return NULL; + + dc = GetDC((HWND) window); + if (!dc) { + PyErr_SetString(PyExc_IOError, "cannot create dc"); + return NULL; + } + + return Py_BuildValue("i", (int) dc); +} + +static PyObject* +_releasedc(ImagingDisplayObject* display, PyObject* args) +{ + int window, dc; + + if (!PyArg_ParseTuple(args, "ii", &window, &dc)) + return NULL; + + ReleaseDC((HWND) window, (HDC) dc); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_fromstring(ImagingDisplayObject* display, PyObject* args) +{ + char* ptr; + int bytes; + if (!PyArg_ParseTuple(args, "s#:fromstring", &ptr, &bytes)) + return NULL; + + if (display->dib->ysize * display->dib->linesize != bytes) { + PyErr_SetString(PyExc_ValueError, "wrong size"); + return NULL; + } + + memcpy(display->dib->bits, ptr, bytes); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_tostring(ImagingDisplayObject* display, PyObject* args) +{ + if (!PyArg_ParseTuple(args, ":tostring")) + return NULL; + + return PyString_FromStringAndSize( + display->dib->bits, display->dib->ysize * display->dib->linesize + ); +} + +static struct PyMethodDef methods[] = { + {"draw", (PyCFunction)_draw, 1}, + {"expose", (PyCFunction)_expose, 1}, + {"paste", (PyCFunction)_paste, 1}, + {"query_palette", (PyCFunction)_query_palette, 1}, + {"getdc", (PyCFunction)_getdc, 1}, + {"releasedc", (PyCFunction)_releasedc, 1}, + {"fromstring", (PyCFunction)_fromstring, 1}, + {"tostring", (PyCFunction)_tostring, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_getattr(ImagingDisplayObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(methods, (PyObject*) self, name); + if (res) + return res; + PyErr_Clear(); + if (!strcmp(name, "mode")) + return Py_BuildValue("s", self->dib->mode); + if (!strcmp(name, "size")) + return Py_BuildValue("ii", self->dib->xsize, self->dib->ysize); + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +statichere PyTypeObject ImagingDisplayType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingDisplay", /*tp_name*/ + sizeof(ImagingDisplayObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_delete, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_hash*/ +}; + +PyObject* +PyImaging_DisplayWin32(PyObject* self, PyObject* args) +{ + ImagingDisplayObject* display; + char *mode; + int xsize, ysize; + + if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) + return NULL; + + display = _new(mode, xsize, ysize); + if (display == NULL) + return NULL; + + return (PyObject*) display; +} + +PyObject* +PyImaging_DisplayModeWin32(PyObject* self, PyObject* args) +{ + char *mode; + int size[2]; + + mode = ImagingGetModeDIB(size); + + return Py_BuildValue("s(ii)", mode, size[0], size[1]); +} + +/* -------------------------------------------------------------------- */ +/* Windows screen grabber */ + +PyObject* +PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) +{ + int width, height; + HBITMAP bitmap; + BITMAPCOREHEADER core; + HDC screen, screen_copy; + PyObject* buffer; + + /* step 1: create a memory DC large enough to hold the + entire screen */ + + screen = CreateDC("DISPLAY", NULL, NULL, NULL); + screen_copy = CreateCompatibleDC(screen); + + width = GetDeviceCaps(screen, HORZRES); + height = GetDeviceCaps(screen, VERTRES); + + bitmap = CreateCompatibleBitmap(screen, width, height); + if (!bitmap) + goto error; + + if (!SelectObject(screen_copy, bitmap)) + goto error; + + /* step 2: copy bits into memory DC bitmap */ + + if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, SRCCOPY)) + goto error; + + /* step 3: extract bits from bitmap */ + + buffer = PyString_FromStringAndSize(NULL, height * ((width*3 + 3) & -4)); + if (!buffer) + return NULL; + + core.bcSize = sizeof(core); + core.bcWidth = width; + core.bcHeight = height; + core.bcPlanes = 1; + core.bcBitCount = 24; + if (!GetDIBits(screen_copy, bitmap, 0, height, PyString_AS_STRING(buffer), + (BITMAPINFO*) &core, DIB_RGB_COLORS)) + goto error; + + DeleteObject(bitmap); + DeleteDC(screen_copy); + DeleteDC(screen); + + return Py_BuildValue("(ii)N", width, height, buffer); + +error: + PyErr_SetString(PyExc_IOError, "screen grab failed"); + + DeleteDC(screen_copy); + DeleteDC(screen); + + return NULL; +} + +static BOOL CALLBACK list_windows_callback(HWND hwnd, LPARAM lParam) +{ + PyObject* window_list = (PyObject*) lParam; + PyObject* item; + PyObject* title; + RECT inner, outer; + int title_size; + int status; + + /* get window title */ + title_size = GetWindowTextLength(hwnd); + if (title_size > 0) { + title = PyString_FromStringAndSize(NULL, title_size); + if (title) + GetWindowText(hwnd, PyString_AS_STRING(title), title_size+1); + } else + title = PyString_FromString(""); + if (!title) + return 0; + + /* get bounding boxes */ + GetClientRect(hwnd, &inner); + GetWindowRect(hwnd, &outer); + + item = Py_BuildValue( + "lN(iiii)(iiii)", (long) hwnd, title, + inner.left, inner.top, inner.right, inner.bottom, + outer.left, outer.top, outer.right, outer.bottom + ); + if (!item) + return 0; + + status = PyList_Append(window_list, item); + + Py_DECREF(item); + + if (status < 0) + return 0; + + return 1; +} + +PyObject* +PyImaging_ListWindowsWin32(PyObject* self, PyObject* args) +{ + PyObject* window_list; + + window_list = PyList_New(0); + if (!window_list) + return NULL; + + EnumWindows(list_windows_callback, (LPARAM) window_list); + + if (PyErr_Occurred()) { + Py_DECREF(window_list); + return NULL; + } + + return window_list; +} + +/* -------------------------------------------------------------------- */ +/* Windows clipboard grabber */ + +PyObject* +PyImaging_GrabClipboardWin32(PyObject* self, PyObject* args) +{ + int clip; + HANDLE handle; + int size; + void* data; + PyObject* result; + + int verbose = 0; /* debugging; will be removed in future versions */ + if (!PyArg_ParseTuple(args, "|i", &verbose)) + return NULL; + + + clip = OpenClipboard(NULL); + /* FIXME: check error status */ + + if (verbose) { + UINT format = EnumClipboardFormats(0); + char buffer[200]; + char* result; + while (format != 0) { + if (GetClipboardFormatName(format, buffer, sizeof buffer) > 0) + result = buffer; + else + switch (format) { + case CF_BITMAP: + result = "CF_BITMAP"; + break; + case CF_DIB: + result = "CF_DIB"; + break; + case CF_DIF: + result = "CF_DIF"; + break; + case CF_ENHMETAFILE: + result = "CF_ENHMETAFILE"; + break; + case CF_HDROP: + result = "CF_HDROP"; + break; + case CF_LOCALE: + result = "CF_LOCALE"; + break; + case CF_METAFILEPICT: + result = "CF_METAFILEPICT"; + break; + case CF_OEMTEXT: + result = "CF_OEMTEXT"; + break; + case CF_OWNERDISPLAY: + result = "CF_OWNERDISPLAY"; + break; + case CF_PALETTE: + result = "CF_PALETTE"; + break; + case CF_PENDATA: + result = "CF_PENDATA"; + break; + case CF_RIFF: + result = "CF_RIFF"; + break; + case CF_SYLK: + result = "CF_SYLK"; + break; + case CF_TEXT: + result = "CF_TEXT"; + break; + case CF_WAVE: + result = "CF_WAVE"; + break; + case CF_TIFF: + result = "CF_TIFF"; + break; + case CF_UNICODETEXT: + result = "CF_UNICODETEXT"; + break; + default: + sprintf(buffer, "[%d]", format); + result = buffer; + break; + } + printf("%s (%d)\n", result, format); + format = EnumClipboardFormats(format); + } + } + + handle = GetClipboardData(CF_DIB); + if (!handle) { + /* FIXME: add CF_HDROP support to allow cut-and-paste from + the explorer */ + CloseClipboard(); + Py_INCREF(Py_None); + return Py_None; + } + + size = GlobalSize(handle); + data = GlobalLock(handle); + +#if 0 + /* calculate proper size for string formats */ + if (format == CF_TEXT || format == CF_OEMTEXT) + size = strlen(data); + else if (format == CF_UNICODETEXT) + size = wcslen(data) * 2; +#endif + + result = PyString_FromStringAndSize(data, size); + + GlobalUnlock(handle); + + CloseClipboard(); + + return result; +} + +/* -------------------------------------------------------------------- */ +/* Windows class */ + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 522 +#endif + +static int mainloop = 0; + +static void +callback_error(const char* handler) +{ + PyObject* sys_stderr; + + sys_stderr = PySys_GetObject("stderr"); + + if (sys_stderr) { + PyFile_WriteString("*** ImageWin: error in ", sys_stderr); + PyFile_WriteString((char*) handler, sys_stderr); + PyFile_WriteString(":\n", sys_stderr); + } + + PyErr_Print(); + PyErr_Clear(); +} + +static LRESULT CALLBACK +windowCallback(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + PyObject* callback = NULL; + PyObject* result; + PyThreadState* threadstate; + PyThreadState* current_threadstate; + HDC dc; + RECT rect; + LRESULT status = 0; + + /* set up threadstate for messages that calls back into python */ + switch (message) { + case WM_CREATE: + mainloop++; + break; + case WM_DESTROY: + mainloop--; + /* fall through... */ + case WM_PAINT: + case WM_SIZE: + callback = (PyObject*) GetWindowLong(wnd, 0); + if (callback) { + threadstate = (PyThreadState*) + GetWindowLong(wnd, sizeof(PyObject*)); + current_threadstate = PyThreadState_Swap(NULL); + PyEval_RestoreThread(threadstate); + } else + return DefWindowProc(wnd, message, wParam, lParam); + } + + /* process message */ + switch (message) { + + case WM_PAINT: + /* redraw (part of) window. this generates a WCK-style + damage/clear/repair cascade */ + BeginPaint(wnd, &ps); + dc = GetDC(wnd); + GetWindowRect(wnd, &rect); /* in screen coordinates */ + + result = PyObject_CallFunction( + callback, "siiii", "damage", + ps.rcPaint.left, ps.rcPaint.top, + ps.rcPaint.right, ps.rcPaint.bottom + ); + if (result) + Py_DECREF(result); + else + callback_error("window damage callback"); + + result = PyObject_CallFunction( + callback, "siiiii", "clear", (int) dc, + 0, 0, rect.right-rect.left, rect.bottom-rect.top + ); + if (result) + Py_DECREF(result); + else + callback_error("window clear callback"); + + result = PyObject_CallFunction( + callback, "siiiii", "repair", (int) dc, + 0, 0, rect.right-rect.left, rect.bottom-rect.top + ); + if (result) + Py_DECREF(result); + else + callback_error("window repair callback"); + + ReleaseDC(wnd, dc); + EndPaint(wnd, &ps); + break; + + case WM_SIZE: + /* resize window */ + result = PyObject_CallFunction( + callback, "sii", "resize", LOWORD(lParam), HIWORD(lParam) + ); + if (result) { + InvalidateRect(wnd, NULL, 1); + Py_DECREF(result); + } else + callback_error("window resize callback"); + break; + + case WM_DESTROY: + /* destroy window */ + result = PyObject_CallFunction(callback, "s", "destroy"); + if (result) + Py_DECREF(result); + else + callback_error("window destroy callback"); + Py_DECREF(callback); + break; + + default: + status = DefWindowProc(wnd, message, wParam, lParam); + } + + if (callback) { + /* restore thread state */ + PyEval_SaveThread(); + PyThreadState_Swap(threadstate); + } + + return status; +} + +PyObject* +PyImaging_CreateWindowWin32(PyObject* self, PyObject* args) +{ + HWND wnd; + WNDCLASS windowClass; + + char* title; + PyObject* callback; + int width = 0, height = 0; + if (!PyArg_ParseTuple(args, "sO|ii", &title, &callback, &width, &height)) + return NULL; + + if (width <= 0) + width = CW_USEDEFAULT; + if (height <= 0) + height = CW_USEDEFAULT; + + /* register toplevel window class */ + windowClass.style = CS_CLASSDC; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = sizeof(PyObject*) + sizeof(PyThreadState*); + windowClass.hInstance = GetModuleHandle(NULL); + /* windowClass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); */ + windowClass.hbrBackground = NULL; + windowClass.lpszMenuName = NULL; + windowClass.lpszClassName = "pilWindow"; + windowClass.lpfnWndProc = windowCallback; + windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1)); + windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); /* CROSS? */ + + RegisterClass(&windowClass); /* FIXME: check return status */ + + wnd = CreateWindowEx( + 0, windowClass.lpszClassName, title, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, + HWND_DESKTOP, NULL, NULL, NULL + ); + + if (!wnd) { + PyErr_SetString(PyExc_IOError, "failed to create window"); + return NULL; + } + + /* register window callback */ + Py_INCREF(callback); + SetWindowLong(wnd, 0, (LONG) callback); + SetWindowLong(wnd, sizeof(callback), (LONG) PyThreadState_Get()); + + Py_BEGIN_ALLOW_THREADS + ShowWindow(wnd, SW_SHOWNORMAL); + SetForegroundWindow(wnd); /* to make sure it's visible */ + Py_END_ALLOW_THREADS + + return Py_BuildValue("l", (long) wnd); +} + +PyObject* +PyImaging_EventLoopWin32(PyObject* self, PyObject* args) +{ + MSG msg; + + Py_BEGIN_ALLOW_THREADS + while (mainloop && GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + +/* -------------------------------------------------------------------- */ +/* windows WMF renderer */ + +#define GET32(p,o) ((DWORD*)(p+o))[0] + +PyObject * +PyImaging_DrawWmf(PyObject* self, PyObject* args) +{ + HBITMAP bitmap; + HENHMETAFILE meta; + BITMAPCOREHEADER core; + HDC dc; + RECT rect; + PyObject* buffer = NULL; + char* ptr; + + char* data; + int datasize; + int width, height; + int x0, y0, x1, y1; + if (!PyArg_ParseTuple(args, "s#(ii)(iiii):_load", &data, &datasize, + &width, &height, &x0, &x1, &y0, &y1)) + return NULL; + + /* step 1: copy metafile contents into METAFILE object */ + + if (datasize > 22 && GET32(data, 0) == 0x9ac6cdd7) { + + /* placeable windows metafile (22-byte aldus header) */ + meta = SetWinMetaFileBits(datasize-22, data+22, NULL, NULL); + + } else if (datasize > 80 && GET32(data, 0) == 1 && + GET32(data, 40) == 0x464d4520) { + + /* enhanced metafile */ + meta = SetEnhMetaFileBits(datasize, data); + + } else { + + /* unknown meta format */ + meta = NULL; + + } + + if (!meta) { + PyErr_SetString(PyExc_IOError, "cannot load metafile"); + return NULL; + } + + /* step 2: create bitmap */ + + core.bcSize = sizeof(core); + core.bcWidth = width; + core.bcHeight = height; + core.bcPlanes = 1; + core.bcBitCount = 24; + + dc = CreateCompatibleDC(NULL); + + bitmap = CreateDIBSection( + dc, (BITMAPINFO*) &core, DIB_RGB_COLORS, &ptr, NULL, 0 + ); + + if (!bitmap) { + PyErr_SetString(PyExc_IOError, "cannot create bitmap"); + goto error; + } + + if (!SelectObject(dc, bitmap)) { + PyErr_SetString(PyExc_IOError, "cannot select bitmap"); + goto error; + } + + /* step 3: render metafile into bitmap */ + + rect.left = rect.top = 0; + rect.right = width; + rect.bottom = height; + + /* FIXME: make background transparent? configurable? */ + FillRect(dc, &rect, GetStockObject(WHITE_BRUSH)); + + if (!PlayEnhMetaFile(dc, meta, &rect)) { + PyErr_SetString(PyExc_IOError, "cannot render metafile"); + goto error; + } + + /* step 4: extract bits from bitmap */ + + GdiFlush(); + + buffer = PyString_FromStringAndSize(ptr, height * ((width*3 + 3) & -4)); + +error: + DeleteEnhMetaFile(meta); + + if (bitmap) + DeleteObject(bitmap); + + DeleteDC(dc); + + return buffer; +} + +#endif /* WIN32 */ diff --git a/doctest.py b/doctest.py new file mode 100644 index 000000000..e8140ecdf --- /dev/null +++ b/doctest.py @@ -0,0 +1,1111 @@ +# Module doctest version 0.9.6 +# Released to the public domain 16-Jan-2001, +# by Tim Peters (tim.one@home.com). + +# local modifications: +# 2001-02-13 fl: minor tweaks to make it run under both 1.5.2 and 2.0 + +# Provided as-is; use at your own risk; no warranty; no promises; enjoy! + +"""Module doctest -- a framework for running examples in docstrings. + +NORMAL USAGE + +In normal use, end each module M with: + +def _test(): + import doctest, M # replace M with your module's name + return doctest.testmod(M) # ditto + +if __name__ == "__main__": + _test() + +Then running the module as a script will cause the examples in the +docstrings to get executed and verified: + +python M.py + +This won't display anything unless an example fails, in which case the +failing example(s) and the cause(s) of the failure(s) are printed to stdout +(why not stderr? because stderr is a lame hack <0.2 wink>), and the final +line of output is "Test failed.". + +Run it with the -v switch instead: + +python M.py -v + +and a detailed report of all examples tried is printed to stdout, along +with assorted summaries at the end. + +You can force verbose mode by passing "verbose=1" to testmod, or prohibit +it by passing "verbose=0". In either of those cases, sys.argv is not +examined by testmod. + +In any case, testmod returns a 2-tuple of ints (f, t), where f is the +number of docstring examples that failed and t is the total number of +docstring examples attempted. + + +WHICH DOCSTRINGS ARE EXAMINED? + ++ M.__doc__. + ++ f.__doc__ for all functions f in M.__dict__.values(), except those + with private names. + ++ C.__doc__ for all classes C in M.__dict__.values(), except those with + private names. + ++ If M.__test__ exists and "is true", it must be a dict, and + each entry maps a (string) name to a function object, class object, or + string. Function and class object docstrings found from M.__test__ + are searched even if the name is private, and strings are searched + directly as if they were docstrings. In output, a key K in M.__test__ + appears with name + .__test__.K + +Any classes found are recursively searched similarly, to test docstrings in +their contained methods and nested classes. Private names reached from M's +globals are skipped, but all names reached from M.__test__ are searched. + +By default, a name is considered to be private if it begins with an +underscore (like "_my_func") but doesn't both begin and end with (at least) +two underscores (like "__init__"). You can change the default by passing +your own "isprivate" function to testmod. + +If you want to test docstrings in objects with private names too, stuff +them into an M.__test__ dict, or see ADVANCED USAGE below (e.g., pass your +own isprivate function to Tester's constructor, or call the rundoc method +of a Tester instance). + +Warning: imports can cause trouble; e.g., if you do + +from XYZ import XYZclass + +then XYZclass is a name in M.__dict__ too, and doctest has no way to know +that XYZclass wasn't *defined* in M. So it may try to execute the examples +in XYZclass's docstring, and those in turn may require a different set of +globals to work correctly. I prefer to do "import *"- friendly imports, +a la + +import XYY +_XYZclass = XYZ.XYZclass +del XYZ + +or (Python 2.0) + +from XYZ import XYZclass as _XYZclass + +and then the leading underscore stops testmod from going nuts. You may +prefer the method in the next section. + + +WHAT'S THE EXECUTION CONTEXT? + +By default, each time testmod finds a docstring to test, it uses a *copy* +of M's globals (so that running tests on a module doesn't change the +module's real globals, and so that one test in M can't leave behind crumbs +that accidentally allow another test to work). This means examples can +freely use any names defined at top-level in M. It also means that sloppy +imports (see above) can cause examples in external docstrings to use +globals inappropriate for them. + +You can force use of your own dict as the execution context by passing +"globs=your_dict" to testmod instead. Presumably this would be a copy of +M.__dict__ merged with the globals from other imported modules. + + +WHAT IF I WANT TO TEST A WHOLE PACKAGE? + +Piece o' cake, provided the modules do their testing from docstrings. +Here's the test.py I use for the world's most elaborate Rational/ +floating-base-conversion pkg (which I'll distribute some day): + +from Rational import Cvt +from Rational import Format +from Rational import machprec +from Rational import Rat +from Rational import Round +from Rational import utils + +modules = (Cvt, + Format, + machprec, + Rat, + Round, + utils) + +def _test(): + import doctest + import sys + verbose = "-v" in sys.argv + for mod in modules: + doctest.testmod(mod, verbose=verbose, report=0) + doctest.master.summarize() + +if __name__ == "__main__": + _test() + +IOW, it just runs testmod on all the pkg modules. testmod remembers the +names and outcomes (# of failures, # of tries) for each item it's seen, and +passing "report=0" prevents it from printing a summary in verbose mode. +Instead, the summary is delayed until all modules have been tested, and +then "doctest.master.summarize()" forces the summary at the end. + +So this is very nice in practice: each module can be tested individually +with almost no work beyond writing up docstring examples, and collections +of modules can be tested too as a unit with no more work than the above. + + +WHAT ABOUT EXCEPTIONS? + +No problem, as long as the only output generated by the example is the +traceback itself. For example: + + >>> a = [None] + >>> a[1] + Traceback (innermost last): + File "", line 1, in ? + IndexError: list index out of range + >>> + +Note that only the exception type and value are compared (specifically, +only the last line in the traceback). + + +ADVANCED USAGE + +doctest.testmod() captures the testing policy I find most useful most +often. You may want other policies. + +testmod() actually creates a local instance of class doctest.Tester, runs +appropriate methods of that class, and merges the results into global +Tester instance doctest.master. + +You can create your own instances of doctest.Tester, and so build your own +policies, or even run methods of doctest.master directly. See +doctest.Tester.__doc__ for details. + + +SO WHAT DOES A DOCSTRING EXAMPLE LOOK LIKE ALREADY!? + +Oh ya. It's easy! In most cases a copy-and-paste of an interactive +console session works fine -- just make sure the leading whitespace is +rigidly consistent (you can mix tabs and spaces if you're too lazy to do it +right, but doctest is not in the business of guessing what you think a tab +means). + + >>> # comments are ignored + >>> x = 12 + >>> x + 12 + >>> if x == 13: + ... print "yes" + ... else: + ... print "no" + ... print "NO" + ... print "NO!!!" + ... + no + NO + NO!!! + >>> + +Any expected output must immediately follow the final ">>>" or "..." line +containing the code, and the expected output (if any) extends to the next +">>>" or all-whitespace line. That's it. + +Bummers: + ++ Expected output cannot contain an all-whitespace line, since such a line + is taken to signal the end of expected output. + ++ Output to stdout is captured, but not output to stderr (exception + tracebacks are captured via a different means). + ++ If you continue a line via backslashing in an interactive session, or for + any other reason use a backslash, you need to double the backslash in the + docstring version. This is simply because you're in a string, and so the + backslash must be escaped for it to survive intact. Like: + +>>> if "yes" == \\ +... "y" + \\ +... "es": # in the source code you'll see the doubled backslashes +... print 'yes' +yes + +The starting column doesn't matter: + +>>> assert "Easy!" + >>> import math + >>> math.floor(1.9) + 1.0 + +and as many leading whitespace characters are stripped from the expected +output as appeared in the initial ">>>" line that triggered it. + +If you execute this very file, the examples above will be found and +executed, leading to this output in verbose mode: + +Running doctest.__doc__ +Trying: a = [None] +Expecting: nothing +ok +Trying: a[1] +Expecting: +Traceback (innermost last): + File "", line 1, in ? +IndexError: list index out of range +ok +Trying: x = 12 +Expecting: nothing +ok +Trying: x +Expecting: 12 +ok +Trying: +if x == 13: + print "yes" +else: + print "no" + print "NO" + print "NO!!!" +Expecting: +no +NO +NO!!! +ok +... and a bunch more like that, with this summary at the end: + +5 items had no tests: + doctest.Tester.__init__ + doctest.Tester.run__test__ + doctest.Tester.summarize + doctest.run_docstring_examples + doctest.testmod +12 items passed all tests: + 9 tests in doctest + 6 tests in doctest.Tester + 10 tests in doctest.Tester.merge + 7 tests in doctest.Tester.rundict + 3 tests in doctest.Tester.rundoc + 3 tests in doctest.Tester.runstring + 2 tests in doctest.__test__._TestClass + 2 tests in doctest.__test__._TestClass.__init__ + 2 tests in doctest.__test__._TestClass.get + 1 tests in doctest.__test__._TestClass.square + 2 tests in doctest.__test__.string + 7 tests in doctest.is_private +54 tests in 17 items. +54 passed and 0 failed. +Test passed. +""" + +# 0,0,1 06-Mar-1999 +# initial version posted +# 0,0,2 06-Mar-1999 +# loosened parsing: +# cater to stinkin' tabs +# don't insist on a blank after PS2 prefix +# so trailing "... " line from a compound stmt no longer +# breaks if the file gets whitespace-trimmed +# better error msgs for inconsistent leading whitespace +# 0,9,1 08-Mar-1999 +# exposed the Tester class and added client methods +# plus docstring examples of their use (eww - head-twisting!) +# fixed logic error in reporting total # of tests & failures +# added __test__ support to testmod (a pale reflection of Christian +# Tismer's vision ...) +# removed the "deep" argument; fiddle __test__ instead +# simplified endcase logic for extracting tests, and running them. +# before, if no output was expected but some was produced +# anyway via an eval'ed result, the discrepancy wasn't caught +# made TestClass private and used __test__ to get at it +# many doc updates +# speed _SpoofOut for long expected outputs +# 0,9,2 09-Mar-1999 +# throw out comments from examples, enabling use of the much simpler +# exec compile(... "single") ... +# for simulating the runtime; that barfs on comment-only lines +# used the traceback module to do a much better job of reporting +# exceptions +# run __doc__ values thru str(), "just in case" +# privateness of names now determined by an overridable "isprivate" +# function +# by default a name now considered to be private iff it begins with +# an underscore but doesn't both begin & end with two of 'em; so +# e.g. Class.__init__ etc are searched now -- as they always +# should have been +# 0,9,3 18-Mar-1999 +# added .flush stub to _SpoofOut (JPython buglet diagnosed by +# Hugh Emberson) +# repaired ridiculous docs about backslashes in examples +# minor internal changes +# changed source to Unix line-end conventions +# moved __test__ logic into new Tester.run__test__ method +# 0,9,4 27-Mar-1999 +# report item name and line # in failing examples +# 0,9,5 29-Jun-1999 +# allow straightforward exceptions in examples - thanks to Mark Hammond! +# 0,9,6 16-Jan-2001 +# fiddling for changes in Python 2.0: some of the embedded docstring +# examples no longer worked *exactly* as advertised, due to minor +# language changes, and running doctest on itself pointed that out. +# Hard to think of a better example of why this is useful . + +__version__ = 0, 9, 6 + +import types +_FunctionType = types.FunctionType +_ClassType = types.ClassType +_ModuleType = types.ModuleType +_StringType = types.StringType +del types + +import string +_string_find = string.find +_string_join = string.join +_string_split = string.split +_string_rindex = string.rindex +del string + +import re +PS1 = ">>>" +PS2 = "..." +_isPS1 = re.compile(r"(\s*)" + re.escape(PS1)).match +_isPS2 = re.compile(r"(\s*)" + re.escape(PS2)).match +_isEmpty = re.compile(r"\s*$").match +_isComment = re.compile(r"\s*#").match +del re + +__all__ = [] + +# Extract interactive examples from a string. Return a list of triples, +# (source, outcome, lineno). "source" is the source code, and ends +# with a newline iff the source spans more than one line. "outcome" is +# the expected output if any, else an empty string. When not empty, +# outcome always ends with a newline. "lineno" is the line number, +# 0-based wrt the start of the string, of the first source line. + +def _extract_examples(s): + isPS1, isPS2 = _isPS1, _isPS2 + isEmpty, isComment = _isEmpty, _isComment + examples = [] + lines = _string_split(s, "\n") + i, n = 0, len(lines) + while i < n: + line = lines[i] + i = i + 1 + m = isPS1(line) + if m is None: + continue + j = m.end(0) # beyond the prompt + if isEmpty(line, j) or isComment(line, j): + # a bare prompt or comment -- not interesting + continue + lineno = i - 1 + if line[j] != " ": + raise ValueError("line " + `lineno` + " of docstring lacks " + "blank after " + PS1 + ": " + line) + j = j + 1 + blanks = m.group(1) + nblanks = len(blanks) + # suck up this and following PS2 lines + source = [] + while 1: + source.append(line[j:]) + line = lines[i] + m = isPS2(line) + if m: + if m.group(1) != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + i = i + 1 + else: + break + if len(source) == 1: + source = source[0] + else: + # get rid of useless null line from trailing empty "..." + if source[-1] == "": + del source[-1] + source = _string_join(source, "\n") + "\n" + # suck up response + if isPS1(line) or isEmpty(line): + expect = "" + else: + expect = [] + while 1: + if line[:nblanks] != blanks: + raise ValueError("inconsistent leading whitespace " + "in line " + `i` + " of docstring: " + line) + expect.append(line[nblanks:]) + i = i + 1 + line = lines[i] + if isPS1(line) or isEmpty(line): + break + expect = _string_join(expect, "\n") + "\n" + examples.append( (source, expect, lineno) ) + return examples + +# Capture stdout when running examples. + +class _SpoofOut: + def __init__(self): + self.clear() + def write(self, s): + self.buf.append(s) + def get(self): + return _string_join(self.buf, "") + def clear(self): + self.buf = [] + def flush(self): + # JPython calls flush + pass + +# Display some tag-and-msg pairs nicely, keeping the tag and its msg +# on the same line when that makes sense. + +def _tag_out(printer, *tag_msg_pairs): + for tag, msg in tag_msg_pairs: + printer(tag + ":") + msg_has_nl = msg[-1:] == "\n" + msg_has_two_nl = msg_has_nl and \ + _string_find(msg, "\n") < len(msg) - 1 + if len(tag) + len(msg) < 76 and not msg_has_two_nl: + printer(" ") + else: + printer("\n") + printer(msg) + if not msg_has_nl: + printer("\n") + +# Run list of examples, in context globs. "out" can be used to display +# stuff to "the real" stdout, and fakeout is an instance of _SpoofOut +# that captures the examples' std output. Return (#failures, #tries). + +def _run_examples_inner(out, fakeout, examples, globs, verbose, name): + import sys, traceback + OK, BOOM, FAIL = range(3) + NADA = "nothing" + stderr = _SpoofOut() + failures = 0 + for source, want, lineno in examples: + if verbose: + _tag_out(out, ("Trying", source), + ("Expecting", want or NADA)) + fakeout.clear() + try: + exec compile(source, "", "single") in globs + got = fakeout.get() + state = OK + except: + # See whether the exception was expected. + if _string_find(want, "Traceback (innermost last):\n") == 0 or\ + _string_find(want, "Traceback (most recent call last):\n") == 0: + # Only compare exception type and value - the rest of + # the traceback isn't necessary. + want = _string_split(want, '\n')[-2] + '\n' + exc_type, exc_val, exc_tb = sys.exc_info() + got = traceback.format_exception_only(exc_type, exc_val)[0] + state = OK + else: + # unexpected exception + stderr.clear() + traceback.print_exc(file=stderr) + state = BOOM + + if state == OK: + if got == want: + if verbose: + out("ok\n") + continue + state = FAIL + + assert state in (FAIL, BOOM) + failures = failures + 1 + out("*" * 65 + "\n") + _tag_out(out, ("Failure in example", source)) + out("from line #" + `lineno` + " of " + name + "\n") + if state == FAIL: + _tag_out(out, ("Expected", want or NADA), ("Got", got)) + else: + assert state == BOOM + _tag_out(out, ("Exception raised", stderr.get())) + + return failures, len(examples) + +# Run list of examples, in context globs. Return (#failures, #tries). + +def _run_examples(examples, globs, verbose, name): + import sys + saveout = sys.stdout + try: + sys.stdout = fakeout = _SpoofOut() + x = _run_examples_inner(saveout.write, fakeout, examples, + globs, verbose, name) + finally: + sys.stdout = saveout + return x + +def run_docstring_examples(f, globs, verbose=0, name="NoName"): + """f, globs, verbose=0, name="NoName" -> run examples from f.__doc__. + + Use dict globs as the globals for execution. + Return (#failures, #tries). + + If optional arg verbose is true, print stuff even if there are no + failures. + Use string name in failure msgs. + """ + + try: + doc = f.__doc__ + if not doc: + # docstring empty or None + return 0, 0 + # just in case CT invents a doc object that has to be forced + # to look like a string <0.9 wink> + doc = str(doc) + except: + return 0, 0 + + e = _extract_examples(doc) + if not e: + return 0, 0 + return _run_examples(e, globs, verbose, name) + +def is_private(prefix, base): + """prefix, base -> true iff name prefix + "." + base is "private". + + Prefix may be an empty string, and base does not contain a period. + Prefix is ignored (although functions you write conforming to this + protocol may make use of it). + Return true iff base begins with an (at least one) underscore, but + does not both begin and end with (at least) two underscores. + + >>> is_private("a.b", "my_func") + 0 + >>> is_private("____", "_my_func") + 1 + >>> is_private("someclass", "__init__") + 0 + >>> is_private("sometypo", "__init_") + 1 + >>> is_private("x.y.z", "_") + 1 + >>> is_private("_x.y.z", "__") + 0 + >>> is_private("", "") # senseless but consistent + 0 + """ + + return base[:1] == "_" and not base[:2] == "__" == base[-2:] + +class Tester: + """Class Tester -- runs docstring examples and accumulates stats. + +In normal use, function doctest.testmod() hides all this from you, +so use that if you can. Create your own instances of Tester to do +fancier things. + +Methods: + runstring(s, name) + Search string s for examples to run; use name for logging. + Return (#failures, #tries). + + rundoc(object, name=None) + Search object.__doc__ for examples to run; use name (or + object.__name__) for logging. Return (#failures, #tries). + + rundict(d, name) + Search for examples in docstrings in all of d.values(); use name + for logging. Return (#failures, #tries). + + run__test__(d, name) + Treat dict d like module.__test__. Return (#failures, #tries). + + summarize(verbose=None) + Display summary of testing results, to stdout. Return + (#failures, #tries). + + merge(other) + Merge in the test results from Tester instance "other". + +>>> from doctest import Tester +>>> t = Tester(globs={'x': 42}, verbose=0) +>>> t.runstring(r''' +... >>> x = x * 2 +... >>> print x +... 42 +... ''', 'XYZ') +***************************************************************** +Failure in example: print x +from line #2 of XYZ +Expected: 42 +Got: 84 +(1, 2) +>>> t.runstring(">>> x = x * 2\\n>>> print x\\n84\\n", 'example2') +(0, 2) +>>> t.summarize() +1 items had failures: + 1 of 2 in XYZ +***Test Failed*** 1 failures. +(1, 4) +>>> t.summarize(verbose=1) +1 items passed all tests: + 2 tests in example2 +1 items had failures: + 1 of 2 in XYZ +4 tests in 2 items. +3 passed and 1 failed. +***Test Failed*** 1 failures. +(1, 4) +>>> +""" + + def __init__(self, mod=None, globs=None, verbose=None, + isprivate=None): + """mod=None, globs=None, verbose=None, isprivate=None + +See doctest.__doc__ for an overview. + +Optional keyword arg "mod" is a module, whose globals are used for +executing examples. If not specified, globs must be specified. + +Optional keyword arg "globs" gives a dict to be used as the globals +when executing examples; if not specified, use the globals from +module mod. + +In either case, a copy of the dict is used for each docstring +examined. + +Optional keyword arg "verbose" prints lots of stuff if true, only +failures if false; by default, it's true iff "-v" is in sys.argv. + +Optional keyword arg "isprivate" specifies a function used to determine +whether a name is private. The default function is doctest.is_private; +see its docs for details. +""" + + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and type(mod) is not _ModuleType: + raise TypeError("Tester.__init__: mod must be a module; " + + `mod`) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + if verbose is None: + import sys + verbose = "-v" in sys.argv + self.verbose = verbose + + if isprivate is None: + isprivate = is_private + self.isprivate = isprivate + + self.name2ft = {} # map name to (#failures, #trials) pair + + def runstring(self, s, name): + """ + s, name -> search string s for examples to run, logging as name. + + Use string name as the key for logging the outcome. + Return (#failures, #examples). + + >>> t = Tester(globs={}, verbose=1) + >>> test = r''' + ... # just an example + ... >>> x = 1 + 2 + ... >>> x + ... 3 + ... ''' + >>> t.runstring(test, "Example") + Running string Example + Trying: x = 1 + 2 + Expecting: nothing + ok + Trying: x + Expecting: 3 + ok + 0 of 2 examples failed in string Example + (0, 2) + """ + + if self.verbose: + print "Running string", name + f = t = 0 + e = _extract_examples(s) + if e: + f, t = _run_examples(e, self.globs.copy(), self.verbose, name) + if self.verbose: + print f, "of", t, "examples failed in string", name + self.__record_outcome(name, f, t) + return f, t + + def rundoc(self, object, name=None): + """ + object, name=None -> search object.__doc__ for examples to run. + + Use optional string name as the key for logging the outcome; + by default use object.__name__. + Return (#failures, #examples). + If object is a class object, search recursively for method + docstrings too. + object.__doc__ is examined regardless of name, but if object is + a class, whether private names reached from object are searched + depends on the constructor's "isprivate" argument. + + >>> t = Tester(globs={}, verbose=0) + >>> def _f(): + ... '''Trivial docstring example. + ... >>> assert 2 == 2 + ... ''' + ... return 32 + ... + >>> t.rundoc(_f) # expect 0 failures in 1 example + (0, 1) + """ + + if name is None: + try: + name = object.__name__ + except AttributeError: + raise ValueError("Tester.rundoc: name must be given " + "when object.__name__ doesn't exist; " + `object`) + if self.verbose: + print "Running", name + ".__doc__" + f, t = run_docstring_examples(object, self.globs.copy(), + self.verbose, name) + if self.verbose: + print f, "of", t, "examples failed in", name + ".__doc__" + self.__record_outcome(name, f, t) + if type(object) is _ClassType: + f2, t2 = self.rundict(object.__dict__, name) + f = f + f2 + t = t + t2 + return f, t + + def rundict(self, d, name): + """ + d. name -> search for docstring examples in all of d.values(). + + For k, v in d.items() such that v is a function or class, + do self.rundoc(v, name + "." + k). Whether this includes + objects with private names depends on the constructor's + "isprivate" argument. + Return aggregate (#failures, #examples). + + >>> def _f(): + ... '''>>> assert 1 == 1 + ... ''' + >>> def g(): + ... '''>>> assert 2 != 1 + ... ''' + >>> d = {"_f": _f, "g": g} + >>> t = Tester(globs={}, verbose=0) + >>> t.rundict(d, "rundict_test") # _f is skipped + (0, 1) + >>> t = Tester(globs={}, verbose=0, isprivate=lambda x,y: 0) + >>> t.rundict(d, "rundict_test_pvt") # both are searched + (0, 2) + """ + + if not hasattr(d, "items"): + raise TypeError("Tester.rundict: d must support .items(); " + + `d`) + f = t = 0 + for thisname, value in d.items(): + if type(value) in (_FunctionType, _ClassType): + f2, t2 = self.__runone(value, name + "." + thisname) + f = f + f2 + t = t + t2 + return f, t + + def run__test__(self, d, name): + """d, name -> Treat dict d like module.__test__. + + Return (#failures, #tries). + See testmod.__doc__ for details. + """ + + failures = tries = 0 + prefix = name + "." + savepvt = self.isprivate + try: + self.isprivate = lambda *args: 0 + for k, v in d.items(): + thisname = prefix + k + if type(v) is _StringType: + f, t = self.runstring(v, thisname) + elif type(v) in (_FunctionType, _ClassType): + f, t = self.rundoc(v, thisname) + else: + raise TypeError("Tester.run__test__: values in " + "dict must be strings, functions " + "or classes; " + `v`) + failures = failures + f + tries = tries + t + finally: + self.isprivate = savepvt + return failures, tries + + def summarize(self, verbose=None): + """ + verbose=None -> summarize results, return (#failures, #tests). + + Print summary of test results to stdout. + Optional arg 'verbose' controls how wordy this is. By + default, use the verbose setting established by the + constructor. + """ + + if verbose is None: + verbose = self.verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self.name2ft.items(): + name, (f, t) = x + assert f <= t + totalt = totalt + t + totalf = totalf + f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print len(notests), "items had no tests:" + notests.sort() + for thing in notests: + print " ", thing + if passed: + print len(passed), "items passed all tests:" + passed.sort() + for thing, count in passed: + print " %3d tests in %s" % (count, thing) + if failed: + print len(failed), "items had failures:" + failed.sort() + for thing, (f, t) in failed: + print " %3d of %3d in %s" % (f, t, thing) + if verbose: + print totalt, "tests in", len(self.name2ft), "items." + print totalt - totalf, "passed and", totalf, "failed." + if totalf: + print "***Test Failed***", totalf, "failures." + elif verbose: + print "Test passed." + return totalf, totalt + + def merge(self, other): + """ + other -> merge in test results from the other Tester instance. + + If self and other both have a test result for something + with the same name, the (#failures, #tests) results are + summed, and a warning is printed to stdout. + + >>> from doctest import Tester + >>> t1 = Tester(globs={}, verbose=0) + >>> t1.runstring(''' + ... >>> x = 12 + ... >>> print x + ... 12 + ... ''', "t1example") + (0, 2) + >>> + >>> t2 = Tester(globs={}, verbose=0) + >>> t2.runstring(''' + ... >>> x = 13 + ... >>> print x + ... 13 + ... ''', "t2example") + (0, 2) + >>> common = ">>> assert 1 + 2 == 3\\n" + >>> t1.runstring(common, "common") + (0, 1) + >>> t2.runstring(common, "common") + (0, 1) + >>> t1.merge(t2) + *** Tester.merge: 'common' in both testers; summing outcomes. + >>> t1.summarize(1) + 3 items passed all tests: + 2 tests in common + 2 tests in t1example + 2 tests in t2example + 6 tests in 3 items. + 6 passed and 0 failed. + Test passed. + (0, 6) + >>> + """ + + d = self.name2ft + for name, (f, t) in other.name2ft.items(): + if d.has_key(name): + print "*** Tester.merge: '" + name + "' in both" \ + " testers; summing outcomes." + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + + def __record_outcome(self, name, f, t): + if self.name2ft.has_key(name): + print "*** Warning: '" + name + "' was tested before;", \ + "summing outcomes." + f2, t2 = self.name2ft[name] + f = f + f2 + t = t + t2 + self.name2ft[name] = f, t + + def __runone(self, target, name): + if "." in name: + i = _string_rindex(name, ".") + prefix, base = name[:i], name[i+1:] + else: + prefix, base = "", base + if self.isprivate(prefix, base): + return 0, 0 + return self.rundoc(target, name) + +master = None + +def testmod(m, name=None, globs=None, verbose=None, isprivate=None, + report=1): + """m, name=None, globs=None, verbose=None, isprivate=None, report=1 + + Test examples in docstrings in functions and classes reachable from + module m, starting with m.__doc__. Private names are skipped. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__dict__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "isprivate" specifies a function used to + determine whether a name is private. The default function is + doctest.is_private; see its docs for details. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + + global master + + if type(m) is not _ModuleType: + raise TypeError("testmod: module required; " + `m`) + if name is None: + name = m.__name__ + tester = Tester(m, globs=globs, verbose=verbose, isprivate=isprivate) + failures, tries = tester.rundoc(m, name) + f, t = tester.rundict(m.__dict__, name) + failures = failures + f + tries = tries + t + if hasattr(m, "__test__"): + testdict = m.__test__ + if testdict: + if not hasattr(testdict, "items"): + raise TypeError("testmod: module.__test__ must support " + ".items(); " + `testdict`) + f, t = tester.run__test__(testdict, name + ".__test__") + failures = failures + f + tries = tries + t + if report: + tester.summarize() + if master is None: + master = tester + else: + master.merge(tester) + return failures, tries + +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print t.get() + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print x.get() + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """ + } + +def _test(): + import doctest + return doctest.testmod(doctest) + +if __name__ == "__main__": + _test() diff --git a/encode.c b/encode.c new file mode 100644 index 000000000..2ae13ad72 --- /dev/null +++ b/encode.c @@ -0,0 +1,540 @@ +/* + * The Python Imaging Library. + * + * standard encoder interfaces for the Imaging library + * + * History: + * 1996-04-19 fl Based on decoders.c + * 1996-05-12 fl Compile cleanly as C++ + * 1996-12-30 fl Plugged potential memory leak for tiled images + * 1997-01-03 fl Added GIF encoder + * 1997-01-05 fl Plugged encoder buffer leaks + * 1997-01-11 fl Added encode_to_file method + * 1998-03-09 fl Added mode/rawmode argument to encoders + * 1998-07-09 fl Added interlace argument to GIF encoder + * 1999-02-07 fl Added PCX encoder + * + * Copyright (c) 1997-2001 by Secret Labs AB + * Copyright (c) 1996-1997 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + +/* FIXME: make these pluggable! */ + +#include "Python.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#include "Imaging.h" +#include "Gif.h" + +#ifdef HAVE_UNISTD_H +#include /* write */ +#endif + +/* -------------------------------------------------------------------- */ +/* Common */ +/* -------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + int (*encode)(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); + struct ImagingCodecStateInstance state; + Imaging im; + PyObject* lock; +} ImagingEncoderObject; + +staticforward PyTypeObject ImagingEncoderType; + +static ImagingEncoderObject* +PyImaging_EncoderNew(int contextsize) +{ + ImagingEncoderObject *encoder; + void *context; + + ImagingEncoderType.ob_type = &PyType_Type; + + encoder = PyObject_New(ImagingEncoderObject, &ImagingEncoderType); + if (encoder == NULL) + return NULL; + + /* Clear the encoder state */ + memset(&encoder->state, 0, sizeof(encoder->state)); + + /* Allocate encoder context */ + if (contextsize > 0) { + context = (void*) calloc(1, contextsize); + if (!context) { + Py_DECREF(encoder); + (void) PyErr_NoMemory(); + return NULL; + } + } else + context = 0; + + /* Initialize encoder context */ + encoder->state.context = context; + + /* Target image */ + encoder->lock = NULL; + encoder->im = NULL; + + return encoder; +} + +static void +_dealloc(ImagingEncoderObject* encoder) +{ + free(encoder->state.buffer); + free(encoder->state.context); + Py_XDECREF(encoder->lock); + PyObject_Del(encoder); +} + +static PyObject* +_encode(ImagingEncoderObject* encoder, PyObject* args) +{ + PyObject* buf; + PyObject* result; + int status; + + /* Encode to a Python string (allocated by this method) */ + + int bufsize = 16384; + + if (!PyArg_ParseTuple(args, "|i", &bufsize)) + return NULL; + + buf = PyString_FromStringAndSize(NULL, bufsize); + if (!buf) + return NULL; + + status = encoder->encode(encoder->im, &encoder->state, + (UINT8*) PyString_AsString(buf), bufsize); + + /* adjust string length to avoid slicing in encoder */ + if (_PyString_Resize(&buf, (status > 0) ? status : 0) < 0) + return NULL; + + result = Py_BuildValue("iiO", status, encoder->state.errcode, buf); + + Py_DECREF(buf); /* must release buffer!!! */ + + return result; +} + +static PyObject* +_encode_to_file(ImagingEncoderObject* encoder, PyObject* args) +{ + UINT8* buf; + int status; + ImagingSectionCookie cookie; + + /* Encode to a file handle */ + + int fh; + int bufsize = 16384; + + if (!PyArg_ParseTuple(args, "i|i", &fh, &bufsize)) + return NULL; + + /* Allocate an encoder buffer */ + buf = (UINT8*) malloc(bufsize); + if (!buf) + return PyErr_NoMemory(); + + ImagingSectionEnter(&cookie); + + do { + + /* This replaces the inner loop in the ImageFile _save + function. */ + + status = encoder->encode(encoder->im, &encoder->state, buf, bufsize); + + if (status > 0) + if (write(fh, buf, status) < 0) { + ImagingSectionLeave(&cookie); + free(buf); + return PyErr_SetFromErrno(PyExc_IOError); + } + + } while (encoder->state.errcode == 0); + + ImagingSectionLeave(&cookie); + + free(buf); + + return Py_BuildValue("i", encoder->state.errcode); +} + +extern Imaging PyImaging_AsImaging(PyObject *op); + +static PyObject* +_setimage(ImagingEncoderObject* encoder, PyObject* args) +{ + PyObject* op; + Imaging im; + ImagingCodecState state; + int x0, y0, x1, y1; + + /* Define where image data should be stored */ + + x0 = y0 = x1 = y1 = 0; + + /* FIXME: should publish the ImagingType descriptor */ + if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) + return NULL; + im = PyImaging_AsImaging(op); + if (!im) + return NULL; + + encoder->im = im; + + state = &encoder->state; + + if (x0 == 0 && x1 == 0) { + state->xsize = im->xsize; + state->ysize = im->ysize; + } else { + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; + } + + if (state->xsize <= 0 || + state->xsize + state->xoff > im->xsize || + state->ysize <= 0 || + state->ysize + state->yoff > im->ysize) { + PyErr_SetString(PyExc_SystemError, "tile cannot extend outside image"); + return NULL; + } + + /* Allocate memory buffer (if bits field is set) */ + if (state->bits > 0) { + state->bytes = (state->bits * state->xsize+7)/8; + state->buffer = (UINT8*) malloc(state->bytes); + if (!state->buffer) + return PyErr_NoMemory(); + } + + /* Keep a reference to the image object, to make sure it doesn't + go away before we do */ + Py_INCREF(op); + Py_XDECREF(encoder->lock); + encoder->lock = op; + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef methods[] = { + {"encode", (PyCFunction)_encode, 1}, + {"encode_to_file", (PyCFunction)_encode_to_file, 1}, + {"setimage", (PyCFunction)_setimage, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_getattr(ImagingEncoderObject* self, char* name) +{ + return Py_FindMethod(methods, (PyObject*) self, name); +} + +statichere PyTypeObject ImagingEncoderType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingEncoder", /*tp_name*/ + sizeof(ImagingEncoderObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_hash*/ +}; + +/* -------------------------------------------------------------------- */ + +int +get_packer(ImagingEncoderObject* encoder, const char* mode, + const char* rawmode) +{ + int bits; + ImagingShuffler pack; + + pack = ImagingFindPacker(mode, rawmode, &bits); + if (!pack) { + Py_DECREF(encoder); + PyErr_SetString(PyExc_SystemError, "unknown raw mode"); + return -1; + } + + encoder->state.shuffle = pack; + encoder->state.bits = bits; + + return 0; +} + + +/* -------------------------------------------------------------------- */ +/* EPS */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_EpsEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + encoder = PyImaging_EncoderNew(0); + if (encoder == NULL) + return NULL; + + encoder->encode = ImagingEpsEncode; + + return (PyObject*) encoder; +} + + +/* -------------------------------------------------------------------- */ +/* GIF */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_GifEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char *mode; + char *rawmode; + int bits = 8; + int interlace = 0; + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits, &interlace)) + return NULL; + + encoder = PyImaging_EncoderNew(sizeof(GIFENCODERSTATE)); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, mode, rawmode) < 0) + return NULL; + + encoder->encode = ImagingGifEncode; + + ((GIFENCODERSTATE*)encoder->state.context)->bits = bits; + ((GIFENCODERSTATE*)encoder->state.context)->interlace = interlace; + + return (PyObject*) encoder; +} + + +/* -------------------------------------------------------------------- */ +/* PCX */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_PcxEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char *mode; + char *rawmode; + int bits = 8; + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &bits)) + return NULL; + + encoder = PyImaging_EncoderNew(0); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, mode, rawmode) < 0) + return NULL; + + encoder->encode = ImagingPcxEncode; + + return (PyObject*) encoder; +} + + +/* -------------------------------------------------------------------- */ +/* RAW */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_RawEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char *mode; + char *rawmode; + int stride = 0; + int ystep = 1; + + if (!PyArg_ParseTuple(args, "ss|ii", &mode, &rawmode, &stride, &ystep)) + return NULL; + + encoder = PyImaging_EncoderNew(0); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, mode, rawmode) < 0) + return NULL; + + encoder->encode = ImagingRawEncode; + + encoder->state.ystep = ystep; + encoder->state.count = stride; + + return (PyObject*) encoder; +} + + +/* -------------------------------------------------------------------- */ +/* XBM */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyImaging_XbmEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + encoder = PyImaging_EncoderNew(0); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, "1", "1;R") < 0) + return NULL; + + encoder->encode = ImagingXbmEncode; + + return (PyObject*) encoder; +} + + +/* -------------------------------------------------------------------- */ +/* ZIP */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBZ + +#include "Zip.h" + +PyObject* +PyImaging_ZipEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char* mode; + char* rawmode; + int optimize = 0; + char* dictionary = NULL; + int dictionary_size = 0; + if (!PyArg_ParseTuple(args, "ss|is#", &mode, &rawmode, &optimize, + &dictionary, &dictionary_size)) + return NULL; + + encoder = PyImaging_EncoderNew(sizeof(ZIPSTATE)); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, mode, rawmode) < 0) + return NULL; + + encoder->encode = ImagingZipEncode; + + if (rawmode[0] == 'P') + /* disable filtering */ + ((ZIPSTATE*)encoder->state.context)->mode = ZIP_PNG_PALETTE; + + ((ZIPSTATE*)encoder->state.context)->optimize = optimize; + ((ZIPSTATE*)encoder->state.context)->dictionary = dictionary; + ((ZIPSTATE*)encoder->state.context)->dictionary_size = dictionary_size; + + return (PyObject*) encoder; +} +#endif + + +/* -------------------------------------------------------------------- */ +/* JPEG */ +/* -------------------------------------------------------------------- */ + +#ifdef HAVE_LIBJPEG + +/* We better define this encoder last in this file, so the following + undef's won't mess things up for the Imaging library proper. */ + +#undef HAVE_PROTOTYPES +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT8 +#undef INT16 +#undef INT32 + +#include "Jpeg.h" + +PyObject* +PyImaging_JpegEncoderNew(PyObject* self, PyObject* args) +{ + ImagingEncoderObject* encoder; + + char *mode; + char *rawmode; + int quality = 0; + int progressive = 0; + int smooth = 0; + int optimize = 0; + int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */ + int xdpi = 0, ydpi = 0; + int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */ + char* extra = NULL; int extra_size; + if (!PyArg_ParseTuple(args, "ss|iiiiiiiis#", &mode, &rawmode, &quality, + &progressive, &smooth, &optimize, &streamtype, + &xdpi, &ydpi, &subsampling, &extra, &extra_size)) + return NULL; + + encoder = PyImaging_EncoderNew(sizeof(JPEGENCODERSTATE)); + if (encoder == NULL) + return NULL; + + if (get_packer(encoder, mode, rawmode) < 0) + return NULL; + + if (extra && extra_size > 0) { + char* p = malloc(extra_size); + if (!p) + return PyErr_NoMemory(); + memcpy(p, extra, extra_size); + extra = p; + } else + extra = NULL; + + encoder->encode = ImagingJpegEncode; + + ((JPEGENCODERSTATE*)encoder->state.context)->quality = quality; + ((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling; + ((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive; + ((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth; + ((JPEGENCODERSTATE*)encoder->state.context)->optimize = optimize; + ((JPEGENCODERSTATE*)encoder->state.context)->streamtype = streamtype; + ((JPEGENCODERSTATE*)encoder->state.context)->xdpi = xdpi; + ((JPEGENCODERSTATE*)encoder->state.context)->ydpi = ydpi; + ((JPEGENCODERSTATE*)encoder->state.context)->extra = extra; + ((JPEGENCODERSTATE*)encoder->state.context)->extra_size = extra_size; + + return (PyObject*) encoder; +} + +#endif diff --git a/libImaging/Access.c b/libImaging/Access.c new file mode 100644 index 000000000..5ebc9b6f3 --- /dev/null +++ b/libImaging/Access.c @@ -0,0 +1,255 @@ +/* + * The Python Imaging Library + * $Id$ + * + * imaging access objects + * + * Copyright (c) Fredrik Lundh 2009. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +/* use Tests/make_hash.py to calculate these values */ +#define ACCESS_TABLE_SIZE 21 +#define ACCESS_TABLE_HASH 30197 + +static struct ImagingAccessInstance access_table[ACCESS_TABLE_SIZE]; + +static inline UINT32 +hash(const char* mode) +{ + UINT32 i = ACCESS_TABLE_HASH; + while (*mode) + i = ((i<<5) + i) ^ (UINT8) *mode++; + return i % ACCESS_TABLE_SIZE; +} + +static ImagingAccess +add_item(const char* mode) +{ + UINT32 i = hash(mode); + /* printf("hash %s => %d\n", mode, i); */ + if (access_table[i].mode) { + fprintf(stderr, "AccessInit: hash collision: %d for both %s and %s\n", + i, mode, access_table[i].mode); + exit(1); + } + access_table[i].mode = mode; + return &access_table[i]; +} + +/* fetch pointer to pixel line */ + +static void* +line_8(Imaging im, int x, int y) +{ + return &im->image8[y][x]; +} + +static void* +line_16(Imaging im, int x, int y) +{ + return &im->image8[y][x+x]; +} + +static void* +line_32(Imaging im, int x, int y) +{ + return &im->image32[y][x]; +} + +/* fetch individual pixel */ + +static void +get_pixel(Imaging im, int x, int y, void* color) +{ + char* out = color; + + /* generic pixel access*/ + + if (im->image8) { + out[0] = im->image8[y][x]; + } else { + UINT8* p = (UINT8*) &im->image32[y][x]; + if (im->type == IMAGING_TYPE_UINT8 && im->bands == 2) { + out[0] = p[0]; + out[1] = p[3]; + return; + } + memcpy(out, p, im->pixelsize); + } +} + +static void +get_pixel_8(Imaging im, int x, int y, void* color) +{ + char* out = color; + out[0] = im->image8[y][x]; +} + +static void +get_pixel_16L(Imaging im, int x, int y, void* color) +{ + UINT8* in = (UINT8*) &im->image[y][x+x]; + INT16* out = color; +#ifdef WORDS_BIGENDIAN + out[0] = in[0] + (in[1]<<8); +#else + out[0] = *(INT16*) in; +#endif +} + +static void +get_pixel_16B(Imaging im, int x, int y, void* color) +{ + UINT8* in = (UINT8*) &im->image[y][x+x]; + INT16* out = color; +#ifdef WORDS_BIGENDIAN + out[0] = *(INT16*) in; +#else + out[0] = in[1] + (in[0]<<8); +#endif +} + +static void +get_pixel_32(Imaging im, int x, int y, void* color) +{ + INT32* out = color; + out[0] = im->image32[y][x]; +} + +static void +get_pixel_32L(Imaging im, int x, int y, void* color) +{ + UINT8* in = (UINT8*) &im->image[y][x*4]; + INT32* out = color; +#ifdef WORDS_BIGENDIAN + out[0] = in[0] + (in[1]<<8) + (in[2]<<16) + (in[3]<<24); +#else + out[0] = *(INT32*) in; +#endif +} + +static void +get_pixel_32B(Imaging im, int x, int y, void* color) +{ + UINT8* in = (UINT8*) &im->image[y][x*4]; + INT32* out = color; +#ifdef WORDS_BIGENDIAN + out[0] = *(INT32*) in; +#else + out[0] = in[3] + (in[2]<<8) + (in[1]<<16) + (in[0]<<24); +#endif +} + +/* store individual pixel */ + +static void +put_pixel(Imaging im, int x, int y, const void* color) +{ + if (im->image8) + im->image8[y][x] = *((UINT8*) color); + else + im->image32[y][x] = *((INT32*) color); +} + +static void +put_pixel_8(Imaging im, int x, int y, const void* color) +{ + im->image8[y][x] = *((UINT8*) color); +} + +static void +put_pixel_16L(Imaging im, int x, int y, const void* color) +{ + const char* in = color; + UINT8* out = (UINT8*) &im->image8[y][x+x]; + out[0] = in[0]; + out[1] = in[1]; +} + +static void +put_pixel_16B(Imaging im, int x, int y, const void* color) +{ + const char* in = color; + UINT8* out = (UINT8*) &im->image8[y][x+x]; + out[0] = in[1]; + out[1] = in[0]; +} + +static void +put_pixel_32L(Imaging im, int x, int y, const void* color) +{ + const char* in = color; + UINT8* out = (UINT8*) &im->image8[y][x*4]; + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +static void +put_pixel_32B(Imaging im, int x, int y, const void* color) +{ + const char* in = color; + UINT8* out = (UINT8*) &im->image8[y][x*4]; + out[0] = in[3]; + out[1] = in[2]; + out[2] = in[1]; + out[3] = in[0]; +} + +static void +put_pixel_32(Imaging im, int x, int y, const void* color) +{ + im->image32[y][x] = *((INT32*) color); +} + +void +ImagingAccessInit() +{ +#define ADD(mode_, line_, get_pixel_, put_pixel_) \ + { ImagingAccess access = add_item(mode_); \ + access->line = line_; \ + access->get_pixel = get_pixel_; \ + access->put_pixel = put_pixel_; \ + } + + /* populate access table */ + ADD("1", line_8, get_pixel_8, put_pixel_8); + ADD("L", line_8, get_pixel_8, put_pixel_8); + ADD("LA", line_32, get_pixel, put_pixel); + ADD("I", line_32, get_pixel_32, put_pixel_32); + ADD("I;16", line_16, get_pixel_16L, put_pixel_16L); + ADD("I;16L", line_16, get_pixel_16L, put_pixel_16L); + ADD("I;16B", line_16, get_pixel_16B, put_pixel_16B); + ADD("I;32L", line_32, get_pixel_32L, put_pixel_32L); + ADD("I;32B", line_32, get_pixel_32B, put_pixel_32B); + ADD("F", line_32, get_pixel_32, put_pixel_32); + ADD("P", line_8, get_pixel_8, put_pixel_8); + ADD("PA", line_32, get_pixel, put_pixel); + ADD("RGB", line_32, get_pixel_32, put_pixel_32); + ADD("RGBA", line_32, get_pixel_32, put_pixel_32); + ADD("RGBa", line_32, get_pixel_32, put_pixel_32); + ADD("RGBX", line_32, get_pixel_32, put_pixel_32); + ADD("CMYK", line_32, get_pixel_32, put_pixel_32); + ADD("YCbCr", line_32, get_pixel_32, put_pixel_32); +} + +ImagingAccess +ImagingAccessNew(Imaging im) +{ + ImagingAccess access = &access_table[hash(im->mode)]; + if (im->mode[0] != access->mode[0] || strcmp(im->mode, access->mode) != 0) + return NULL; + return access; +} + +void +_ImagingAccessDelete(Imaging im, ImagingAccess access) +{ + +} diff --git a/libImaging/Antialias.c b/libImaging/Antialias.c new file mode 100644 index 000000000..53dfa3522 --- /dev/null +++ b/libImaging/Antialias.c @@ -0,0 +1,307 @@ +/* + * The Python Imaging Library + * $Id$ + * + * pilopen antialiasing support + * + * history: + * 2002-03-09 fl Created (for PIL 1.1.3) + * 2002-03-10 fl Added support for mode "F" + * + * Copyright (c) 1997-2002 by Secret Labs AB + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +#include + +/* resampling filters (from antialias.py) */ + +struct filter { + float (*filter)(float x); + float support; +}; + +static inline float sinc_filter(float x) +{ + if (x == 0.0) + return 1.0; + x = x * M_PI; + return sin(x) / x; +} + +static inline float antialias_filter(float x) +{ + /* lanczos (truncated sinc) */ + if (-3.0 <= x && x < 3.0) + return sinc_filter(x) * sinc_filter(x/3); + return 0.0; +} + +static struct filter ANTIALIAS = { antialias_filter, 3.0 }; + +static inline float nearest_filter(float x) +{ + if (-0.5 <= x && x < 0.5) + return 1.0; + return 0.0; +} + +static struct filter NEAREST = { nearest_filter, 0.5 }; + +static inline float bilinear_filter(float x) +{ + if (x < 0.0) + x = -x; + if (x < 1.0) + return 1.0-x; + return 0.0; +} + +static struct filter BILINEAR = { bilinear_filter, 1.0 }; + +static inline float bicubic_filter(float x) +{ + /* FIXME: double-check this algorithm */ + /* FIXME: for best results, "a" should be -0.5 to -1.0, but we'll + set it to zero for now, to match the 1.1 magnifying filter */ +#define a 0.0 + if (x < 0.0) + x = -x; + if (x < 1.0) + return (((a + 2.0) * x) - (a + 3.0)) * x*x + 1; + if (x < 2.0) + return (((a * x) - 5*a) * x + 8) * x - 4*a; + return 0.0; +#undef a +} + +static struct filter BICUBIC = { bicubic_filter, 2.0 }; + +Imaging +ImagingStretch(Imaging imOut, Imaging imIn, int filter) +{ + /* FIXME: this is a quick and straightforward translation from a + python prototype. might need some further C-ification... */ + + ImagingSectionCookie cookie; + struct filter *filterp; + float support, scale, filterscale; + float center, ww, ss, ymin, ymax, xmin, xmax; + int xx, yy, x, y, b; + float *k; + + /* check modes */ + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + + /* check filter */ + switch (filter) { + case IMAGING_TRANSFORM_NEAREST: + filterp = &NEAREST; + break; + case IMAGING_TRANSFORM_ANTIALIAS: + filterp = &ANTIALIAS; + break; + case IMAGING_TRANSFORM_BILINEAR: + filterp = &BILINEAR; + break; + case IMAGING_TRANSFORM_BICUBIC: + filterp = &BICUBIC; + break; + default: + return (Imaging) ImagingError_ValueError( + "unsupported resampling filter" + ); + } + + if (imIn->ysize == imOut->ysize) { + /* prepare for horizontal stretch */ + filterscale = scale = (float) imIn->xsize / imOut->xsize; + } else if (imIn->xsize == imOut->xsize) { + /* prepare for vertical stretch */ + filterscale = scale = (float) imIn->ysize / imOut->ysize; + } else + return (Imaging) ImagingError_Mismatch(); + + /* determine support size (length of resampling filter) */ + support = filterp->support; + + if (filterscale < 1.0) { + filterscale = 1.0; + support = 0.5; + } + + support = support * filterscale; + + /* coefficient buffer (with rounding safety margin) */ + k = malloc(((int) support * 2 + 10) * sizeof(float)); + if (!k) + return (Imaging) ImagingError_MemoryError(); + + ImagingSectionEnter(&cookie); + if (imIn->xsize == imOut->xsize) { + /* vertical stretch */ + for (yy = 0; yy < imOut->ysize; yy++) { + center = (yy + 0.5) * scale; + ww = 0.0; + ss = 1.0 / filterscale; + /* calculate filter weights */ + ymin = floor(center - support); + if (ymin < 0.0) + ymin = 0.0; + ymax = ceil(center + support); + if (ymax > (float) imIn->ysize) + ymax = (float) imIn->ysize; + for (y = (int) ymin; y < (int) ymax; y++) { + float w = filterp->filter((y - center + 0.5) * ss) * ss; + k[y - (int) ymin] = w; + ww = ww + w; + } + if (ww == 0.0) + ww = 1.0; + else + ww = 1.0 / ww; + if (imIn->image8) { + /* 8-bit grayscale */ + for (xx = 0; xx < imOut->xsize; xx++) { + ss = 0.0; + for (y = (int) ymin; y < (int) ymax; y++) + ss = ss + imIn->image8[y][xx] * k[y - (int) ymin]; + ss = ss * ww + 0.5; + if (ss < 0.5) + imOut->image8[yy][xx] = 0; + else if (ss >= 255.0) + imOut->image8[yy][xx] = 255; + else + imOut->image8[yy][xx] = (UINT8) ss; + } + } else + switch(imIn->type) { + case IMAGING_TYPE_UINT8: + /* n-bit grayscale */ + for (xx = 0; xx < imOut->xsize*4; xx++) { + /* FIXME: skip over unused pixels */ + ss = 0.0; + for (y = (int) ymin; y < (int) ymax; y++) + ss = ss + (UINT8) imIn->image[y][xx] * k[y-(int) ymin]; + ss = ss * ww + 0.5; + if (ss < 0.5) + imOut->image[yy][xx] = (UINT8) 0; + else if (ss >= 255.0) + imOut->image[yy][xx] = (UINT8) 255; + else + imOut->image[yy][xx] = (UINT8) ss; + } + break; + case IMAGING_TYPE_INT32: + /* 32-bit integer */ + for (xx = 0; xx < imOut->xsize; xx++) { + ss = 0.0; + for (y = (int) ymin; y < (int) ymax; y++) + ss = ss + IMAGING_PIXEL_I(imIn, xx, y) * k[y - (int) ymin]; + IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww; + } + break; + case IMAGING_TYPE_FLOAT32: + /* 32-bit float */ + for (xx = 0; xx < imOut->xsize; xx++) { + ss = 0.0; + for (y = (int) ymin; y < (int) ymax; y++) + ss = ss + IMAGING_PIXEL_F(imIn, xx, y) * k[y - (int) ymin]; + IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww; + } + break; + default: + ImagingSectionLeave(&cookie); + return (Imaging) ImagingError_ModeError(); + } + } + } else { + /* horizontal stretch */ + for (xx = 0; xx < imOut->xsize; xx++) { + center = (xx + 0.5) * scale; + ww = 0.0; + ss = 1.0 / filterscale; + xmin = floor(center - support); + if (xmin < 0.0) + xmin = 0.0; + xmax = ceil(center + support); + if (xmax > (float) imIn->xsize) + xmax = (float) imIn->xsize; + for (x = (int) xmin; x < (int) xmax; x++) { + float w = filterp->filter((x - center + 0.5) * ss) * ss; + k[x - (int) xmin] = w; + ww = ww + w; + } + if (ww == 0.0) + ww = 1.0; + else + ww = 1.0 / ww; + if (imIn->image8) { + /* 8-bit grayscale */ + for (yy = 0; yy < imOut->ysize; yy++) { + ss = 0.0; + for (x = (int) xmin; x < (int) xmax; x++) + ss = ss + imIn->image8[yy][x] * k[x - (int) xmin]; + ss = ss * ww + 0.5; + if (ss < 0.5) + imOut->image8[yy][xx] = (UINT8) 0; + else if (ss >= 255.0) + imOut->image8[yy][xx] = (UINT8) 255; + else + imOut->image8[yy][xx] = (UINT8) ss; + } + } else + switch(imIn->type) { + case IMAGING_TYPE_UINT8: + /* n-bit grayscale */ + for (yy = 0; yy < imOut->ysize; yy++) { + for (b = 0; b < imIn->bands; b++) { + if (imIn->bands == 2 && b) + b = 3; /* hack to deal with LA images */ + ss = 0.0; + for (x = (int) xmin; x < (int) xmax; x++) + ss = ss + (UINT8) imIn->image[yy][x*4+b] * k[x - (int) xmin]; + ss = ss * ww + 0.5; + if (ss < 0.5) + imOut->image[yy][xx*4+b] = (UINT8) 0; + else if (ss >= 255.0) + imOut->image[yy][xx*4+b] = (UINT8) 255; + else + imOut->image[yy][xx*4+b] = (UINT8) ss; + } + } + break; + case IMAGING_TYPE_INT32: + /* 32-bit integer */ + for (yy = 0; yy < imOut->ysize; yy++) { + ss = 0.0; + for (x = (int) xmin; x < (int) xmax; x++) + ss = ss + IMAGING_PIXEL_I(imIn, x, yy) * k[x - (int) xmin]; + IMAGING_PIXEL_I(imOut, xx, yy) = (int) ss * ww; + } + break; + case IMAGING_TYPE_FLOAT32: + /* 32-bit float */ + for (yy = 0; yy < imOut->ysize; yy++) { + ss = 0.0; + for (x = (int) xmin; x < (int) xmax; x++) + ss = ss + IMAGING_PIXEL_F(imIn, x, yy) * k[x - (int) xmin]; + IMAGING_PIXEL_F(imOut, xx, yy) = ss * ww; + } + break; + default: + ImagingSectionLeave(&cookie); + return (Imaging) ImagingError_ModeError(); + } + } + } + ImagingSectionLeave(&cookie); + + free(k); + + return imOut; +} diff --git a/libImaging/Bands.c b/libImaging/Bands.c new file mode 100644 index 000000000..fdfc6ae95 --- /dev/null +++ b/libImaging/Bands.c @@ -0,0 +1,129 @@ +/* + * The Python Imaging Library + * $Id$ + * + * stuff to extract and paste back individual bands + * + * history: + * 1996-03-20 fl Created + * 1997-08-27 fl Fixed putband for single band targets. + * 2003-09-26 fl Fixed getband/putband for 2-band images (LA, PA). + * + * Copyright (c) 1997-2003 by Secret Labs AB. + * Copyright (c) 1996-1997 by Fredrik Lundh. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + + +#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) + + +Imaging +ImagingGetBand(Imaging imIn, int band) +{ + Imaging imOut; + int x, y; + + /* Check arguments */ + if (!imIn || imIn->type != IMAGING_TYPE_UINT8) + return (Imaging) ImagingError_ModeError(); + + if (band < 0 || band >= imIn->bands) + return (Imaging) ImagingError_ValueError("band index out of range"); + + /* Shortcuts */ + if (imIn->bands == 1) + return ImagingCopy(imIn); + + /* Special case for LXXA etc */ + if (imIn->bands == 2 && band == 1) + band = 3; + + imOut = ImagingNew("L", imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + + /* Extract band from image */ + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y] + band; + UINT8* out = imOut->image8[y]; + for (x = 0; x < imIn->xsize; x++) { + out[x] = *in; + in += 4; + } + } + + return imOut; +} + +Imaging +ImagingPutBand(Imaging imOut, Imaging imIn, int band) +{ + int x, y; + + /* Check arguments */ + if (!imIn || imIn->bands != 1 || !imOut) + return (Imaging) ImagingError_ModeError(); + + if (band < 0 || band >= imOut->bands) + return (Imaging) ImagingError_ValueError("band index out of range"); + + if (imIn->type != imOut->type || + imIn->xsize != imOut->xsize || + imIn->ysize != imOut->ysize) + return (Imaging) ImagingError_Mismatch(); + + /* Shortcuts */ + if (imOut->bands == 1) + return ImagingCopy2(imOut, imIn); + + /* Special case for LXXA etc */ + if (imOut->bands == 2 && band == 1) + band = 3; + + /* Insert band into image */ + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = imIn->image8[y]; + UINT8* out = (UINT8*) imOut->image[y] + band; + for (x = 0; x < imIn->xsize; x++) { + *out = in[x]; + out += 4; + } + } + + return imOut; +} + +Imaging +ImagingFillBand(Imaging imOut, int band, int color) +{ + int x, y; + + /* Check arguments */ + if (!imOut || imOut->type != IMAGING_TYPE_UINT8) + return (Imaging) ImagingError_ModeError(); + + if (band < 0 || band >= imOut->bands) + return (Imaging) ImagingError_ValueError("band index out of range"); + + /* Special case for LXXA etc */ + if (imOut->bands == 2 && band == 1) + band = 3; + + color = CLIP(color); + + /* Insert color into image */ + for (y = 0; y < imOut->ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y] + band; + for (x = 0; x < imOut->xsize; x++) { + *out = (UINT8) color; + out += 4; + } + } + + return imOut; +} diff --git a/libImaging/Bit.h b/libImaging/Bit.h new file mode 100644 index 000000000..56e3a17d2 --- /dev/null +++ b/libImaging/Bit.h @@ -0,0 +1,30 @@ +/* Bit.h */ + +typedef struct { + + /* CONFIGURATION */ + + /* Number of bits per pixel */ + int bits; + + /* Line padding (0 or 8) */ + int pad; + + /* Fill order */ + /* 0=msb/msb, 1=msbfill/lsbshift, 2=lsbfill/msbshift, 3=lsb/lsb */ + int fill; + + /* Signed integers (0=unsigned, 1=signed) */ + int sign; + + /* Lookup table (not implemented) */ + unsigned long lutsize; + FLOAT32* lut; + + /* INTERNAL */ + unsigned long mask; + unsigned long signmask; + unsigned long bitbuffer; + int bitcount; + +} BITSTATE; diff --git a/libImaging/BitDecode.c b/libImaging/BitDecode.c new file mode 100644 index 000000000..572926f00 --- /dev/null +++ b/libImaging/BitDecode.c @@ -0,0 +1,138 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for packed bitfields (converts to floating point) + * + * history: + * 97-05-31 fl created (much more than originally intended) + * + * Copyright (c) Fredrik Lundh 1997. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include "Bit.h" + + +int +ImagingBitDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + BITSTATE* bitstate = state->context; + UINT8* ptr; + + if (state->state == 0) { + + /* Initialize context variables */ + + /* this decoder only works for float32 image buffers */ + if (im->type != IMAGING_TYPE_FLOAT32) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + /* sanity check */ + if (bitstate->bits < 1 || bitstate->bits >= 32) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + bitstate->mask = (1<bits)-1; + + if (bitstate->sign) + bitstate->signmask = (1<<(bitstate->bits-1)); + + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; + + state->state = 1; + + } + + ptr = buf; + + while (bytes > 0) { + + UINT8 byte = *ptr; + + ptr++; + bytes--; + + /* get a byte from the input stream and insert in the bit buffer */ + if (bitstate->fill&1) + /* fill MSB first */ + bitstate->bitbuffer |= (unsigned long) byte << bitstate->bitcount; + else + /* fill LSB first */ + bitstate->bitbuffer = (bitstate->bitbuffer << 8) | byte; + + bitstate->bitcount += 8; + + while (bitstate->bitcount >= bitstate->bits) { + + /* get a pixel from the bit buffer */ + unsigned long data; + FLOAT32 pixel; + + if (bitstate->fill&2) { + /* store LSB first */ + data = bitstate->bitbuffer & bitstate->mask; + if (bitstate->bitcount > 32) + /* bitbuffer overflow; restore it from last input byte */ + bitstate->bitbuffer = byte >> (8 - (bitstate->bitcount - + bitstate->bits)); + else + bitstate->bitbuffer >>= bitstate->bits; + } else + /* store MSB first */ + data = (bitstate->bitbuffer >> (bitstate->bitcount - + bitstate->bits)) + & bitstate->mask; + + bitstate->bitcount -= bitstate->bits; + + if (bitstate->lutsize > 0) { + /* map through lookup table */ + if (data <= 0) + pixel = bitstate->lut[0]; + else if (data >= bitstate->lutsize) + pixel = bitstate->lut[bitstate->lutsize-1]; + else + pixel = bitstate->lut[data]; + } else { + /* convert */ + if (data & bitstate->signmask) + /* image memory contains signed data */ + pixel = (FLOAT32) (INT32) (data | ~bitstate->mask); + else + pixel = (FLOAT32) data; + } + + *(FLOAT32*)(&im->image32[state->y][state->x]) = pixel; + + /* step forward */ + if (++state->x >= state->xsize) { + /* new line */ + state->y += state->ystep; + if (state->y < 0 || state->y >= state->ysize) { + /* end of file (errcode = 0) */ + return -1; + } + state->x = 0; + /* reset bit buffer */ + if (bitstate->pad > 0) + bitstate->bitcount = 0; + } + } + } + + return ptr - buf; +} diff --git a/libImaging/Blend.c b/libImaging/Blend.c new file mode 100644 index 000000000..0861c8ef4 --- /dev/null +++ b/libImaging/Blend.c @@ -0,0 +1,79 @@ +/* + * The Python Imaging Library + * $Id$ + * + * interpolate between two existing images + * + * history: + * 96-03-20 fl Created + * 96-05-18 fl Simplified blend expression + * 96-10-05 fl Fixed expression bug, special case for interpolation + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + + +Imaging +ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha) +{ + Imaging imOut; + int x, y; + + /* Check arguments */ + if (!imIn1 || !imIn2 || imIn1->type != IMAGING_TYPE_UINT8) + return ImagingError_ModeError(); + if (imIn1->type != imIn2->type || + imIn1->bands != imIn2->bands || + imIn1->xsize != imIn2->xsize || + imIn1->ysize != imIn2->ysize) + return ImagingError_Mismatch(); + + /* Shortcuts */ + if (alpha == 0.0) + return ImagingCopy(imIn1); + else if (alpha == 1.0) + return ImagingCopy(imIn2); + + imOut = ImagingNew(imIn1->mode, imIn1->xsize, imIn1->ysize); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, imIn1); + + if (alpha >= 0 && alpha <= 1.0) { + /* Interpolate between bands */ + for (y = 0; y < imIn1->ysize; y++) { + UINT8* in1 = (UINT8*) imIn1->image[y]; + UINT8* in2 = (UINT8*) imIn2->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn1->linesize; x++) + out[x] = (UINT8) + ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); + } + } else { + /* Extrapolation; must make sure to clip resulting values */ + for (y = 0; y < imIn1->ysize; y++) { + UINT8* in1 = (UINT8*) imIn1->image[y]; + UINT8* in2 = (UINT8*) imIn2->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn1->linesize; x++) { + float temp = (float) + ((int) in1[x] + alpha * ((int) in2[x] - (int) in1[x])); + if (temp <= 0.0) + out[x] = 0; + else if (temp >= 255.0) + out[x] = 255; + else + out[x] = (UINT8) temp; + } + } + } + + return imOut; +} diff --git a/libImaging/Chops.c b/libImaging/Chops.c new file mode 100644 index 000000000..e5993195a --- /dev/null +++ b/libImaging/Chops.c @@ -0,0 +1,148 @@ +/* + * The Python Imaging Library + * $Id$ + * + * basic channel operations + * + * history: + * 1996-03-28 fl Created + * 1996-08-13 fl Added and/or/xor for "1" images + * 1996-12-14 fl Added add_modulo, sub_modulo + * 2005-09-10 fl Fixed output values from and/or/xor + * + * Copyright (c) 1996 by Fredrik Lundh. + * Copyright (c) 1997 by Secret Labs AB. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + +#define CHOP(operation, mode)\ + int x, y;\ + Imaging imOut;\ + imOut = create(imIn1, imIn2, mode);\ + if (!imOut)\ + return NULL;\ + for (y = 0; y < imOut->ysize; y++) {\ + UINT8* out = (UINT8*) imOut->image[y];\ + UINT8* in1 = (UINT8*) imIn1->image[y];\ + UINT8* in2 = (UINT8*) imIn2->image[y];\ + for (x = 0; x < imOut->linesize; x++) {\ + int temp = operation;\ + if (temp <= 0)\ + out[x] = 0;\ + else if (temp >= 255)\ + out[x] = 255;\ + else\ + out[x] = temp;\ + }\ + }\ + return imOut; + +#define CHOP2(operation, mode)\ + int x, y;\ + Imaging imOut;\ + imOut = create(imIn1, imIn2, mode);\ + if (!imOut)\ + return NULL;\ + for (y = 0; y < imOut->ysize; y++) {\ + UINT8* out = (UINT8*) imOut->image[y];\ + UINT8* in1 = (UINT8*) imIn1->image[y];\ + UINT8* in2 = (UINT8*) imIn2->image[y];\ + for (x = 0; x < imOut->linesize; x++) {\ + out[x] = operation;\ + }\ + }\ + return imOut; + +static Imaging +create(Imaging im1, Imaging im2, char* mode) +{ + int xsize, ysize; + + if (!im1 || !im2 || im1->type != IMAGING_TYPE_UINT8 || + (mode != NULL && (strcmp(im1->mode, "1") || strcmp(im2->mode, "1")))) + return (Imaging) ImagingError_ModeError(); + if (im1->type != im2->type || + im1->bands != im2->bands) + return (Imaging) ImagingError_Mismatch(); + + xsize = (im1->xsize < im2->xsize) ? im1->xsize : im2->xsize; + ysize = (im1->ysize < im2->ysize) ? im1->ysize : im2->ysize; + + return ImagingNew(im1->mode, xsize, ysize); +} + +Imaging +ImagingChopLighter(Imaging imIn1, Imaging imIn2) +{ + CHOP((in1[x] > in2[x]) ? in1[x] : in2[x], NULL); +} + +Imaging +ImagingChopDarker(Imaging imIn1, Imaging imIn2) +{ + CHOP((in1[x] < in2[x]) ? in1[x] : in2[x], NULL); +} + +Imaging +ImagingChopDifference(Imaging imIn1, Imaging imIn2) +{ + CHOP(abs((int) in1[x] - (int) in2[x]), NULL); +} + +Imaging +ImagingChopMultiply(Imaging imIn1, Imaging imIn2) +{ + CHOP((int) in1[x] * (int) in2[x] / 255, NULL); +} + +Imaging +ImagingChopScreen(Imaging imIn1, Imaging imIn2) +{ + CHOP(255 - ((int) (255 - in1[x]) * (int) (255 - in2[x])) / 255, NULL); +} + +Imaging +ImagingChopAdd(Imaging imIn1, Imaging imIn2, float scale, int offset) +{ + CHOP(((int) in1[x] + (int) in2[x]) / scale + offset, NULL); +} + +Imaging +ImagingChopSubtract(Imaging imIn1, Imaging imIn2, float scale, int offset) +{ + CHOP(((int) in1[x] - (int) in2[x]) / scale + offset, NULL); +} + +Imaging +ImagingChopAnd(Imaging imIn1, Imaging imIn2) +{ + CHOP2((in1[x] && in2[x]) ? 255 : 0, "1"); +} + +Imaging +ImagingChopOr(Imaging imIn1, Imaging imIn2) +{ + CHOP2((in1[x] || in2[x]) ? 255 : 0, "1"); +} + +Imaging +ImagingChopXor(Imaging imIn1, Imaging imIn2) +{ + CHOP2(((in1[x] != 0) ^ (in2[x] != 0)) ? 255 : 0, "1"); +} + +Imaging +ImagingChopAddModulo(Imaging imIn1, Imaging imIn2) +{ + CHOP2(in1[x] + in2[x], NULL); +} + +Imaging +ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2) +{ + CHOP2(in1[x] - in2[x], NULL); +} diff --git a/libImaging/Convert.c b/libImaging/Convert.c new file mode 100644 index 000000000..25299aa82 --- /dev/null +++ b/libImaging/Convert.c @@ -0,0 +1,1132 @@ +/* + * The Python Imaging Library + * $Id$ + * + * convert images + * + * history: + * 1995-06-15 fl created + * 1995-11-28 fl added some "RGBA" and "CMYK" conversions + * 1996-04-22 fl added "1" conversions (same as "L") + * 1996-05-05 fl added palette conversions (hack) + * 1996-07-23 fl fixed "1" conversions to zero/non-zero convention + * 1996-11-01 fl fixed "P" to "L" and "RGB" to "1" conversions + * 1996-12-29 fl set alpha byte in RGB converters + * 1997-05-12 fl added ImagingConvert2 + * 1997-05-30 fl added floating point support + * 1997-08-27 fl added "P" to "1" and "P" to "F" conversions + * 1998-01-11 fl added integer support + * 1998-07-01 fl added "YCbCr" support + * 1998-07-02 fl added "RGBX" conversions (sort of) + * 1998-07-04 fl added floyd-steinberg dithering + * 1998-07-12 fl changed "YCrCb" to "YCbCr" (!) + * 1998-12-29 fl added basic "I;16" and "I;16B" conversions + * 1999-02-03 fl added "RGBa", and "BGR" conversions (experimental) + * 2003-09-26 fl added "LA" and "PA" conversions (experimental) + * 2005-05-05 fl fixed "P" to "1" threshold + * 2005-12-08 fl fixed palette memory leak in topalette + * + * Copyright (c) 1997-2005 by Secret Labs AB. + * Copyright (c) 1995-1997 by Fredrik Lundh. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + +#define CLIP(v) ((v) <= 0 ? 0 : (v) >= 255 ? 255 : (v)) +#define CLIP16(v) ((v) <= -32768 ? -32768 : (v) >= 32767 ? 32767 : (v)) + +/* like (a * b + 127) / 255), but much faster on most platforms */ +#define MULDIV255(a, b, tmp)\ + (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) + +/* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ +#define L(rgb)\ + ((INT32) (rgb)[0]*299 + (INT32) (rgb)[1]*587 + (INT32) (rgb)[2]*114) + +/* ------------------- */ +/* 1 (bit) conversions */ +/* ------------------- */ + +static void +bit2l(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) + *out++ = (*in++ != 0) ? 255 : 0; +} + +static void +bit2rgb(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + UINT8 v = (*in++ != 0) ? 255 : 0; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; + } +} + +static void +bit2cmyk(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = (*in++ != 0) ? 0 : 255; + } +} + +static void +bit2ycbcr(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = (*in++ != 0) ? 255 : 0; + *out++ = 128; + *out++ = 128; + *out++ = 255; + } +} + +/* ----------------- */ +/* RGB/L conversions */ +/* ----------------- */ + +static void +l2bit(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) + *out++ = (*in++ >= 128) ? 255 : 0; +} + +static void +l2la(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + UINT8 v = *in++; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; + } +} + +static void +l2rgb(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + UINT8 v = *in++; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = 255; + } +} + +static void +la2l(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) + *out++ = in[0]; +} + +static void +la2rgb(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) { + UINT8 v = in[0]; + *out++ = v; + *out++ = v; + *out++ = v; + *out++ = in[3]; + } +} + +static void +rgb2bit(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + *out++ = (L(in) >= 128000) ? 255 : 0; +} + +static void +rgb2l(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + *out++ = L(in) / 1000; +} + +static void +rgb2la(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4, out += 4) { + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + out[0] = out[1] = out[2] = L(in) / 1000; + out[3] = 255; + } +} + +static void +rgb2i(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++, in += 4) + *out++ = L(in) / 1000; +} + +static void +rgb2f(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++, in += 4) + *out++ = (float) L(in) / 1000.0F; +} + +static void +rgb2bgr15(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + UINT16* out = (UINT16*) out_; + for (x = 0; x < xsize; x++, in += 4) + *out++ = + ((((UINT16)in[0])<<7)&0x7c00) + + ((((UINT16)in[1])<<2)&0x03e0) + + ((((UINT16)in[2])>>3)&0x001f); +} + +static void +rgb2bgr16(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + UINT16* out = (UINT16*) out_; + for (x = 0; x < xsize; x++, in += 4) + *out++ = + ((((UINT16)in[0])<<8)&0xf800) + + ((((UINT16)in[1])<<3)&0x07e0) + + ((((UINT16)in[2])>>3)&0x001f); +} + +static void +rgb2bgr24(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + } +} + +/* ---------------- */ +/* RGBA conversions */ +/* ---------------- */ + +static void +rgb2rgba(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = 255; in++; + } +} + +static void +rgba2la(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4, out += 4) { + /* ITU-R Recommendation 601-2 (assuming nonlinear RGB) */ + out[0] = out[1] = out[2] = L(in) / 1000; + out[3] = in[3]; + } +} + +static void +rgba2rgb(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = 255; in++; + } +} + +static void +rgba2rgba(UINT8* out, const UINT8* in, int xsize) +{ + int x; + unsigned int alpha, tmp; + for (x = 0; x < xsize; x++) { + alpha = in[3]; + *out++ = MULDIV255(*in++, alpha, tmp); + *out++ = MULDIV255(*in++, alpha, tmp); + *out++ = MULDIV255(*in++, alpha, tmp); + *out++ = *in++; + } +} + +/* ---------------- */ +/* CMYK conversions */ +/* ---------------- */ + +static void +l2cmyk(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = ~(*in++); + } +} + +static void +rgb2cmyk(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + /* Note: no undercolour removal */ + *out++ = ~(*in++); + *out++ = ~(*in++); + *out++ = ~(*in++); + *out++ = 0; in++; + } +} + +static void +cmyk2rgb(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) { + *out++ = CLIP(255 - (in[0] + in[3])); + *out++ = CLIP(255 - (in[1] + in[3])); + *out++ = CLIP(255 - (in[2] + in[3])); + *out++ = 255; + } +} + +/* ------------- */ +/* I conversions */ +/* ------------- */ + +static void +bit2i(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (*in++ != 0) ? 255 : 0; +} + +static void +l2i(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (INT32) *in++; +} + +static void +i2l(UINT8* out, const UINT8* in_, int xsize) +{ + int x; + INT32* in = (INT32*) in_; + for (x = 0; x < xsize; x++, in++, out++) { + if (*in <= 0) + *out = 0; + else if (*in >= 255) + *out = 255; + else + *out = (UINT8) *in; + } +} + +static void +i2f(UINT8* out_, const UINT8* in_, int xsize) +{ + int x; + INT32* in = (INT32*) in_; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (FLOAT32) *in++; +} + +/* ------------- */ +/* F conversions */ +/* ------------- */ + +static void +bit2f(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (*in++ != 0) ? 255.0F : 0.0F; +} + +static void +l2f(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (FLOAT32) *in++; +} + +static void +f2l(UINT8* out, const UINT8* in_, int xsize) +{ + int x; + FLOAT32* in = (FLOAT32*) in_; + for (x = 0; x < xsize; x++, in++, out++) { + if (*in <= 0.0) + *out = 0; + else if (*in >= 255.0) + *out = 255; + else + *out = (UINT8) *in; + } +} + +static void +f2i(UINT8* out_, const UINT8* in_, int xsize) +{ + int x; + FLOAT32* in = (FLOAT32*) in_; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (INT32) *in++; +} + +/* ----------------- */ +/* YCbCr conversions */ +/* ----------------- */ + +/* See ConvertYCbCr.c for RGB/YCbCr tables */ + +static void +l2ycbcr(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++) { + *out++ = *in++; + *out++ = 128; + *out++ = 128; + *out++ = 255; + } +} + +static void +ycbcr2l(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) + *out++ = in[0]; +} + +/* ------------------------- */ +/* I;16 (16-bit) conversions */ +/* ------------------------- */ + +static void +I_I16L(UINT8* out, const UINT8* in_, int xsize) +{ + int x, v; + INT32* in = (INT32*) in_; + for (x = 0; x < xsize; x++, in++) { + v = CLIP16(*in); + *out++ = (UINT8) v; + *out++ = (UINT8) (v >> 8); + } +} + +static void +I_I16B(UINT8* out, const UINT8* in_, int xsize) +{ + int x, v; + INT32* in = (INT32*) in_; + for (x = 0; x < xsize; x++, in++) { + v = CLIP16(*in); + *out++ = (UINT8) (v >> 8); + *out++ = (UINT8) v; + } +} + + +static void +I16L_I(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++, in += 2) + *out++ = in[0] + ((int) in[1] << 8); +} + + +static void +I16B_I(UINT8* out_, const UINT8* in, int xsize) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++, in += 2) + *out++ = in[1] + ((int) in[0] << 8); +} + +static void +L_I16L(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in++) { + *out++ = *in; + *out++ = 0; + } +} + +static void +L_I16B(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in++) { + *out++ = 0; + *out++ = *in; + } +} + +static void +I16L_L(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 2) + if (in[1] != 0) + *out++ = 255; + else + *out++ = in[0]; +} + +static void +I16B_L(UINT8* out, const UINT8* in, int xsize) +{ + int x; + for (x = 0; x < xsize; x++, in += 2) + if (in[0] != 0) + *out++ = 255; + else + *out++ = in[1]; +} + +static struct { + const char* from; + const char* to; + ImagingShuffler convert; +} converters[] = { + + { "1", "L", bit2l }, + { "1", "I", bit2i }, + { "1", "F", bit2f }, + { "1", "RGB", bit2rgb }, + { "1", "RGBA", bit2rgb }, + { "1", "RGBX", bit2rgb }, + { "1", "CMYK", bit2cmyk }, + { "1", "YCbCr", bit2ycbcr }, + + { "L", "1", l2bit }, + { "L", "LA", l2la }, + { "L", "I", l2i }, + { "L", "F", l2f }, + { "L", "RGB", l2rgb }, + { "L", "RGBA", l2rgb }, + { "L", "RGBX", l2rgb }, + { "L", "CMYK", l2cmyk }, + { "L", "YCbCr", l2ycbcr }, + + { "LA", "L", la2l }, + { "LA", "RGB", la2rgb }, + { "LA", "RGBX", la2rgb }, + { "LA", "RGBA", la2rgb }, + + { "I", "L", i2l }, + { "I", "F", i2f }, + + { "F", "L", f2l }, + { "F", "I", f2i }, + + { "RGB", "1", rgb2bit }, + { "RGB", "L", rgb2l }, + { "RGB", "LA", rgb2la }, + { "RGB", "I", rgb2i }, + { "RGB", "F", rgb2f }, + { "RGB", "BGR;15", rgb2bgr15 }, + { "RGB", "BGR;16", rgb2bgr16 }, + { "RGB", "BGR;24", rgb2bgr24 }, + { "RGB", "RGBA", rgb2rgba }, + { "RGB", "RGBX", rgb2rgba }, + { "RGB", "CMYK", rgb2cmyk }, + { "RGB", "YCbCr", ImagingConvertRGB2YCbCr }, + + { "RGBA", "1", rgb2bit }, + { "RGBA", "L", rgb2l }, + { "RGBA", "LA", rgba2la }, + { "RGBA", "I", rgb2i }, + { "RGBA", "F", rgb2f }, + { "RGBA", "RGB", rgba2rgb }, + { "RGBA", "RGBa", rgba2rgba }, + { "RGBA", "RGBX", rgb2rgba }, + { "RGBA", "CMYK", rgb2cmyk }, + { "RGBA", "YCbCr", ImagingConvertRGB2YCbCr }, + + { "RGBX", "1", rgb2bit }, + { "RGBX", "L", rgb2l }, + { "RGBA", "I", rgb2i }, + { "RGBA", "F", rgb2f }, + { "RGBX", "RGB", rgba2rgb }, + { "RGBX", "CMYK", rgb2cmyk }, + { "RGBX", "YCbCr", ImagingConvertRGB2YCbCr }, + + { "CMYK", "RGB", cmyk2rgb }, + { "CMYK", "RGBA", cmyk2rgb }, + { "CMYK", "RGBX", cmyk2rgb }, + + { "YCbCr", "L", ycbcr2l }, + { "YCbCr", "RGB", ImagingConvertYCbCr2RGB }, + + { "I", "I;16", I_I16L }, + { "I;16", "I", I16L_I }, + { "L", "I;16", L_I16L }, + { "I;16", "L", I16L_L }, + + { "I", "I;16L", I_I16L }, + { "I;16L", "I", I16L_I }, + { "I", "I;16B", I_I16B }, + { "I;16B", "I", I16B_I }, + + { "L", "I;16L", L_I16L }, + { "I;16L", "L", I16L_L }, + { "L", "I;16B", L_I16B }, + { "I;16B", "L", I16B_L }, + + { NULL } +}; + +/* FIXME: translate indexed versions to pointer versions below this line */ + +/* ------------------- */ +/* Palette conversions */ +/* ------------------- */ + +static void +p2bit(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + /* FIXME: precalculate greyscale palette? */ + for (x = 0; x < xsize; x++) + *out++ = (L(&palette[in[x]*4]) >= 128000) ? 255 : 0; +} + +static void +p2l(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + /* FIXME: precalculate greyscale palette? */ + for (x = 0; x < xsize; x++) + *out++ = L(&palette[in[x]*4]) / 1000; +} + +static void +pa2la(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + /* FIXME: precalculate greyscale palette? */ + for (x = 0; x < xsize; x++, in += 2) { + *out++ = L(&palette[in[0]*4]) / 1000; + *out++ = in[1]; + } +} + +static void +p2i(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + INT32* out = (INT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = L(&palette[in[x]*4]) / 1000; +} + +static void +p2f(UINT8* out_, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + FLOAT32* out = (FLOAT32*) out_; + for (x = 0; x < xsize; x++) + *out++ = (float) L(&palette[in[x]*4]) / 1000.0F; +} + +static void +p2rgb(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + for (x = 0; x < xsize; x++) { + const UINT8* rgb = &palette[*in++ * 4]; + *out++ = rgb[0]; + *out++ = rgb[1]; + *out++ = rgb[2]; + *out++ = 255; + } +} + +static void +p2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + for (x = 0; x < xsize; x++) { + const UINT8* rgba = &palette[*in++ * 4]; + *out++ = rgba[0]; + *out++ = rgba[1]; + *out++ = rgba[2]; + *out++ = rgba[3]; + } +} + +static void +pa2rgba(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + int x; + for (x = 0; x < xsize; x++, in += 4) { + const UINT8* rgb = &palette[in[0] * 4]; + *out++ = rgb[0]; + *out++ = rgb[1]; + *out++ = rgb[2]; + *out++ = in[3]; + } +} + +static void +p2cmyk(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + p2rgb(out, in, xsize, palette); + rgb2cmyk(out, out, xsize); +} + +static void +p2ycbcr(UINT8* out, const UINT8* in, int xsize, const UINT8* palette) +{ + p2rgb(out, in, xsize, palette); + ImagingConvertRGB2YCbCr(out, out, xsize); +} + +static Imaging +frompalette(Imaging imOut, Imaging imIn, const char *mode) +{ + ImagingSectionCookie cookie; + int alpha; + int y; + void (*convert)(UINT8*, const UINT8*, int, const UINT8*); + + /* Map palette image to L, RGB, RGBA, or CMYK */ + + if (!imIn->palette) + return (Imaging) ImagingError_ValueError("no palette"); + + alpha = !strcmp(imIn->mode, "PA"); + + if (strcmp(mode, "1") == 0) + convert = p2bit; + else if (strcmp(mode, "L") == 0) + convert = p2l; + else if (strcmp(mode, "LA") == 0) + convert = (alpha) ? pa2la : p2l; + else if (strcmp(mode, "I") == 0) + convert = p2i; + else if (strcmp(mode, "F") == 0) + convert = p2f; + else if (strcmp(mode, "RGB") == 0) + convert = p2rgb; + else if (strcmp(mode, "RGBA") == 0) + convert = (alpha) ? pa2rgba : p2rgba; + else if (strcmp(mode, "RGBX") == 0) + convert = p2rgba; + else if (strcmp(mode, "CMYK") == 0) + convert = p2cmyk; + else if (strcmp(mode, "YCbCr") == 0) + convert = p2ycbcr; + else + return (Imaging) ImagingError_ValueError("conversion not supported"); + + imOut = ImagingNew2(mode, imOut, imIn); + if (!imOut) + return NULL; + + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize, imIn->palette->palette); + ImagingSectionLeave(&cookie); + + return imOut; +} + +static Imaging +topalette(Imaging imOut, Imaging imIn, ImagingPalette inpalette, int dither) +{ + ImagingSectionCookie cookie; + int x, y; + ImagingPalette palette = inpalette;; + + /* Map L or RGB/RGBX/RGBA to palette image */ + if (strcmp(imIn->mode, "L") != 0 && strncmp(imIn->mode, "RGB", 3) != 0) + return (Imaging) ImagingError_ValueError("conversion not supported"); + + if (palette == NULL) { + /* FIXME: make user configurable */ + if (imIn->bands == 1) + palette = ImagingPaletteNew("RGB"); /* Initialised to grey ramp */ + else + palette = ImagingPaletteNewBrowser(); /* Standard colour cube */ + } + + if (!palette) + return (Imaging) ImagingError_ValueError("no palette"); + + imOut = ImagingNew2("P", imOut, imIn); + if (!imOut) { + if (palette != inpalette) + ImagingPaletteDelete(palette); + return NULL; + } + + ImagingPaletteDelete(imOut->palette); + imOut->palette = ImagingPaletteDuplicate(palette); + + if (imIn->bands == 1) { + /* greyscale image */ + + /* Greyscale palette: copy data as is */ + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) + memcpy(imOut->image[y], imIn->image[y], imIn->linesize); + ImagingSectionLeave(&cookie); + + } else { + /* colour image */ + + /* Create mapping cache */ + if (ImagingPaletteCachePrepare(palette) < 0) { + ImagingDelete(imOut); + if (palette != inpalette) + ImagingPaletteDelete(palette); + return NULL; + } + + if (dither) { + /* floyd-steinberg dither */ + + int* errors; + errors = calloc(imIn->xsize + 1, sizeof(int) * 3); + if (!errors) { + ImagingDelete(imOut); + return ImagingError_MemoryError(); + } + + /* Map each pixel to the nearest palette entry */ + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + int r, r0, r1, r2; + int g, g0, g1, g2; + int b, b0, b1, b2; + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = imOut->image8[y]; + int* e = errors; + + r = r0 = r1 = 0; + g = g0 = g1 = 0; + b = b0 = b1 = b2 = 0; + + for (x = 0; x < imIn->xsize; x++, in += 4) { + int d2; + INT16* cache; + + r = CLIP(in[0] + (r + e[3+0])/16); + g = CLIP(in[1] + (g + e[3+1])/16); + b = CLIP(in[2] + (b + e[3+2])/16); + + /* get closest colour */ + cache = &ImagingPaletteCache(palette, r, g, b); + if (cache[0] == 0x100) + ImagingPaletteCacheUpdate(palette, r, g, b); + out[x] = (UINT8) cache[0]; + + r -= (int) palette->palette[cache[0]*4]; + g -= (int) palette->palette[cache[0]*4+1]; + b -= (int) palette->palette[cache[0]*4+2]; + + /* propagate errors (don't ask ;-) */ + r2 = r; d2 = r + r; r += d2; e[0] = r + r0; + r += d2; r0 = r + r1; r1 = r2; r += d2; + g2 = g; d2 = g + g; g += d2; e[1] = g + g0; + g += d2; g0 = g + g1; g1 = g2; g += d2; + b2 = b; d2 = b + b; b += d2; e[2] = b + b0; + b += d2; b0 = b + b1; b1 = b2; b += d2; + + e += 3; + + } + + e[0] = b0; + e[1] = b1; + e[2] = b2; + + } + ImagingSectionLeave(&cookie); + free(errors); + + } else { + + /* closest colour */ + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + int r, g, b; + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = imOut->image8[y]; + + for (x = 0; x < imIn->xsize; x++, in += 4) { + INT16* cache; + + r = in[0]; g = in[1]; b = in[2]; + + /* get closest colour */ + cache = &ImagingPaletteCache(palette, r, g, b); + if (cache[0] == 0x100) + ImagingPaletteCacheUpdate(palette, r, g, b); + out[x] = (UINT8) cache[0]; + + } + } + ImagingSectionLeave(&cookie); + + } + if (inpalette != palette) + ImagingPaletteCacheDelete(palette); + } + + if (inpalette != palette) + ImagingPaletteDelete(palette); + + return imOut; +} + +static Imaging +tobilevel(Imaging imOut, Imaging imIn, int dither) +{ + ImagingSectionCookie cookie; + int x, y; + int* errors; + + /* Map L or RGB to dithered 1 image */ + if (strcmp(imIn->mode, "L") != 0 && strcmp(imIn->mode, "RGB") != 0) + return (Imaging) ImagingError_ValueError("conversion not supported"); + + imOut = ImagingNew2("1", imOut, imIn); + if (!imOut) + return NULL; + + errors = calloc(imIn->xsize + 1, sizeof(int)); + if (!errors) { + ImagingDelete(imOut); + return ImagingError_MemoryError(); + } + + if (imIn->bands == 1) { + + /* map each pixel to black or white, using error diffusion */ + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + int l, l0, l1, l2, d2; + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = imOut->image8[y]; + + l = l0 = l1 = 0; + + for (x = 0; x < imIn->xsize; x++) { + + /* pick closest colour */ + l = CLIP(in[x] + (l + errors[x+1])/16); + out[x] = (l > 128) ? 255 : 0; + + /* propagate errors */ + l -= (int) out[x]; + l2 = l; d2 = l + l; l += d2; errors[x] = l + l0; + l += d2; l0 = l + l1; l1 = l2; l += d2; + } + + errors[x] = l0; + + } + ImagingSectionLeave(&cookie); + + } else { + + /* map each pixel to black or white, using error diffusion */ + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + int l, l0, l1, l2, d2; + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = imOut->image8[y]; + + l = l0 = l1 = 0; + + for (x = 0; x < imIn->xsize; x++, in += 4) { + + /* pick closest colour */ + l = CLIP(L(in)/1000 + (l + errors[x+1])/16); + out[x] = (l > 128) ? 255 : 0; + + /* propagate errors */ + l -= (int) out[x]; + l2 = l; d2 = l + l; l += d2; errors[x] = l + l0; + l += d2; l0 = l + l1; l1 = l2; l += d2; + + } + + errors[x] = l0; + + } + ImagingSectionLeave(&cookie); + } + + free(errors); + + return imOut; +} + + +static Imaging +convert(Imaging imOut, Imaging imIn, const char *mode, + ImagingPalette palette, int dither) +{ + ImagingSectionCookie cookie; + ImagingShuffler convert; + int y; + + if (!imIn) + return (Imaging) ImagingError_ModeError(); + + if (!mode) { + /* Map palette image to full depth */ + if (!imIn->palette) + return (Imaging) ImagingError_ModeError(); + mode = imIn->palette->mode; + } else + /* Same mode? */ + if (!strcmp(imIn->mode, mode)) + return ImagingCopy2(imOut, imIn); + + + /* test for special conversions */ + + if (strcmp(imIn->mode, "P") == 0 || strcmp(imIn->mode, "PA") == 0) + return frompalette(imOut, imIn, mode); + + if (strcmp(mode, "P") == 0) + return topalette(imOut, imIn, palette, dither); + + if (dither && strcmp(mode, "1") == 0) + return tobilevel(imOut, imIn, dither); + + + /* standard conversion machinery */ + + convert = NULL; + + for (y = 0; converters[y].from; y++) + if (!strcmp(imIn->mode, converters[y].from) && + !strcmp(mode, converters[y].to)) { + convert = converters[y].convert; + break; + } + + if (!convert) +#ifdef notdef + return (Imaging) ImagingError_ValueError("conversion not supported"); +#else + { + static char buf[256]; + /* FIXME: may overflow if mode is too large */ + sprintf(buf, "conversion from %s to %s not supported", imIn->mode, mode); + return (Imaging) ImagingError_ValueError(buf); + } +#endif + + imOut = ImagingNew2(mode, imOut, imIn); + if (!imOut) + return NULL; + + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize); + ImagingSectionLeave(&cookie); + + return imOut; +} + +Imaging +ImagingConvert(Imaging imIn, const char *mode, + ImagingPalette palette, int dither) +{ + return convert(NULL, imIn, mode, palette, dither); +} + +Imaging +ImagingConvert2(Imaging imOut, Imaging imIn) +{ + return convert(imOut, imIn, imOut->mode, NULL, 0); +} + +Imaging +ImagingConvertInPlace(Imaging imIn, const char* mode) +{ + ImagingSectionCookie cookie; + ImagingShuffler convert; + int y; + + /* limited support for inplace conversion */ + if (strcmp(imIn->mode, "L") == 0 && strcmp(mode, "1") == 0) + convert = l2bit; + else if (strcmp(imIn->mode, "1") == 0 && strcmp(mode, "L") == 0) + convert = bit2l; + else + return ImagingError_ModeError(); + + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) + (*convert)((UINT8*) imIn->image[y], (UINT8*) imIn->image[y], + imIn->xsize); + ImagingSectionLeave(&cookie); + + return imIn; +} diff --git a/libImaging/ConvertYCbCr.c b/libImaging/ConvertYCbCr.c new file mode 100644 index 000000000..40cd01780 --- /dev/null +++ b/libImaging/ConvertYCbCr.c @@ -0,0 +1,387 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * code to convert YCbCr data + * + * history: + * 98-07-01 hk Created + * + * Copyright (c) Secret Labs AB 1998 + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +/* JPEG/JFIF YCbCr conversions + + Y = R * 0.29900 + G * 0.58700 + B * 0.11400 + Cb = R * -0.16874 + G * -0.33126 + B * 0.50000 + 128 + Cr = R * 0.50000 + G * -0.41869 + B * -0.08131 + 128 + + R = Y + + (Cr - 128) * 1.40200 + G = Y + (Cb - 128) * -0.34414 + (Cr - 128) * -0.71414 + B = Y + (Cb - 128) * 1.77200 + +*/ + +#define SCALE 6 /* bits */ + +static INT16 Y_R[] = { 0, 19, 38, 57, 77, 96, 115, 134, 153, 172, 191, +210, 230, 249, 268, 287, 306, 325, 344, 364, 383, 402, 421, 440, 459, +478, 498, 517, 536, 555, 574, 593, 612, 631, 651, 670, 689, 708, 727, +746, 765, 785, 804, 823, 842, 861, 880, 899, 919, 938, 957, 976, 995, +1014, 1033, 1052, 1072, 1091, 1110, 1129, 1148, 1167, 1186, 1206, +1225, 1244, 1263, 1282, 1301, 1320, 1340, 1359, 1378, 1397, 1416, +1435, 1454, 1473, 1493, 1512, 1531, 1550, 1569, 1588, 1607, 1627, +1646, 1665, 1684, 1703, 1722, 1741, 1761, 1780, 1799, 1818, 1837, +1856, 1875, 1894, 1914, 1933, 1952, 1971, 1990, 2009, 2028, 2048, +2067, 2086, 2105, 2124, 2143, 2162, 2182, 2201, 2220, 2239, 2258, +2277, 2296, 2315, 2335, 2354, 2373, 2392, 2411, 2430, 2449, 2469, +2488, 2507, 2526, 2545, 2564, 2583, 2602, 2622, 2641, 2660, 2679, +2698, 2717, 2736, 2756, 2775, 2794, 2813, 2832, 2851, 2870, 2890, +2909, 2928, 2947, 2966, 2985, 3004, 3023, 3043, 3062, 3081, 3100, +3119, 3138, 3157, 3177, 3196, 3215, 3234, 3253, 3272, 3291, 3311, +3330, 3349, 3368, 3387, 3406, 3425, 3444, 3464, 3483, 3502, 3521, +3540, 3559, 3578, 3598, 3617, 3636, 3655, 3674, 3693, 3712, 3732, +3751, 3770, 3789, 3808, 3827, 3846, 3865, 3885, 3904, 3923, 3942, +3961, 3980, 3999, 4019, 4038, 4057, 4076, 4095, 4114, 4133, 4153, +4172, 4191, 4210, 4229, 4248, 4267, 4286, 4306, 4325, 4344, 4363, +4382, 4401, 4420, 4440, 4459, 4478, 4497, 4516, 4535, 4554, 4574, +4593, 4612, 4631, 4650, 4669, 4688, 4707, 4727, 4746, 4765, 4784, +4803, 4822, 4841, 4861, 4880 }; + +static INT16 Y_G[] = { 0, 38, 75, 113, 150, 188, 225, 263, 301, 338, +376, 413, 451, 488, 526, 564, 601, 639, 676, 714, 751, 789, 826, 864, +902, 939, 977, 1014, 1052, 1089, 1127, 1165, 1202, 1240, 1277, 1315, +1352, 1390, 1428, 1465, 1503, 1540, 1578, 1615, 1653, 1691, 1728, +1766, 1803, 1841, 1878, 1916, 1954, 1991, 2029, 2066, 2104, 2141, +2179, 2217, 2254, 2292, 2329, 2367, 2404, 2442, 2479, 2517, 2555, +2592, 2630, 2667, 2705, 2742, 2780, 2818, 2855, 2893, 2930, 2968, +3005, 3043, 3081, 3118, 3156, 3193, 3231, 3268, 3306, 3344, 3381, +3419, 3456, 3494, 3531, 3569, 3607, 3644, 3682, 3719, 3757, 3794, +3832, 3870, 3907, 3945, 3982, 4020, 4057, 4095, 4132, 4170, 4208, +4245, 4283, 4320, 4358, 4395, 4433, 4471, 4508, 4546, 4583, 4621, +4658, 4696, 4734, 4771, 4809, 4846, 4884, 4921, 4959, 4997, 5034, +5072, 5109, 5147, 5184, 5222, 5260, 5297, 5335, 5372, 5410, 5447, +5485, 5522, 5560, 5598, 5635, 5673, 5710, 5748, 5785, 5823, 5861, +5898, 5936, 5973, 6011, 6048, 6086, 6124, 6161, 6199, 6236, 6274, +6311, 6349, 6387, 6424, 6462, 6499, 6537, 6574, 6612, 6650, 6687, +6725, 6762, 6800, 6837, 6875, 6913, 6950, 6988, 7025, 7063, 7100, +7138, 7175, 7213, 7251, 7288, 7326, 7363, 7401, 7438, 7476, 7514, +7551, 7589, 7626, 7664, 7701, 7739, 7777, 7814, 7852, 7889, 7927, +7964, 8002, 8040, 8077, 8115, 8152, 8190, 8227, 8265, 8303, 8340, +8378, 8415, 8453, 8490, 8528, 8566, 8603, 8641, 8678, 8716, 8753, +8791, 8828, 8866, 8904, 8941, 8979, 9016, 9054, 9091, 9129, 9167, +9204, 9242, 9279, 9317, 9354, 9392, 9430, 9467, 9505, 9542, 9580 }; + +static INT16 Y_B[] = { 0, 7, 15, 22, 29, 36, 44, 51, 58, 66, 73, 80, +88, 95, 102, 109, 117, 124, 131, 139, 146, 153, 161, 168, 175, 182, +190, 197, 204, 212, 219, 226, 233, 241, 248, 255, 263, 270, 277, 285, +292, 299, 306, 314, 321, 328, 336, 343, 350, 358, 365, 372, 379, 387, +394, 401, 409, 416, 423, 430, 438, 445, 452, 460, 467, 474, 482, 489, +496, 503, 511, 518, 525, 533, 540, 547, 554, 562, 569, 576, 584, 591, +598, 606, 613, 620, 627, 635, 642, 649, 657, 664, 671, 679, 686, 693, +700, 708, 715, 722, 730, 737, 744, 751, 759, 766, 773, 781, 788, 795, +803, 810, 817, 824, 832, 839, 846, 854, 861, 868, 876, 883, 890, 897, +905, 912, 919, 927, 934, 941, 948, 956, 963, 970, 978, 985, 992, 1000, +1007, 1014, 1021, 1029, 1036, 1043, 1051, 1058, 1065, 1073, 1080, +1087, 1094, 1102, 1109, 1116, 1124, 1131, 1138, 1145, 1153, 1160, +1167, 1175, 1182, 1189, 1197, 1204, 1211, 1218, 1226, 1233, 1240, +1248, 1255, 1262, 1270, 1277, 1284, 1291, 1299, 1306, 1313, 1321, +1328, 1335, 1342, 1350, 1357, 1364, 1372, 1379, 1386, 1394, 1401, +1408, 1415, 1423, 1430, 1437, 1445, 1452, 1459, 1466, 1474, 1481, +1488, 1496, 1503, 1510, 1518, 1525, 1532, 1539, 1547, 1554, 1561, +1569, 1576, 1583, 1591, 1598, 1605, 1612, 1620, 1627, 1634, 1642, +1649, 1656, 1663, 1671, 1678, 1685, 1693, 1700, 1707, 1715, 1722, +1729, 1736, 1744, 1751, 1758, 1766, 1773, 1780, 1788, 1795, 1802, +1809, 1817, 1824, 1831, 1839, 1846, 1853, 1860 }; + +static INT16 Cb_R[] = { 0, -10, -21, -31, -42, -53, -64, -75, -85, +-96, -107, -118, -129, -139, -150, -161, -172, -183, -193, -204, -215, +-226, -237, -247, -258, -269, -280, -291, -301, -312, -323, -334, +-345, -355, -366, -377, -388, -399, -409, -420, -431, -442, -453, +-463, -474, -485, -496, -507, -517, -528, -539, -550, -561, -571, +-582, -593, -604, -615, -625, -636, -647, -658, -669, -679, -690, +-701, -712, -723, -733, -744, -755, -766, -777, -787, -798, -809, +-820, -831, -841, -852, -863, -874, -885, -895, -906, -917, -928, +-939, -949, -960, -971, -982, -993, -1003, -1014, -1025, -1036, -1047, +-1057, -1068, -1079, -1090, -1101, -1111, -1122, -1133, -1144, -1155, +-1165, -1176, -1187, -1198, -1209, -1219, -1230, -1241, -1252, -1263, +-1273, -1284, -1295, -1306, -1317, -1327, -1338, -1349, -1360, -1371, +-1381, -1392, -1403, -1414, -1425, -1435, -1446, -1457, -1468, -1479, +-1489, -1500, -1511, -1522, -1533, -1543, -1554, -1565, -1576, -1587, +-1597, -1608, -1619, -1630, -1641, -1651, -1662, -1673, -1684, -1694, +-1705, -1716, -1727, -1738, -1748, -1759, -1770, -1781, -1792, -1802, +-1813, -1824, -1835, -1846, -1856, -1867, -1878, -1889, -1900, -1910, +-1921, -1932, -1943, -1954, -1964, -1975, -1986, -1997, -2008, -2018, +-2029, -2040, -2051, -2062, -2072, -2083, -2094, -2105, -2116, -2126, +-2137, -2148, -2159, -2170, -2180, -2191, -2202, -2213, -2224, -2234, +-2245, -2256, -2267, -2278, -2288, -2299, -2310, -2321, -2332, -2342, +-2353, -2364, -2375, -2386, -2396, -2407, -2418, -2429, -2440, -2450, +-2461, -2472, -2483, -2494, -2504, -2515, -2526, -2537, -2548, -2558, +-2569, -2580, -2591, -2602, -2612, -2623, -2634, -2645, -2656, -2666, +-2677, -2688, -2699, -2710, -2720, -2731, -2742, -2753 }; + +static INT16 Cb_G[] = { 0, -20, -41, -63, -84, -105, -126, -147, -169, +-190, -211, -232, -253, -275, -296, -317, -338, -359, -381, -402, +-423, -444, -465, -487, -508, -529, -550, -571, -593, -614, -635, +-656, -677, -699, -720, -741, -762, -783, -805, -826, -847, -868, +-889, -911, -932, -953, -974, -995, -1017, -1038, -1059, -1080, -1101, +-1123, -1144, -1165, -1186, -1207, -1229, -1250, -1271, -1292, -1313, +-1335, -1356, -1377, -1398, -1419, -1441, -1462, -1483, -1504, -1525, +-1547, -1568, -1589, -1610, -1631, -1653, -1674, -1695, -1716, -1737, +-1759, -1780, -1801, -1822, -1843, -1865, -1886, -1907, -1928, -1949, +-1971, -1992, -2013, -2034, -2055, -2077, -2098, -2119, -2140, -2161, +-2183, -2204, -2225, -2246, -2267, -2289, -2310, -2331, -2352, -2373, +-2395, -2416, -2437, -2458, -2479, -2501, -2522, -2543, -2564, -2585, +-2607, -2628, -2649, -2670, -2691, -2713, -2734, -2755, -2776, -2797, +-2819, -2840, -2861, -2882, -2903, -2925, -2946, -2967, -2988, -3009, +-3031, -3052, -3073, -3094, -3115, -3137, -3158, -3179, -3200, -3221, +-3243, -3264, -3285, -3306, -3328, -3349, -3370, -3391, -3412, -3434, +-3455, -3476, -3497, -3518, -3540, -3561, -3582, -3603, -3624, -3646, +-3667, -3688, -3709, -3730, -3752, -3773, -3794, -3815, -3836, -3858, +-3879, -3900, -3921, -3942, -3964, -3985, -4006, -4027, -4048, -4070, +-4091, -4112, -4133, -4154, -4176, -4197, -4218, -4239, -4260, -4282, +-4303, -4324, -4345, -4366, -4388, -4409, -4430, -4451, -4472, -4494, +-4515, -4536, -4557, -4578, -4600, -4621, -4642, -4663, -4684, -4706, +-4727, -4748, -4769, -4790, -4812, -4833, -4854, -4875, -4896, -4918, +-4939, -4960, -4981, -5002, -5024, -5045, -5066, -5087, -5108, -5130, +-5151, -5172, -5193, -5214, -5236, -5257, -5278, -5299, -5320, -5342, +-5363, -5384, -5405 }; + +static INT16 Cb_B[] = { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, +320, 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, 672, 704, 736, +768, 800, 832, 864, 896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, +1184, 1216, 1248, 1280, 1312, 1344, 1376, 1408, 1440, 1472, 1504, +1536, 1568, 1600, 1632, 1664, 1696, 1728, 1760, 1792, 1824, 1856, +1888, 1920, 1952, 1984, 2016, 2048, 2080, 2112, 2144, 2176, 2208, +2240, 2272, 2304, 2336, 2368, 2400, 2432, 2464, 2496, 2528, 2560, +2592, 2624, 2656, 2688, 2720, 2752, 2784, 2816, 2848, 2880, 2912, +2944, 2976, 3008, 3040, 3072, 3104, 3136, 3168, 3200, 3232, 3264, +3296, 3328, 3360, 3392, 3424, 3456, 3488, 3520, 3552, 3584, 3616, +3648, 3680, 3712, 3744, 3776, 3808, 3840, 3872, 3904, 3936, 3968, +4000, 4032, 4064, 4096, 4128, 4160, 4192, 4224, 4256, 4288, 4320, +4352, 4384, 4416, 4448, 4480, 4512, 4544, 4576, 4608, 4640, 4672, +4704, 4736, 4768, 4800, 4832, 4864, 4896, 4928, 4960, 4992, 5024, +5056, 5088, 5120, 5152, 5184, 5216, 5248, 5280, 5312, 5344, 5376, +5408, 5440, 5472, 5504, 5536, 5568, 5600, 5632, 5664, 5696, 5728, +5760, 5792, 5824, 5856, 5888, 5920, 5952, 5984, 6016, 6048, 6080, +6112, 6144, 6176, 6208, 6240, 6272, 6304, 6336, 6368, 6400, 6432, +6464, 6496, 6528, 6560, 6592, 6624, 6656, 6688, 6720, 6752, 6784, +6816, 6848, 6880, 6912, 6944, 6976, 7008, 7040, 7072, 7104, 7136, +7168, 7200, 7232, 7264, 7296, 7328, 7360, 7392, 7424, 7456, 7488, +7520, 7552, 7584, 7616, 7648, 7680, 7712, 7744, 7776, 7808, 7840, +7872, 7904, 7936, 7968, 8000, 8032, 8064, 8096, 8128, 8160 }; + +#define Cr_R Cb_B + +static INT16 Cr_G[] = { 0, -26, -53, -79, -106, -133, -160, -187, +-213, -240, -267, -294, -321, -347, -374, -401, -428, -455, -481, +-508, -535, -562, -589, -615, -642, -669, -696, -722, -749, -776, +-803, -830, -856, -883, -910, -937, -964, -990, -1017, -1044, -1071, +-1098, -1124, -1151, -1178, -1205, -1232, -1258, -1285, -1312, -1339, +-1366, -1392, -1419, -1446, -1473, -1500, -1526, -1553, -1580, -1607, +-1634, -1660, -1687, -1714, -1741, -1768, -1794, -1821, -1848, -1875, +-1902, -1928, -1955, -1982, -2009, -2036, -2062, -2089, -2116, -2143, +-2169, -2196, -2223, -2250, -2277, -2303, -2330, -2357, -2384, -2411, +-2437, -2464, -2491, -2518, -2545, -2571, -2598, -2625, -2652, -2679, +-2705, -2732, -2759, -2786, -2813, -2839, -2866, -2893, -2920, -2947, +-2973, -3000, -3027, -3054, -3081, -3107, -3134, -3161, -3188, -3215, +-3241, -3268, -3295, -3322, -3349, -3375, -3402, -3429, -3456, -3483, +-3509, -3536, -3563, -3590, -3616, -3643, -3670, -3697, -3724, -3750, +-3777, -3804, -3831, -3858, -3884, -3911, -3938, -3965, -3992, -4018, +-4045, -4072, -4099, -4126, -4152, -4179, -4206, -4233, -4260, -4286, +-4313, -4340, -4367, -4394, -4420, -4447, -4474, -4501, -4528, -4554, +-4581, -4608, -4635, -4662, -4688, -4715, -4742, -4769, -4796, -4822, +-4849, -4876, -4903, -4929, -4956, -4983, -5010, -5037, -5063, -5090, +-5117, -5144, -5171, -5197, -5224, -5251, -5278, -5305, -5331, -5358, +-5385, -5412, -5439, -5465, -5492, -5519, -5546, -5573, -5599, -5626, +-5653, -5680, -5707, -5733, -5760, -5787, -5814, -5841, -5867, -5894, +-5921, -5948, -5975, -6001, -6028, -6055, -6082, -6109, -6135, -6162, +-6189, -6216, -6243, -6269, -6296, -6323, -6350, -6376, -6403, -6430, +-6457, -6484, -6510, -6537, -6564, -6591, -6618, -6644, -6671, -6698, +-6725, -6752, -6778, -6805, -6832 }; + +static INT16 Cr_B[] = { 0, -4, -9, -15, -20, -25, -30, -35, -41, -46, +-51, -56, -61, -67, -72, -77, -82, -87, -93, -98, -103, -108, -113, +-119, -124, -129, -134, -140, -145, -150, -155, -160, -166, -171, +-176, -181, -186, -192, -197, -202, -207, -212, -218, -223, -228, +-233, -238, -244, -249, -254, -259, -264, -270, -275, -280, -285, +-290, -296, -301, -306, -311, -316, -322, -327, -332, -337, -342, +-348, -353, -358, -363, -368, -374, -379, -384, -389, -394, -400, +-405, -410, -415, -421, -426, -431, -436, -441, -447, -452, -457, +-462, -467, -473, -478, -483, -488, -493, -499, -504, -509, -514, +-519, -525, -530, -535, -540, -545, -551, -556, -561, -566, -571, +-577, -582, -587, -592, -597, -603, -608, -613, -618, -623, -629, +-634, -639, -644, -649, -655, -660, -665, -670, -675, -681, -686, +-691, -696, -702, -707, -712, -717, -722, -728, -733, -738, -743, +-748, -754, -759, -764, -769, -774, -780, -785, -790, -795, -800, +-806, -811, -816, -821, -826, -832, -837, -842, -847, -852, -858, +-863, -868, -873, -878, -884, -889, -894, -899, -904, -910, -915, +-920, -925, -930, -936, -941, -946, -951, -957, -962, -967, -972, +-977, -983, -988, -993, -998, -1003, -1009, -1014, -1019, -1024, +-1029, -1035, -1040, -1045, -1050, -1055, -1061, -1066, -1071, -1076, +-1081, -1087, -1092, -1097, -1102, -1107, -1113, -1118, -1123, -1128, +-1133, -1139, -1144, -1149, -1154, -1159, -1165, -1170, -1175, -1180, +-1185, -1191, -1196, -1201, -1206, -1211, -1217, -1222, -1227, -1232, +-1238, -1243, -1248, -1253, -1258, -1264, -1269, -1274, -1279, -1284, +-1290, -1295, -1300, -1305, -1310, -1316, -1321, -1326 }; + +static INT16 R_Cr[] = { -11484, -11394, -11305, -11215, -11125, +-11036, -10946, -10856, -10766, -10677, -10587, -10497, -10407, +-10318, -10228, -10138, -10049, -9959, -9869, -9779, -9690, -9600, +-9510, -9420, -9331, -9241, -9151, -9062, -8972, -8882, -8792, -8703, +-8613, -8523, -8433, -8344, -8254, -8164, -8075, -7985, -7895, -7805, +-7716, -7626, -7536, -7446, -7357, -7267, -7177, -7088, -6998, -6908, +-6818, -6729, -6639, -6549, -6459, -6370, -6280, -6190, -6101, -6011, +-5921, -5831, -5742, -5652, -5562, -5472, -5383, -5293, -5203, -5113, +-5024, -4934, -4844, -4755, -4665, -4575, -4485, -4396, -4306, -4216, +-4126, -4037, -3947, -3857, -3768, -3678, -3588, -3498, -3409, -3319, +-3229, -3139, -3050, -2960, -2870, -2781, -2691, -2601, -2511, -2422, +-2332, -2242, -2152, -2063, -1973, -1883, -1794, -1704, -1614, -1524, +-1435, -1345, -1255, -1165, -1076, -986, -896, -807, -717, -627, -537, +-448, -358, -268, -178, -89, 0, 90, 179, 269, 359, 449, 538, 628, 718, +808, 897, 987, 1077, 1166, 1256, 1346, 1436, 1525, 1615, 1705, 1795, +1884, 1974, 2064, 2153, 2243, 2333, 2423, 2512, 2602, 2692, 2782, +2871, 2961, 3051, 3140, 3230, 3320, 3410, 3499, 3589, 3679, 3769, +3858, 3948, 4038, 4127, 4217, 4307, 4397, 4486, 4576, 4666, 4756, +4845, 4935, 5025, 5114, 5204, 5294, 5384, 5473, 5563, 5653, 5743, +5832, 5922, 6012, 6102, 6191, 6281, 6371, 6460, 6550, 6640, 6730, +6819, 6909, 6999, 7089, 7178, 7268, 7358, 7447, 7537, 7627, 7717, +7806, 7896, 7986, 8076, 8165, 8255, 8345, 8434, 8524, 8614, 8704, +8793, 8883, 8973, 9063, 9152, 9242, 9332, 9421, 9511, 9601, 9691, +9780, 9870, 9960, 10050, 10139, 10229, 10319, 10408, 10498, 10588, +10678, 10767, 10857, 10947, 11037, 11126, 11216, 11306, 11395 }; + +static INT16 G_Cb[] = { 2819, 2797, 2775, 2753, 2731, 2709, 2687, +2665, 2643, 2621, 2599, 2577, 2555, 2533, 2511, 2489, 2467, 2445, +2423, 2401, 2379, 2357, 2335, 2313, 2291, 2269, 2247, 2225, 2202, +2180, 2158, 2136, 2114, 2092, 2070, 2048, 2026, 2004, 1982, 1960, +1938, 1916, 1894, 1872, 1850, 1828, 1806, 1784, 1762, 1740, 1718, +1696, 1674, 1652, 1630, 1608, 1586, 1564, 1542, 1520, 1498, 1476, +1454, 1432, 1410, 1388, 1366, 1344, 1321, 1299, 1277, 1255, 1233, +1211, 1189, 1167, 1145, 1123, 1101, 1079, 1057, 1035, 1013, 991, 969, +947, 925, 903, 881, 859, 837, 815, 793, 771, 749, 727, 705, 683, 661, +639, 617, 595, 573, 551, 529, 507, 485, 463, 440, 418, 396, 374, 352, +330, 308, 286, 264, 242, 220, 198, 176, 154, 132, 110, 88, 66, 44, 22, +0, -21, -43, -65, -87, -109, -131, -153, -175, -197, -219, -241, -263, +-285, -307, -329, -351, -373, -395, -417, -439, -462, -484, -506, +-528, -550, -572, -594, -616, -638, -660, -682, -704, -726, -748, +-770, -792, -814, -836, -858, -880, -902, -924, -946, -968, -990, +-1012, -1034, -1056, -1078, -1100, -1122, -1144, -1166, -1188, -1210, +-1232, -1254, -1276, -1298, -1320, -1343, -1365, -1387, -1409, -1431, +-1453, -1475, -1497, -1519, -1541, -1563, -1585, -1607, -1629, -1651, +-1673, -1695, -1717, -1739, -1761, -1783, -1805, -1827, -1849, -1871, +-1893, -1915, -1937, -1959, -1981, -2003, -2025, -2047, -2069, -2091, +-2113, -2135, -2157, -2179, -2201, -2224, -2246, -2268, -2290, -2312, +-2334, -2356, -2378, -2400, -2422, -2444, -2466, -2488, -2510, -2532, +-2554, -2576, -2598, -2620, -2642, -2664, -2686, -2708, -2730, -2752, +-2774, -2796 }; + +static INT16 G_Cr[] = { 5850, 5805, 5759, 5713, 5667, 5622, 5576, +5530, 5485, 5439, 5393, 5347, 5302, 5256, 5210, 5165, 5119, 5073, +5028, 4982, 4936, 4890, 4845, 4799, 4753, 4708, 4662, 4616, 4570, +4525, 4479, 4433, 4388, 4342, 4296, 4251, 4205, 4159, 4113, 4068, +4022, 3976, 3931, 3885, 3839, 3794, 3748, 3702, 3656, 3611, 3565, +3519, 3474, 3428, 3382, 3336, 3291, 3245, 3199, 3154, 3108, 3062, +3017, 2971, 2925, 2879, 2834, 2788, 2742, 2697, 2651, 2605, 2559, +2514, 2468, 2422, 2377, 2331, 2285, 2240, 2194, 2148, 2102, 2057, +2011, 1965, 1920, 1874, 1828, 1782, 1737, 1691, 1645, 1600, 1554, +1508, 1463, 1417, 1371, 1325, 1280, 1234, 1188, 1143, 1097, 1051, +1006, 960, 914, 868, 823, 777, 731, 686, 640, 594, 548, 503, 457, 411, +366, 320, 274, 229, 183, 137, 91, 46, 0, -45, -90, -136, -182, -228, +-273, -319, -365, -410, -456, -502, -547, -593, -639, -685, -730, +-776, -822, -867, -913, -959, -1005, -1050, -1096, -1142, -1187, +-1233, -1279, -1324, -1370, -1416, -1462, -1507, -1553, -1599, -1644, +-1690, -1736, -1781, -1827, -1873, -1919, -1964, -2010, -2056, -2101, +-2147, -2193, -2239, -2284, -2330, -2376, -2421, -2467, -2513, -2558, +-2604, -2650, -2696, -2741, -2787, -2833, -2878, -2924, -2970, -3016, +-3061, -3107, -3153, -3198, -3244, -3290, -3335, -3381, -3427, -3473, +-3518, -3564, -3610, -3655, -3701, -3747, -3793, -3838, -3884, -3930, +-3975, -4021, -4067, -4112, -4158, -4204, -4250, -4295, -4341, -4387, +-4432, -4478, -4524, -4569, -4615, -4661, -4707, -4752, -4798, -4844, +-4889, -4935, -4981, -5027, -5072, -5118, -5164, -5209, -5255, -5301, +-5346, -5392, -5438, -5484, -5529, -5575, -5621, -5666, -5712, -5758, +-5804 }; + +static INT16 B_Cb[] = { -14515, -14402, -14288, -14175, -14062, +-13948, -13835, -13721, -13608, -13495, -13381, -13268, -13154, +-13041, -12928, -12814, -12701, -12587, -12474, -12360, -12247, +-12134, -12020, -11907, -11793, -11680, -11567, -11453, -11340, +-11226, -11113, -11000, -10886, -10773, -10659, -10546, -10433, +-10319, -10206, -10092, -9979, -9865, -9752, -9639, -9525, -9412, +-9298, -9185, -9072, -8958, -8845, -8731, -8618, -8505, -8391, -8278, +-8164, -8051, -7938, -7824, -7711, -7597, -7484, -7371, -7257, -7144, +-7030, -6917, -6803, -6690, -6577, -6463, -6350, -6236, -6123, -6010, +-5896, -5783, -5669, -5556, -5443, -5329, -5216, -5102, -4989, -4876, +-4762, -4649, -4535, -4422, -4309, -4195, -4082, -3968, -3855, -3741, +-3628, -3515, -3401, -3288, -3174, -3061, -2948, -2834, -2721, -2607, +-2494, -2381, -2267, -2154, -2040, -1927, -1814, -1700, -1587, -1473, +-1360, -1246, -1133, -1020, -906, -793, -679, -566, -453, -339, -226, +-112, 0, 113, 227, 340, 454, 567, 680, 794, 907, 1021, 1134, 1247, +1361, 1474, 1588, 1701, 1815, 1928, 2041, 2155, 2268, 2382, 2495, +2608, 2722, 2835, 2949, 3062, 3175, 3289, 3402, 3516, 3629, 3742, +3856, 3969, 4083, 4196, 4310, 4423, 4536, 4650, 4763, 4877, 4990, +5103, 5217, 5330, 5444, 5557, 5670, 5784, 5897, 6011, 6124, 6237, +6351, 6464, 6578, 6691, 6804, 6918, 7031, 7145, 7258, 7372, 7485, +7598, 7712, 7825, 7939, 8052, 8165, 8279, 8392, 8506, 8619, 8732, +8846, 8959, 9073, 9186, 9299, 9413, 9526, 9640, 9753, 9866, 9980, +10093, 10207, 10320, 10434, 10547, 10660, 10774, 10887, 11001, 11114, +11227, 11341, 11454, 11568, 11681, 11794, 11908, 12021, 12135, 12248, +12361, 12475, 12588, 12702, 12815, 12929, 13042, 13155, 13269, 13382, +13496, 13609, 13722, 13836, 13949, 14063, 14176, 14289, 14403 }; + + +void +ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels) +{ + int x; + UINT8 a; + int r, g, b; + int y, cr, cb; + + for (x = 0; x < pixels; x++, in +=4, out += 4) { + + r = in[0]; + g = in[1]; + b = in[2]; + a = in[3]; + + y = (Y_R[r] + Y_G[g] + Y_B[b]) >> SCALE; + cb = ((Cb_R[r] + Cb_G[g] + Cb_B[b]) >> SCALE) + 128; + cr = ((Cr_R[r] + Cr_G[g] + Cr_B[b]) >> SCALE) + 128; + + out[0] = (UINT8) y; + out[1] = (UINT8) cb; + out[2] = (UINT8) cr; + out[3] = a; + } +} + +void +ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels) +{ + int x; + UINT8 a; + int r, g, b; + int y, cr, cb; + + for (x = 0; x < pixels; x++, in += 4, out += 4) { + + y = in[0]; + cb = in[1]; + cr = in[2]; + a = in[3]; + + r = y + (( R_Cr[cr]) >> SCALE); + g = y + ((G_Cb[cb] + G_Cr[cr]) >> SCALE); + b = y + ((B_Cb[cb] ) >> SCALE); + + out[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r; + out[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g; + out[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b; + out[3] = a; + } +} diff --git a/libImaging/Copy.c b/libImaging/Copy.c new file mode 100644 index 000000000..4a1fc2489 --- /dev/null +++ b/libImaging/Copy.c @@ -0,0 +1,58 @@ +/* + * The Python Imaging Library + * $Id$ + * + * copy image + * + * history: + * 95-11-26 fl Moved from Imaging.c + * 97-05-12 fl Added ImagingCopy2 + * 97-08-28 fl Allow imOut == NULL in ImagingCopy2 + * + * Copyright (c) Fredrik Lundh 1995-97. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + + +static Imaging +_copy(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int y; + + if (!imIn) + return (Imaging) ImagingError_ValueError(NULL); + + imOut = ImagingNew2(imIn->mode, imOut, imIn); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + if (imIn->block != NULL && imOut->block != NULL) + memcpy(imOut->block, imIn->block, imIn->ysize * imIn->linesize); + else + for (y = 0; y < imIn->ysize; y++) + memcpy(imOut->image[y], imIn->image[y], imIn->linesize); + ImagingSectionLeave(&cookie); + + return imOut; +} + +Imaging +ImagingCopy(Imaging imIn) +{ + return _copy(NULL, imIn); +} + +Imaging +ImagingCopy2(Imaging imOut, Imaging imIn) +{ + return _copy(imOut, imIn); +} diff --git a/libImaging/Crc32.c b/libImaging/Crc32.c new file mode 100644 index 000000000..d83d0fbac --- /dev/null +++ b/libImaging/Crc32.c @@ -0,0 +1,87 @@ +/* + * The Python Imaging Library + * $Id$ + * + * calculate ISO 3307 checksum + * + * history: + * 96-12-10 fl: Created (based on example in the PNG spec) + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +/* Precalculated CRC values (created by makecrctable.py) */ + +static UINT32 crc32table[] = { 0x0, 0x77073096L, 0xEE0E612CL, +0x990951BAL, 0x76DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L, +0xEDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L, 0x9B64C2BL, +0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L, 0x1DB71064L, 0x6AB020F2L, +0xF3B97148L, 0x84BE41DEL, 0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, +0x83D385C7L, 0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL, +0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L, 0x3B6E20C8L, +0x4C69105EL, 0xD56041E4L, 0xA2677172L, 0x3C03E4D1L, 0x4B04D447L, +0xD20D85FDL, 0xA50AB56BL, 0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, +0xACBCF940L, 0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L, +0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L, 0x21B4F4B5L, +0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL, 0x2802B89EL, 0x5F058808L, +0xC60CD9B2L, 0xB10BE924L, 0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, +0xB6662D3DL, 0x76DC4190L, 0x1DB7106L, 0x98D220BCL, 0xEFD5102AL, +0x71B18589L, 0x6B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L, 0x7807C9A2L, +0xF00F934L, 0x9609A88EL, 0xE10E9818L, 0x7F6A0DBBL, 0x86D3D2DL, +0x91646C97L, 0xE6635C01L, 0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, +0xF262004EL, 0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L, +0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL, 0x62DD1DDFL, +0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L, 0x4DB26158L, 0x3AB551CEL, +0xA3BC0074L, 0xD4BB30E2L, 0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, +0xD3D6F4FBL, 0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L, +0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L, 0x5005713CL, +0x270241AAL, 0xBE0B1010L, 0xC90C2086L, 0x5768B525L, 0x206F85B3L, +0xB966D409L, 0xCE61E49FL, 0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, +0xC7D7A8B4L, 0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL, +0xEDB88320L, 0x9ABFB3B6L, 0x3B6E20CL, 0x74B1D29AL, 0xEAD54739L, +0x9DD277AFL, 0x4DB2615L, 0x73DC1683L, 0xE3630B12L, 0x94643B84L, +0xD6D6A3EL, 0x7A6A5AA8L, 0xE40ECF0BL, 0x9309FF9DL, 0xA00AE27L, +0x7D079EB1L, 0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL, +0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L, 0xFED41B76L, +0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL, 0xF9B9DF6FL, 0x8EBEEFF9L, +0x17B7BE43L, 0x60B08ED5L, 0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, +0x4FDFF252L, 0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL, +0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L, 0xDF60EFC3L, +0xA867DF55L, 0x316E8EEFL, 0x4669BE79L, 0xCB61B38CL, 0xBC66831AL, +0x256FD2A0L, 0x5268E236L, 0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, +0x5505262FL, 0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L, +0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL, 0x9B64C2B0L, +0xEC63F226L, 0x756AA39CL, 0x26D930AL, 0x9C0906A9L, 0xEB0E363FL, +0x72076785L, 0x5005713L, 0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, +0xCB61B38L, 0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0xBDBDF21L, +0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL, 0x81BE16CDL, +0xF6B9265BL, 0x6FB077E1L, 0x18B74777L, 0x88085AE6L, 0xFF0F6A70L, +0x66063BCAL, 0x11010B5CL, 0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, +0x166CCF45L, 0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L, +0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL, 0xAED16A4AL, +0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L, 0xA9BCAE53L, 0xDEBB9EC5L, +0x47B2CF7FL, 0x30B5FFE9L, 0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, +0x24B4A3A6L, 0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL, +0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L, 0xB40BBE37L, +0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL }; + + +UINT32 +ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes) +{ + int i; + + crc ^= 0xFFFFFFFFL; + + for (i = 0; i < bytes; i++) + crc = crc32table[(UINT8) crc ^ buffer[i]] ^ (crc >> 8); + + return crc ^ 0xFFFFFFFFL; +} diff --git a/libImaging/Crop.c b/libImaging/Crop.c new file mode 100644 index 000000000..5acacd4c4 --- /dev/null +++ b/libImaging/Crop.c @@ -0,0 +1,61 @@ +/* + * The Python Imaging Library + * $Id$ + * + * cut region from image + * + * history: + * 95-11-27 fl Created + * 98-07-10 fl Fixed "null result" error + * 99-02-05 fl Rewritten to use Paste primitive + * + * Copyright (c) Secret Labs AB 1997-99. + * Copyright (c) Fredrik Lundh 1995. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +Imaging +ImagingCrop(Imaging imIn, int sx0, int sy0, int sx1, int sy1) +{ + Imaging imOut; + int xsize, ysize; + int dx0, dy0, dx1, dy1; + INT32 zero = 0; + + if (!imIn) + return (Imaging) ImagingError_ModeError(); + + xsize = sx1 - sx0; + if (xsize < 0) + xsize = 0; + ysize = sy1 - sy0; + if (ysize < 0) + ysize = 0; + + imOut = ImagingNew(imIn->mode, xsize, ysize); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, imIn); + + if (sx0 < 0 || sy0 < 0 || sx1 > imIn->xsize || sy1 > imIn->ysize) + (void) ImagingFill(imOut, &zero); + + dx0 = -sx0; + dy0 = -sy0; + dx1 = imIn->xsize - sx0; + dy1 = imIn->ysize - sy0; + + /* paste the source image on top of the output image!!! */ + if (ImagingPaste(imOut, imIn, NULL, dx0, dy0, dx1, dy1) < 0) { + ImagingDelete(imOut); + return NULL; + } + + return imOut; +} diff --git a/libImaging/Dib.c b/libImaging/Dib.c new file mode 100644 index 000000000..36dc24ef3 --- /dev/null +++ b/libImaging/Dib.c @@ -0,0 +1,311 @@ +/* + * The Python Imaging Library + * $Id$ + * + * imaging display object for Windows + * + * history: + * 1996-05-12 fl Created + * 1996-05-17 fl Up and running + * 1996-05-21 fl Added palette stuff + * 1996-05-26 fl Added query palette and mode inquery + * 1997-09-21 fl Added draw primitive + * 1998-01-20 fl Use StretchDIBits instead of StretchBlt + * 1998-12-30 fl Plugged a resource leak in DeleteDIB (from Roger Burnham) + * + * Copyright (c) Secret Labs AB 1997-2001. + * Copyright (c) Fredrik Lundh 1996. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#ifdef WIN32 + +#include "ImDib.h" + + +char* +ImagingGetModeDIB(int size_out[2]) +{ + /* Get device characteristics */ + + HDC dc; + char* mode; + + dc = CreateCompatibleDC(NULL); + + mode = "P"; + if (!(GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE)) { + mode = "RGB"; + if (GetDeviceCaps(dc, BITSPIXEL) == 1) + mode = "1"; + } + + if (size_out) { + size_out[0] = GetDeviceCaps(dc, HORZRES); + size_out[1] = GetDeviceCaps(dc, VERTRES); + } + + DeleteDC(dc); + + return mode; +} + + +ImagingDIB +ImagingNewDIB(const char *mode, int xsize, int ysize) +{ + /* Create a Windows bitmap */ + + ImagingDIB dib; + RGBQUAD *palette; + int i; + + /* Check mode */ + if (strcmp(mode, "1") != 0 && strcmp(mode, "L") != 0 && + strcmp(mode, "RGB") != 0) + return (ImagingDIB) ImagingError_ModeError(); + + /* Create DIB context and info header */ + dib = (ImagingDIB) malloc(sizeof(*dib)); + if (!dib) + return (ImagingDIB) ImagingError_MemoryError(); + dib->info = (BITMAPINFO*) malloc(sizeof(BITMAPINFOHEADER) + + 256 * sizeof(RGBQUAD)); + if (!dib->info) { + free(dib); + return (ImagingDIB) ImagingError_MemoryError(); + } + + memset(dib->info, 0, sizeof(BITMAPINFOHEADER)); + dib->info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + dib->info->bmiHeader.biWidth = xsize; + dib->info->bmiHeader.biHeight = ysize; + dib->info->bmiHeader.biPlanes = 1; + dib->info->bmiHeader.biBitCount = strlen(mode)*8; + dib->info->bmiHeader.biCompression = BI_RGB; + + /* Create DIB */ + dib->dc = CreateCompatibleDC(NULL); + if (!dib->dc) { + free(dib->info); + free(dib); + return (ImagingDIB) ImagingError_MemoryError(); + } + + dib->bitmap = CreateDIBSection(dib->dc, dib->info, DIB_RGB_COLORS, + &dib->bits, NULL, 0); + if (!dib->bitmap) { + free(dib->info); + free(dib); + return (ImagingDIB) ImagingError_MemoryError(); + } + + strcpy(dib->mode, mode); + dib->xsize = xsize; + dib->ysize = ysize; + + dib->pixelsize = strlen(mode); + dib->linesize = (xsize * dib->pixelsize + 3) & -4; + + if (dib->pixelsize == 1) + dib->pack = dib->unpack = (ImagingShuffler) memcpy; + else { + dib->pack = ImagingPackBGR; + dib->unpack = ImagingPackBGR; + } + + /* Bind the DIB to the device context */ + dib->old_bitmap = SelectObject(dib->dc, dib->bitmap); + + palette = dib->info->bmiColors; + + /* Bind a palette to it as well (only required for 8-bit DIBs) */ + if (dib->pixelsize == 1) { + for (i = 0; i < 256; i++) { + palette[i].rgbRed = + palette[i].rgbGreen = + palette[i].rgbBlue = i; + palette[i].rgbReserved = 0; + } + SetDIBColorTable(dib->dc, 0, 256, palette); + } + + /* Create an associated palette (for 8-bit displays only) */ + if (strcmp(ImagingGetModeDIB(NULL), "P") == 0) { + + char palbuf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)]; + LPLOGPALETTE pal = (LPLOGPALETTE) palbuf; + int i, r, g, b; + + /* Load system palette */ + pal->palVersion = 0x300; + pal->palNumEntries = 256; + GetSystemPaletteEntries(dib->dc, 0, 256, pal->palPalEntry); + + if (strcmp(mode, "L") == 0) { + + /* Greyscale DIB. Fill all 236 slots with a greyscale ramp + * (this is usually overkill on Windows since VGA only offers + * 6 bits greyscale resolution). Ignore the slots already + * allocated by Windows */ + + i = 10; + for (r = 0; r < 236; r++) { + pal->palPalEntry[i].peRed = + pal->palPalEntry[i].peGreen = + pal->palPalEntry[i].peBlue = i; + i++; + } + + dib->palette = CreatePalette(pal); + + } else if (strcmp(mode, "RGB") == 0) { + +#ifdef CUBE216 + + /* Colour DIB. Create a 6x6x6 colour cube (216 entries) and + * add 20 extra greylevels for best result with greyscale + * images. */ + + i = 10; + for (r = 0; r < 256; r += 51) + for (g = 0; g < 256; g += 51) + for (b = 0; b < 256; b += 51) { + pal->palPalEntry[i].peRed = r; + pal->palPalEntry[i].peGreen = g; + pal->palPalEntry[i].peBlue = b; + i++; + } + for (r = 1; r < 22-1; r++) { + /* Black and white are already provided by the cube. */ + pal->palPalEntry[i].peRed = + pal->palPalEntry[i].peGreen = + pal->palPalEntry[i].peBlue = r * 255 / (22-1); + i++; + } + +#else + + /* Colour DIB. Alternate palette. */ + + i = 10; + for (r = 0; r < 256; r += 37) + for (g = 0; g < 256; g += 32) + for (b = 0; b < 256; b += 64) { + pal->palPalEntry[i].peRed = r; + pal->palPalEntry[i].peGreen = g; + pal->palPalEntry[i].peBlue = b; + i++; + } + +#endif + +#if 0 + { + /* DEBUG: dump palette to file */ + FILE *err = fopen("dib.pal", "w"); + for (i = 0; i < 256; i++) + fprintf(err, "%d: %d/%d/%d\n", i, + pal->palPalEntry[i].peRed, + pal->palPalEntry[i].peGreen, + pal->palPalEntry[i].peBlue); + fclose(err); + } +#endif + + dib->palette = CreatePalette(pal); + + } + + } + + return dib; +} + +void +ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]) +{ + /* Paste image data into a bitmap */ + + /* FIXME: check size! */ + + int y; + for (y = 0; y < im->ysize; y++) + dib->pack(dib->bits + dib->linesize*(dib->ysize-(xy[1]+y)-1) + + xy[0]*dib->pixelsize, im->image[y], im->xsize); + +} + +void +ImagingExposeDIB(ImagingDIB dib, int dc) +{ + /* Copy bitmap to display */ + + if (dib->palette != 0) + SelectPalette((HDC) dc, dib->palette, FALSE); + BitBlt((HDC) dc, 0, 0, dib->xsize, dib->ysize, dib->dc, 0, 0, SRCCOPY); +} + +void +ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]) +{ + /* Copy bitmap to printer/display */ + + if (GetDeviceCaps((HDC) dc, RASTERCAPS) & RC_STRETCHDIB) { + /* stretchdib (printers) */ + StretchDIBits((HDC) dc, dst[0], dst[1], dst[2]-dst[0], dst[3]-dst[1], + src[0], src[1], src[2]-src[0], src[3]-src[1], dib->bits, + dib->info, DIB_RGB_COLORS, SRCCOPY); + } else { + /* stretchblt (displays) */ + if (dib->palette != 0) + SelectPalette((HDC) dc, dib->palette, FALSE); + StretchBlt((HDC) dc, dst[0], dst[1], dst[2]-dst[0], dst[3]-dst[1], + dib->dc, src[0], src[1], src[2]-src[0], src[3]-src[1], + SRCCOPY); + } +} + +int +ImagingQueryPaletteDIB(ImagingDIB dib, int dc) +{ + /* Install bitmap palette */ + + int n; + + if (dib->palette != 0) { + + /* Realize associated palette */ + HPALETTE now = SelectPalette((HDC) dc, dib->palette, FALSE); + n = RealizePalette((HDC) dc); + + /* Restore palette */ + SelectPalette((HDC) dc, now, FALSE); + + } else + n = 0; + + return n; /* number of colours that was changed */ +} + +void +ImagingDeleteDIB(ImagingDIB dib) +{ + /* Clean up */ + + if (dib->palette) + DeleteObject(dib->palette); + if (dib->bitmap) { + SelectObject(dib->dc, dib->old_bitmap); + DeleteObject(dib->bitmap); + } + if (dib->dc) + DeleteDC(dib->dc); + free(dib->info); +} + +#endif /* WIN32 */ diff --git a/libImaging/Draw.c b/libImaging/Draw.c new file mode 100644 index 000000000..bdf17e18a --- /dev/null +++ b/libImaging/Draw.c @@ -0,0 +1,1167 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * a simple drawing package for the Imaging library + * + * history: + * 1996-04-13 fl Created. + * 1996-04-30 fl Added transforms and polygon support. + * 1996-08-12 fl Added filled polygons. + * 1996-11-05 fl Fixed float/int confusion in polygon filler + * 1997-07-04 fl Support 32-bit images (C++ would have been nice) + * 1998-09-09 fl Eliminated qsort casts; improved rectangle clipping + * 1998-09-10 fl Fixed fill rectangle to include lower edge (!) + * 1998-12-29 fl Added arc, chord, and pieslice primitives + * 1999-01-10 fl Added some level 2 ("arrow") stuff (experimental) + * 1999-02-06 fl Added bitmap primitive + * 1999-07-26 fl Eliminated a compiler warning + * 1999-07-31 fl Pass ink as void* instead of int + * 2002-12-10 fl Added experimental RGBA-on-RGB drawing + * 2004-09-04 fl Support simple wide lines (no joins) + * 2005-05-25 fl Fixed line width calculation + * + * Copyright (c) 1996-2006 by Fredrik Lundh + * Copyright (c) 1997-2006 by Secret Labs AB. + * + * See the README file for information on usage and redistribution. + */ + +/* FIXME: support fill/outline attribute for all filled shapes */ +/* FIXME: support zero-winding fill */ +/* FIXME: add drawing context, support affine transforms */ +/* FIXME: support clip window (and mask?) */ + +#include "Imaging.h" + +#include + +#define CEIL(v) (int) ceil(v) +#define FLOOR(v) ((v) >= 0.0 ? (int) (v) : (int) floor(v)) + +#define INK8(ink) (*(UINT8*)ink) +#define INK32(ink) (*(INT32*)ink) + +/* like (a * b + 127) / 255), but much faster on most platforms */ +#define MULDIV255(a, b, tmp)\ + (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) + +#define BLEND(mask, in1, in2, tmp1, tmp2)\ + (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) + +/* -------------------------------------------------------------------- */ +/* Primitives */ +/* -------------------------------------------------------------------- */ + +typedef struct { + /* edge descriptor for polygon engine */ + int d; + int x0, y0; + int xmin, ymin, xmax, ymax; + float dx; +} Edge; + +static inline void +point8(Imaging im, int x, int y, int ink) +{ + if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) + im->image8[y][x] = (UINT8) ink; +} + +static inline void +point32(Imaging im, int x, int y, int ink) +{ + if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) + im->image32[y][x] = ink; +} + +static inline void +point32rgba(Imaging im, int x, int y, int ink) +{ + unsigned int tmp1, tmp2; + + if (x >= 0 && x < im->xsize && y >= 0 && y < im->ysize) { + UINT8* out = (UINT8*) im->image[y]+x*4; + UINT8* in = (UINT8*) &ink; + out[0] = BLEND(in[3], out[0], in[0], tmp1, tmp2); + out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2); + out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2); + } +} + +static inline void +hline8(Imaging im, int x0, int y0, int x1, int ink) +{ + int tmp; + + if (y0 >= 0 && y0 < im->ysize) { + if (x0 > x1) + tmp = x0, x0 = x1, x1 = tmp; + if (x0 < 0) + x0 = 0; + else if (x0 >= im->xsize) + return; + if (x1 < 0) + return; + else if (x1 >= im->xsize) + x1 = im->xsize-1; + if (x0 <= x1) + memset(im->image8[y0] + x0, (UINT8) ink, x1 - x0 + 1); + } +} + +static inline void +hline32(Imaging im, int x0, int y0, int x1, int ink) +{ + int tmp; + INT32* p; + + if (y0 >= 0 && y0 < im->ysize) { + if (x0 > x1) + tmp = x0, x0 = x1, x1 = tmp; + if (x0 < 0) + x0 = 0; + else if (x0 >= im->xsize) + return; + if (x1 < 0) + return; + else if (x1 >= im->xsize) + x1 = im->xsize-1; + p = im->image32[y0]; + while (x0 <= x1) + p[x0++] = ink; + } +} + +static inline void +hline32rgba(Imaging im, int x0, int y0, int x1, int ink) +{ + int tmp; + unsigned int tmp1, tmp2; + + if (y0 >= 0 && y0 < im->ysize) { + if (x0 > x1) + tmp = x0, x0 = x1, x1 = tmp; + if (x0 < 0) + x0 = 0; + else if (x0 >= im->xsize) + return; + if (x1 < 0) + return; + else if (x1 >= im->xsize) + x1 = im->xsize-1; + if (x0 <= x1) { + UINT8* out = (UINT8*) im->image[y0]+x0*4; + UINT8* in = (UINT8*) &ink; + while (x0 <= x1) { + out[0] = BLEND(in[3], out[0], in[0], tmp1, tmp2); + out[1] = BLEND(in[3], out[1], in[1], tmp1, tmp2); + out[2] = BLEND(in[3], out[2], in[2], tmp1, tmp2); + x0++; out += 4; + } + } + } +} + +static inline void +line8(Imaging im, int x0, int y0, int x1, int y1, int ink) +{ + int i, n, e; + int dx, dy; + int xs, ys; + + /* normalize coordinates */ + dx = x1-x0; + if (dx < 0) + dx = -dx, xs = -1; + else + xs = 1; + dy = y1-y0; + if (dy < 0) + dy = -dy, ys = -1; + else + ys = 1; + + n = (dx > dy) ? dx : dy; + + if (dx == 0) + + /* vertical */ + for (i = 0; i < dy; i++) { + point8(im, x0, y0, ink); + y0 += ys; + } + + else if (dy == 0) + + /* horizontal */ + for (i = 0; i < dx; i++) { + point8(im, x0, y0, ink); + x0 += xs; + } + + else if (dx > dy) { + + /* bresenham, horizontal slope */ + n = dx; + dy += dy; + e = dy - dx; + dx += dx; + + for (i = 0; i < n; i++) { + point8(im, x0, y0, ink); + if (e >= 0) { + y0 += ys; + e -= dx; + } + e += dy; + x0 += xs; + } + + } else { + + /* bresenham, vertical slope */ + n = dy; + dx += dx; + e = dx - dy; + dy += dy; + + for (i = 0; i < n; i++) { + point8(im, x0, y0, ink); + if (e >= 0) { + x0 += xs; + e -= dy; + } + e += dx; + y0 += ys; + } + + } +} + +static inline void +line32(Imaging im, int x0, int y0, int x1, int y1, int ink) +{ + int i, n, e; + int dx, dy; + int xs, ys; + + /* normalize coordinates */ + dx = x1-x0; + if (dx < 0) + dx = -dx, xs = -1; + else + xs = 1; + dy = y1-y0; + if (dy < 0) + dy = -dy, ys = -1; + else + ys = 1; + + n = (dx > dy) ? dx : dy; + + if (dx == 0) + + /* vertical */ + for (i = 0; i < dy; i++) { + point32(im, x0, y0, ink); + y0 += ys; + } + + else if (dy == 0) + + /* horizontal */ + for (i = 0; i < dx; i++) { + point32(im, x0, y0, ink); + x0 += xs; + } + + else if (dx > dy) { + + /* bresenham, horizontal slope */ + n = dx; + dy += dy; + e = dy - dx; + dx += dx; + + for (i = 0; i < n; i++) { + point32(im, x0, y0, ink); + if (e >= 0) { + y0 += ys; + e -= dx; + } + e += dy; + x0 += xs; + } + + } else { + + /* bresenham, vertical slope */ + n = dy; + dx += dx; + e = dx - dy; + dy += dy; + + for (i = 0; i < n; i++) { + point32(im, x0, y0, ink); + if (e >= 0) { + x0 += xs; + e -= dy; + } + e += dx; + y0 += ys; + } + + } +} + +static inline void +line32rgba(Imaging im, int x0, int y0, int x1, int y1, int ink) +{ + int i, n, e; + int dx, dy; + int xs, ys; + + /* normalize coordinates */ + dx = x1-x0; + if (dx < 0) + dx = -dx, xs = -1; + else + xs = 1; + dy = y1-y0; + if (dy < 0) + dy = -dy, ys = -1; + else + ys = 1; + + n = (dx > dy) ? dx : dy; + + if (dx == 0) + + /* vertical */ + for (i = 0; i < dy; i++) { + point32rgba(im, x0, y0, ink); + y0 += ys; + } + + else if (dy == 0) + + /* horizontal */ + for (i = 0; i < dx; i++) { + point32rgba(im, x0, y0, ink); + x0 += xs; + } + + else if (dx > dy) { + + /* bresenham, horizontal slope */ + n = dx; + dy += dy; + e = dy - dx; + dx += dx; + + for (i = 0; i < n; i++) { + point32rgba(im, x0, y0, ink); + if (e >= 0) { + y0 += ys; + e -= dx; + } + e += dy; + x0 += xs; + } + + } else { + + /* bresenham, vertical slope */ + n = dy; + dx += dx; + e = dx - dy; + dy += dy; + + for (i = 0; i < n; i++) { + point32rgba(im, x0, y0, ink); + if (e >= 0) { + x0 += xs; + e -= dy; + } + e += dx; + y0 += ys; + } + + } +} + +static int +x_cmp(const void *x0, const void *x1) +{ + float diff = *((float*)x0) - *((float*)x1); + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else + return 0; +} + +static inline int +polygon8(Imaging im, int n, Edge *e, int ink, int eofill) +{ + int i, j; + float *xx; + int ymin, ymax; + float y; + + if (n <= 0) + return 0; + + /* Find upper and lower polygon boundary (within image) */ + + ymin = e[0].ymin; + ymax = e[0].ymax; + for (i = 1; i < n; i++) { + if (e[i].ymin < ymin) ymin = e[i].ymin; + if (e[i].ymax > ymax) ymax = e[i].ymax; + } + + if (ymin < 0) + ymin = 0; + if (ymax >= im->ysize) + ymax = im->ysize-1; + + /* Process polygon edges */ + + xx = malloc(n * sizeof(float)); + if (!xx) + return -1; + + for (;ymin <= ymax; ymin++) { + y = ymin+0.5F; + for (i = j = 0; i < n; i++) + if (y >= e[i].ymin && y <= e[i].ymax) { + if (e[i].d == 0) + hline8(im, e[i].xmin, ymin, e[i].xmax, ink); + else + xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0; + } + if (j == 2) { + if (xx[0] < xx[1]) + hline8(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); + else + hline8(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink); + } else { + qsort(xx, j, sizeof(float), x_cmp); + for (i = 0; i < j-1 ; i += 2) + hline8(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); + } + } + + free(xx); + + return 0; +} + +static inline int +polygon32(Imaging im, int n, Edge *e, int ink, int eofill) +{ + int i, j; + float *xx; + int ymin, ymax; + float y; + + if (n <= 0) + return 0; + + /* Find upper and lower polygon boundary (within image) */ + + ymin = e[0].ymin; + ymax = e[0].ymax; + for (i = 1; i < n; i++) { + if (e[i].ymin < ymin) ymin = e[i].ymin; + if (e[i].ymax > ymax) ymax = e[i].ymax; + } + + if (ymin < 0) + ymin = 0; + if (ymax >= im->ysize) + ymax = im->ysize-1; + + /* Process polygon edges */ + + xx = malloc(n * sizeof(float)); + if (!xx) + return -1; + + for (;ymin <= ymax; ymin++) { + y = ymin+0.5F; + for (i = j = 0; i < n; i++) { + if (y >= e[i].ymin && y <= e[i].ymax) { + if (e[i].d == 0) + hline32(im, e[i].xmin, ymin, e[i].xmax, ink); + else + xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0; + } + } + if (j == 2) { + if (xx[0] < xx[1]) + hline32(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); + else + hline32(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink); + } else { + qsort(xx, j, sizeof(float), x_cmp); + for (i = 0; i < j-1 ; i += 2) + hline32(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); + } + } + + free(xx); + + return 0; +} + +static inline int +polygon32rgba(Imaging im, int n, Edge *e, int ink, int eofill) +{ + int i, j; + float *xx; + int ymin, ymax; + float y; + + if (n <= 0) + return 0; + + /* Find upper and lower polygon boundary (within image) */ + + ymin = e[0].ymin; + ymax = e[0].ymax; + for (i = 1; i < n; i++) { + if (e[i].ymin < ymin) ymin = e[i].ymin; + if (e[i].ymax > ymax) ymax = e[i].ymax; + } + + if (ymin < 0) + ymin = 0; + if (ymax >= im->ysize) + ymax = im->ysize-1; + + /* Process polygon edges */ + + xx = malloc(n * sizeof(float)); + if (!xx) + return -1; + + for (;ymin <= ymax; ymin++) { + y = ymin+0.5F; + for (i = j = 0; i < n; i++) { + if (y >= e[i].ymin && y <= e[i].ymax) { + if (e[i].d == 0) + hline32rgba(im, e[i].xmin, ymin, e[i].xmax, ink); + else + xx[j++] = (y-e[i].y0) * e[i].dx + e[i].x0; + } + } + if (j == 2) { + if (xx[0] < xx[1]) + hline32rgba(im, CEIL(xx[0]-0.5), ymin, FLOOR(xx[1]+0.5), ink); + else + hline32rgba(im, CEIL(xx[1]-0.5), ymin, FLOOR(xx[0]+0.5), ink); + } else { + qsort(xx, j, sizeof(float), x_cmp); + for (i = 0; i < j-1 ; i += 2) + hline32rgba(im, CEIL(xx[i]-0.5), ymin, FLOOR(xx[i+1]+0.5), ink); + } + } + + free(xx); + + return 0; +} + +static inline void +add_edge(Edge *e, int x0, int y0, int x1, int y1) +{ + /* printf("edge %d %d %d %d\n", x0, y0, x1, y1); */ + + if (x0 <= x1) + e->xmin = x0, e->xmax = x1; + else + e->xmin = x1, e->xmax = x0; + + if (y0 <= y1) + e->ymin = y0, e->ymax = y1; + else + e->ymin = y1, e->ymax = y0; + + if (y0 == y1) { + e->d = 0; + e->dx = 0.0; + } else { + e->dx = ((float)(x1-x0)) / (y1-y0); + if (y0 == e->ymin) + e->d = 1; + else + e->d = -1; + } + + e->x0 = x0; + e->y0 = y0; +} + +typedef struct { + void (*point)(Imaging im, int x, int y, int ink); + void (*hline)(Imaging im, int x0, int y0, int x1, int ink); + void (*line)(Imaging im, int x0, int y0, int x1, int y1, int ink); + int (*polygon)(Imaging im, int n, Edge *e, int ink, int eofill); +} DRAW; + +DRAW draw8 = { point8, hline8, line8, polygon8 }; +DRAW draw32 = { point32, hline32, line32, polygon32 }; +DRAW draw32rgba = { point32rgba, hline32rgba, line32rgba, polygon32rgba }; + +/* -------------------------------------------------------------------- */ +/* Interface */ +/* -------------------------------------------------------------------- */ + +#define DRAWINIT()\ + if (im->image8) {\ + draw = &draw8;\ + ink = INK8(ink_);\ + } else {\ + draw = (op) ? &draw32rgba : &draw32; \ + ink = INK32(ink_);\ + } + +int +ImagingDrawPoint(Imaging im, int x0, int y0, const void* ink_, int op) +{ + DRAW* draw; + INT32 ink; + + DRAWINIT(); + + draw->point(im, x0, y0, ink); + + return 0; +} + +int +ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, const void* ink_, + int op) +{ + DRAW* draw; + INT32 ink; + + DRAWINIT(); + + draw->line(im, x0, y0, x1, y1, ink); + + return 0; +} + +int +ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, + const void* ink_, int width, int op) +{ + DRAW* draw; + INT32 ink; + + Edge e[4]; + + int dx, dy; + double d; + + DRAWINIT(); + + if (width <= 1) { + draw->line(im, x0, y0, x1, y1, ink); + return 0; + } + + dx = x1-x0; + dy = y1-y0; + + if (dx == 0 && dy == 0) { + draw->point(im, x0, y0, ink); + return 0; + } + + d = width / sqrt((float) (dx*dx + dy*dy)) / 2.0; + + dx = (int) floor(d * (y1-y0) + 0.5); + dy = (int) floor(d * (x1-x0) + 0.5); + + add_edge(e+0, x0 - dx, y0 + dy, x1 - dx, y1 + dy); + add_edge(e+1, x1 - dx, y1 + dy, x1 + dx, y1 - dy); + add_edge(e+2, x1 + dx, y1 - dy, x0 + dx, y0 - dy); + add_edge(e+3, x0 + dx, y0 - dy, x0 - dx, y0 + dy); + + draw->polygon(im, 4, e, ink, 0); + + return 0; +} + +int +ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, + const void* ink_, int fill, int op) +{ + int y; + int tmp; + DRAW* draw; + INT32 ink; + + DRAWINIT(); + + if (y0 > y1) + tmp = y0, y0 = y1, y1 = tmp; + + if (fill) { + + if (y0 < 0) + y0 = 0; + else if (y0 >= im->ysize) + return 0; + + if (y1 < 0) + return 0; + else if (y1 > im->ysize) + y1 = im->ysize; + + for (y = y0; y <= y1; y++) + draw->hline(im, x0, y, x1, ink); + + } else { + + /* outline */ + draw->line(im, x0, y0, x1, y0, ink); + draw->line(im, x1, y0, x1, y1, ink); + draw->line(im, x1, y1, x0, y1, ink); + draw->line(im, x0, y1, x0, y0, ink); + + } + + return 0; +} + +int +ImagingDrawPolygon(Imaging im, int count, int* xy, const void* ink_, + int fill, int op) +{ + int i, n; + DRAW* draw; + INT32 ink; + + if (count <= 0) + return 0; + + DRAWINIT(); + + if (fill) { + + /* Build edge list */ + Edge* e = malloc(count * sizeof(Edge)); + if (!e) { + (void) ImagingError_MemoryError(); + return -1; + } + for (i = n = 0; i < count-1; i++) + add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3]); + if (xy[i+i] != xy[0] || xy[i+i+1] != xy[1]) + add_edge(&e[n++], xy[i+i], xy[i+i+1], xy[0], xy[1]); + draw->polygon(im, n, e, ink, 0); + free(e); + + } else { + + /* Outline */ + for (i = 0; i < count-1; i++) + draw->line(im, xy[i+i], xy[i+i+1], xy[i+i+2], xy[i+i+3], ink); + draw->line(im, xy[i+i], xy[i+i+1], xy[0], xy[1], ink); + + } + + return 0; +} + +int +ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, const void* ink, + int op) +{ + return ImagingFill2( + im, ink, bitmap, + x0, y0, x0 + bitmap->xsize, y0 + bitmap->ysize + ); +} + +/* -------------------------------------------------------------------- */ +/* standard shapes */ + +#define ARC 0 +#define CHORD 1 +#define PIESLICE 2 + +static int +ellipse(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink_, int fill, + int mode, int op) +{ + int i, n; + int cx, cy; + int w, h; + int x = 0, y = 0; + int lx = 0, ly = 0; + int sx = 0, sy = 0; + DRAW* draw; + INT32 ink; + + w = x1 - x0; + h = y1 - y0; + if (w < 0 || h < 0) + return 0; + + DRAWINIT(); + + cx = (x0 + x1) / 2; + cy = (y0 + y1) / 2; + + while (end < start) + end += 360; + + if (mode != ARC && fill) { + + /* Build edge list */ + Edge* e = malloc((end - start + 3) * sizeof(Edge)); + if (!e) { + ImagingError_MemoryError(); + return -1; + } + + n = 0; + + for (i = start; i <= end; i++) { + x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); + y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); + if (i != start) + add_edge(&e[n++], lx, ly, x, y); + else + sx = x, sy = y; + lx = x, ly = y; + } + + if (n > 0) { + /* close and draw polygon */ + if (mode == PIESLICE) { + if (x != cx || y != cy) { + add_edge(&e[n++], x, y, cx, cy); + add_edge(&e[n++], cx, cy, sx, sy); + } + } else { + if (x != sx || y != sy) + add_edge(&e[n++], x, y, sx, sy); + } + draw->polygon(im, n, e, ink, 0); + } + + free(e); + + } else { + + for (i = start; i <= end; i++) { + x = FLOOR((cos(i*M_PI/180) * w/2) + cx + 0.5); + y = FLOOR((sin(i*M_PI/180) * h/2) + cy + 0.5); + if (i != start) + draw->line(im, lx, ly, x, y, ink); + else + sx = x, sy = y; + lx = x, ly = y; + } + + if (i != start) { + if (mode == PIESLICE) { + if (x != cx || y != cy) { + draw->line(im, x, y, cx, cy, ink); + draw->line(im, cx, cy, sx, sy, ink); + } + } else if (mode == CHORD) { + if (x != sx || y != sy) + draw->line(im, x, y, sx, sy, ink); + } + } + } + + return 0; +} + +int +ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int op) +{ + return ellipse(im, x0, y0, x1, y1, start, end, ink, 0, ARC, op); +} + +int +ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int fill, int op) +{ + return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, CHORD, op); +} + +int +ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, + const void* ink, int fill, int op) +{ + return ellipse(im, x0, y0, x1, y1, 0, 360, ink, fill, CHORD, op); +} + +int +ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int fill, int op) +{ + return ellipse(im, x0, y0, x1, y1, start, end, ink, fill, PIESLICE, op); +} + +/* -------------------------------------------------------------------- */ + +/* experimental level 2 ("arrow") graphics stuff. this implements + portions of the arrow api on top of the Edge structure. the + semantics are ok, except that "curve" flattens the bezier curves by + itself */ + +#if 1 /* ARROW_GRAPHICS */ + +struct ImagingOutlineInstance { + + float x0, y0; + + float x, y; + + int count; + Edge *edges; + + int size; + +}; + + +ImagingOutline +ImagingOutlineNew(void) +{ + ImagingOutline outline; + + outline = calloc(1, sizeof(struct ImagingOutlineInstance)); + if (!outline) + return (ImagingOutline) ImagingError_MemoryError(); + + outline->edges = NULL; + outline->count = outline->size = 0; + + ImagingOutlineMove(outline, 0, 0); + + return outline; +} + +void +ImagingOutlineDelete(ImagingOutline outline) +{ + if (!outline) + return; + + if (outline->edges) + free(outline->edges); + + free(outline); +} + + +static Edge* +allocate(ImagingOutline outline, int extra) +{ + Edge* e; + + if (outline->count + extra > outline->size) { + /* expand outline buffer */ + outline->size += extra + 25; + if (!outline->edges) + e = malloc(outline->size * sizeof(Edge)); + else + e = realloc(outline->edges, outline->size * sizeof(Edge)); + if (!e) + return NULL; + outline->edges = e; + } + + e = outline->edges + outline->count; + + outline->count += extra; + + return e; +} + +int +ImagingOutlineMove(ImagingOutline outline, float x0, float y0) +{ + outline->x = outline->x0 = x0; + outline->y = outline->y0 = y0; + + return 0; +} + +int +ImagingOutlineLine(ImagingOutline outline, float x1, float y1) +{ + Edge* e; + + e = allocate(outline, 1); + if (!e) + return -1; /* out of memory */ + + add_edge(e, (int) outline->x, (int) outline->y, (int) x1, (int) y1); + + outline->x = x1; + outline->y = y1; + + return 0; +} + +int +ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, + float x2, float y2, float x3, float y3) +{ + Edge* e; + int i; + float xo, yo; + +#define STEPS 32 + + e = allocate(outline, STEPS); + if (!e) + return -1; /* out of memory */ + + xo = outline->x; + yo = outline->y; + + /* flatten the bezier segment */ + + for (i = 1; i <= STEPS; i++) { + + float t = ((float) i) / STEPS; + float t2 = t*t; + float t3 = t2*t; + + float u = 1.0F - t; + float u2 = u*u; + float u3 = u2*u; + + float x = outline->x*u3 + 3*(x1*t*u2 + x2*t2*u) + x3*t3 + 0.5; + float y = outline->y*u3 + 3*(y1*t*u2 + y2*t2*u) + y3*t3 + 0.5; + + add_edge(e++, xo, yo, (int) x, (int) y); + + xo = x, yo = y; + + } + + outline->x = xo; + outline->y = yo; + + return 0; +} + +int +ImagingOutlineCurve2(ImagingOutline outline, float cx, float cy, + float x3, float y3) +{ + /* add bezier curve based on three control points (as + in the Flash file format) */ + + return ImagingOutlineCurve( + outline, + (outline->x + cx + cx)/3, (outline->y + cy + cy)/3, + (cx + cx + x3)/3, (cy + cy + y3)/3, + x3, y3); +} + +int +ImagingOutlineClose(ImagingOutline outline) +{ + if (outline->x == outline->x0 && outline->y == outline->y0) + return 0; + return ImagingOutlineLine(outline, outline->x0, outline->y0); +} + +int +ImagingOutlineTransform(ImagingOutline outline, double a[6]) +{ + Edge *eIn; + Edge *eOut; + int i, n; + int x0, y0, x1, y1; + int X0, Y0, X1, Y1; + + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; + double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; + + eIn = outline->edges; + n = outline->count; + + /* FIXME: ugly! */ + outline->edges = NULL; + outline->count = outline->size = 0; + + eOut = allocate(outline, n); + if (!eOut) { + outline->edges = eIn; + outline->count = outline->size = n; + ImagingError_MemoryError(); + return -1; + } + + for (i = 0; i < n; i++) { + + x0 = eIn->x0; + y0 = eIn->y0; + + /* FIXME: ouch! */ + if (eIn->x0 == eIn->xmin) + x1 = eIn->xmax; + else + x1 = eIn->xmin; + if (eIn->y0 == eIn->ymin) + y1 = eIn->ymax; + else + y1 = eIn->ymin; + + /* full moon tonight! if this doesn't work, you may need to + upgrade your compiler (make sure you have the right service + pack) */ + + X0 = (int) (a0*x0 + a1*y0 + a2); + Y0 = (int) (a3*x0 + a4*y0 + a5); + X1 = (int) (a0*x1 + a1*y1 + a2); + Y1 = (int) (a3*x1 + a4*y1 + a5); + + add_edge(eOut, X0, Y0, X1, Y1); + + eIn++; + eOut++; + + } + + free(eIn); + + return 0; +} + +int +ImagingDrawOutline(Imaging im, ImagingOutline outline, const void* ink_, + int fill, int op) +{ + DRAW* draw; + INT32 ink; + + DRAWINIT(); + + draw->polygon(im, outline->count, outline->edges, ink, 0); + + return 0; +} + +#endif diff --git a/libImaging/Effects.c b/libImaging/Effects.c new file mode 100644 index 000000000..1b964ac40 --- /dev/null +++ b/libImaging/Effects.c @@ -0,0 +1,373 @@ +/* + * The Python Imaging Library + * $Id$ + * + * various special effects and image generators + * + * history: + * 1997-05-21 fl Just for fun + * 1997-06-05 fl Added mandelbrot generator + * 2003-05-24 fl Added perlin_turbulence generator (in progress) + * + * Copyright (c) 1997-2003 by Fredrik Lundh. + * Copyright (c) 1997 by Secret Labs AB. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include + +Imaging +ImagingEffectMandelbrot(int xsize, int ysize, double extent[4], int quality) +{ + /* Generate a Mandelbrot set covering the given extent */ + + Imaging im; + int x, y, k; + double width, height; + double x1, y1, xi2, yi2, cr, ci, radius; + double dr, di; + + /* Check arguments */ + width = extent[2] - extent[0]; + height = extent[3] - extent[1]; + if (width < 0.0 || height < 0.0 || quality < 2) + return (Imaging) ImagingError_ValueError(NULL); + + im = ImagingNew("L", xsize, ysize); + if (!im) + return NULL; + + dr = width/(xsize-1); + di = height/(ysize-1); + + radius = 100.0; + + for (y = 0; y < ysize; y++) { + UINT8* buf = im->image8[y]; + for (x = 0; x < xsize; x++) { + x1 = y1 = xi2 = yi2 = 0.0; + cr = x*dr + extent[0]; + ci = y*di + extent[1]; + for (k = 1;; k++) { + y1 = 2*x1*y1 + ci; + x1 = xi2 - yi2 + cr; + xi2 = x1*x1; + yi2 = y1*y1; + if ((xi2 + yi2) > radius) { + buf[x] = k*255/quality; + break; + } + if (k > quality) { + buf[x] = 0; + break; + } + } + } + } + return im; +} + +Imaging +ImagingEffectNoise(int xsize, int ysize, float sigma) +{ + /* Generate gaussian noise centered around 128 */ + + Imaging imOut; + int x, y; + int nextok; + double this, next; + + imOut = ImagingNew("L", xsize, ysize); + if (!imOut) + return NULL; + + next = 0.0; + nextok = 0; + + for (y = 0; y < imOut->ysize; y++) { + UINT8* out = imOut->image8[y]; + for (x = 0; x < imOut->xsize; x++) { + if (nextok) { + this = next; + nextok = 0; + } else { + /* after numerical recepies */ + double v1, v2, radius, factor; + do { + v1 = rand()*(2.0/32767.0) - 1.0; + v2 = rand()*(2.0/32767.0) - 1.0; + radius= v1*v1 + v2*v2; + } while (radius >= 1.0); + factor = sqrt(-2.0*log(radius)/radius); + this = factor * v1; + next = factor * v2; + } + out[x] = (unsigned char) (128 + sigma * this); + } + } + + return imOut; +} + +Imaging +ImagingEffectPerlinTurbulence(int xsize, int ysize) +{ + /* Perlin turbulence (In progress) */ + + return NULL; +} + +Imaging +ImagingEffectSpread(Imaging imIn, int distance) +{ + /* Randomly spread pixels in an image */ + + Imaging imOut; + int x, y; + + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + + if (!imOut) + return NULL; + +#define SPREAD(type, image)\ + for (y = 0; y < imIn->ysize; y++)\ + for (x = 0; x < imIn->xsize; x++) {\ + int xx = x + (rand() % distance) - distance/2;\ + int yy = y + (rand() % distance) - distance/2;\ + if (xx >= 0 && xx < imIn->xsize && yy >= 0 && yy < imIn->ysize) {\ + imOut->image[yy][xx] = imIn->image[y][x];\ + imOut->image[y][x] = imIn->image[yy][xx];\ + } else\ + imOut->image[y][x] = imIn->image[y][x];\ + } + + if (imIn->image8) { + SPREAD(UINT8, image8); + } else { + SPREAD(INT32, image32); + } + + ImagingCopyInfo(imOut, imIn); + + return imOut; +} + +/* -------------------------------------------------------------------- */ +/* Taken from the "C" code in the W3C SVG specification. Translated + to C89 by Fredrik Lundh */ + +#if 0 + +/* Produces results in the range [1, 2**31 - 2]. +Algorithm is: r = (a * r) mod m +where a = 16807 and m = 2**31 - 1 = 2147483647 +See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 +To test: the algorithm should produce the result 1043618065 +as the 10,000th generated number if the original seed is 1. +*/ +#define RAND_m 2147483647 /* 2**31 - 1 */ +#define RAND_a 16807 /* 7**5; primitive root of m */ +#define RAND_q 127773 /* m / a */ +#define RAND_r 2836 /* m % a */ + +static long +perlin_setup_seed(long lSeed) +{ + if (lSeed <= 0) lSeed = -(lSeed % (RAND_m - 1)) + 1; + if (lSeed > RAND_m - 1) lSeed = RAND_m - 1; + return lSeed; +} + +static long +perlin_random(long lSeed) +{ + long result; + result = RAND_a * (lSeed % RAND_q) - RAND_r * (lSeed / RAND_q); + if (result <= 0) result += RAND_m; + return result; +} + +#define BSize 0x100 +#define BM 0xff +#define PerlinN 0x1000 +#define NP 12 /* 2^PerlinN */ +#define NM 0xfff +static int perlin_uLatticeSelector[BSize + BSize + 2]; +static double perlin_fGradient[4][BSize + BSize + 2][2]; +typedef struct +{ + int nWidth; /* How much to subtract to wrap for stitching. */ + int nHeight; + int nWrapX; /* Minimum value to wrap. */ + int nWrapY; +} StitchInfo; + +static void +perlin_init(long lSeed) +{ + double s; + int i, j, k; + lSeed = perlin_setup_seed(lSeed); + for(k = 0; k < 4; k++) + { + for(i = 0; i < BSize; i++) + { + perlin_uLatticeSelector[i] = i; + for (j = 0; j < 2; j++) + perlin_fGradient[k][i][j] = (double)(((lSeed = perlin_random(lSeed)) % (BSize + BSize)) - BSize) / BSize; + s = (double) (sqrt(perlin_fGradient[k][i][0] * perlin_fGradient[k][i][0] + perlin_fGradient[k][i][1] * perlin_fGradient[k][i][1])); + perlin_fGradient[k][i][0] /= s; + perlin_fGradient[k][i][1] /= s; + } + } + while(--i) + { + k = perlin_uLatticeSelector[i]; + perlin_uLatticeSelector[i] = perlin_uLatticeSelector[j = (lSeed = perlin_random(lSeed)) % BSize]; + perlin_uLatticeSelector[j] = k; + } + for(i = 0; i < BSize + 2; i++) + { + perlin_uLatticeSelector[BSize + i] = perlin_uLatticeSelector[i]; + for(k = 0; k < 4; k++) + for(j = 0; j < 2; j++) + perlin_fGradient[k][BSize + i][j] = perlin_fGradient[k][i][j]; + } +} + +#define s_curve(t) ( t * t * (3. - 2. * t) ) +#define lerp(t, a, b) ( a + t * (b - a) ) +static double +perlin_noise2(int nColorChannel, double vec[2], StitchInfo *pStitchInfo) +{ + int bx0, bx1, by0, by1, b00, b10, b01, b11; + double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; + register int i, j; + + t = vec[0] + (double) PerlinN; + bx0 = (int)t; + bx1 = bx0+1; + rx0 = t - (int)t; + rx1 = rx0 - 1.0f; + t = vec[1] + (double) PerlinN; + by0 = (int)t; + by1 = by0+1; + ry0 = t - (int)t; + ry1 = ry0 - 1.0f; + + /* If stitching, adjust lattice points accordingly. */ + if(pStitchInfo != NULL) + { + if(bx0 >= pStitchInfo->nWrapX) + bx0 -= pStitchInfo->nWidth; + if(bx1 >= pStitchInfo->nWrapX) + bx1 -= pStitchInfo->nWidth; + if(by0 >= pStitchInfo->nWrapY) + by0 -= pStitchInfo->nHeight; + if(by1 >= pStitchInfo->nWrapY) + by1 -= pStitchInfo->nHeight; + } + + bx0 &= BM; + bx1 &= BM; + by0 &= BM; + by1 &= BM; + + i = perlin_uLatticeSelector[bx0]; + j = perlin_uLatticeSelector[bx1]; + b00 = perlin_uLatticeSelector[i + by0]; + b10 = perlin_uLatticeSelector[j + by0]; + b01 = perlin_uLatticeSelector[i + by1]; + b11 = perlin_uLatticeSelector[j + by1]; + sx = (double) (s_curve(rx0)); + sy = (double) (s_curve(ry0)); + q = perlin_fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1]; + q = perlin_fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1]; + a = lerp(sx, u, v); + q = perlin_fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1]; + q = perlin_fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1]; + b = lerp(sx, u, v); + return lerp(sy, a, b); +} + +double +perlin_turbulence( + int nColorChannel, double *point, double fBaseFreqX, double fBaseFreqY, + int nNumOctaves, int bFractalSum, int bDoStitching, + double fTileX, double fTileY, double fTileWidth, double fTileHeight) +{ + StitchInfo stitch; + StitchInfo *pStitchInfo = NULL; /* Not stitching when NULL. */ + + double fSum = 0.0f; + double vec[2]; + double ratio = 1; + + int nOctave; + + vec[0] = point[0] * fBaseFreqX; + vec[1] = point[1] * fBaseFreqY; + + /* Adjust the base frequencies if necessary for stitching. */ + if(bDoStitching) + { + /* When stitching tiled turbulence, the frequencies must be adjusted */ + /* so that the tile borders will be continuous. */ + if(fBaseFreqX != 0.0) + { + double fLoFreq = (double) (floor(fTileWidth * fBaseFreqX)) / fTileWidth; + double fHiFreq = (double) (ceil(fTileWidth * fBaseFreqX)) / fTileWidth; + if(fBaseFreqX / fLoFreq < fHiFreq / fBaseFreqX) + fBaseFreqX = fLoFreq; + else + fBaseFreqX = fHiFreq; + } + + if(fBaseFreqY != 0.0) + { + double fLoFreq = (double) (floor(fTileHeight * fBaseFreqY)) / fTileHeight; + double fHiFreq = (double) (ceil(fTileHeight * fBaseFreqY)) / fTileHeight; + if(fBaseFreqY / fLoFreq < fHiFreq / fBaseFreqY) + fBaseFreqY = fLoFreq; + else + fBaseFreqY = fHiFreq; + } + + /* Set up initial stitch values. */ + pStitchInfo = &stitch; + stitch.nWidth = (int) (fTileWidth * fBaseFreqX + 0.5f); + stitch.nWrapX = (int) (fTileX * fBaseFreqX + PerlinN + stitch.nWidth); + stitch.nHeight = (int) (fTileHeight * fBaseFreqY + 0.5f); + stitch.nWrapY = (int) (fTileY * fBaseFreqY + PerlinN + stitch.nHeight); + } + + for(nOctave = 0; nOctave < nNumOctaves; nOctave++) + { + if(bFractalSum) + fSum += (double) (perlin_noise2(nColorChannel, vec, pStitchInfo) / ratio); + else + fSum += (double) (fabs(perlin_noise2(nColorChannel, vec, pStitchInfo)) / ratio); + + vec[0] *= 2; + vec[1] *= 2; + ratio *= 2; + + if(pStitchInfo != NULL) + { + /* Update stitch values. Subtracting PerlinN before the multiplication and */ + /* adding it afterward simplifies to subtracting it once. */ + stitch.nWidth *= 2; + stitch.nWrapX = 2 * stitch.nWrapX - PerlinN; + stitch.nHeight *= 2; + stitch.nWrapY = 2 * stitch.nWrapY - PerlinN; + } + } + return fSum; +} + +#endif diff --git a/libImaging/EpsEncode.c b/libImaging/EpsEncode.c new file mode 100644 index 000000000..704d5a656 --- /dev/null +++ b/libImaging/EpsEncode.c @@ -0,0 +1,80 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * encoder for EPS hex data + * + * history: + * 96-04-19 fl created + * 96-06-27 fl don't drop last block of encoded data + * + * notes: + * FIXME: rename to HexEncode.c ?? + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingEpsEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + enum { HEXBYTE=1, NEWLINE }; + const char *hex = "0123456789abcdef"; + + UINT8* ptr = buf; + UINT8* in, i; + + if (!state->state) { + state->state = HEXBYTE; + state->xsize *= im->pixelsize; /* Hack! */ + } + + in = (UINT8*) im->image[state->y]; + + for (;;) { + + if (state->state == NEWLINE) { + if (bytes < 1) + break; + *ptr++ = '\n'; + bytes--; + state->state = HEXBYTE; + } + + if (bytes < 2) + break; + + i = in[state->x++]; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; + bytes -= 2; + + /* Skip junk bytes */ + if (im->bands == 3 && (state->x & 3) == 3) + state->x++; + + if (++state->count >= 79/2) { + state->state = NEWLINE; + state->count = 0; + } + + if (state->x >= state->xsize) { + state->x = 0; + if (++state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_END; + break; + } + in = (UINT8*) im->image[state->y]; + } + + } + + return ptr - buf; + +} diff --git a/libImaging/Except.c b/libImaging/Except.c new file mode 100644 index 000000000..635435d57 --- /dev/null +++ b/libImaging/Except.c @@ -0,0 +1,83 @@ +/* + * The Python Imaging Library + * $Id$ + * + * default exception handling + * + * This module is usually overridden by application code (e.g. + * _imaging.c for PIL's standard Python bindings). If you get + * linking errors, remove this file from your project/library. + * + * history: + * 1995-06-15 fl Created + * 1998-12-29 fl Minor tweaks + * 2003-09-13 fl Added ImagingEnter/LeaveSection() + * + * Copyright (c) 1997-2003 by Secret Labs AB. + * Copyright (c) 1995-2003 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +/* exception state */ + +void * +ImagingError_IOError(void) +{ + fprintf(stderr, "*** exception: file access error\n"); + return NULL; +} + +void * +ImagingError_MemoryError(void) +{ + fprintf(stderr, "*** exception: out of memory\n"); + return NULL; +} + +void * +ImagingError_ModeError(void) +{ + return ImagingError_ValueError("bad image mode"); + return NULL; +} + +void * +ImagingError_Mismatch(void) +{ + return ImagingError_ValueError("images don't match"); + return NULL; +} + +void * +ImagingError_ValueError(const char *message) +{ + if (!message) + message = "exception: bad argument to function"; + fprintf(stderr, "*** %s\n", message); + return NULL; +} + +void +ImagingError_Clear(void) +{ + /* nop */; +} + +/* thread state */ + +void +ImagingSectionEnter(ImagingSectionCookie* cookie) +{ + /* pass */ +} + +void +ImagingSectionLeave(ImagingSectionCookie* cookie) +{ + /* pass */ +} diff --git a/libImaging/File.c b/libImaging/File.c new file mode 100644 index 000000000..6f454d1ca --- /dev/null +++ b/libImaging/File.c @@ -0,0 +1,193 @@ +/* + * The Python Imaging Library + * $Id$ + * + * built-in image file handling + * + * history: + * 1995-11-26 fl Created, supports PGM/PPM + * 1996-08-07 fl Write "1" images as PGM + * 1999-02-21 fl Don't write non-standard modes + * + * Copyright (c) 1997-99 by Secret Labs AB. + * Copyright (c) 1995-96 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include + +Imaging +ImagingOpenPPM(const char* infile) +{ + FILE* fp; + int i, c, v; + char* mode; + int x, y, max; + Imaging im; + + if (!infile) + return ImagingError_ValueError(NULL); + + fp = fopen(infile, "rb"); + if (!fp) + return ImagingError_IOError(); + + /* PPM magic */ + if (fgetc(fp) != 'P') + goto error; + switch (fgetc(fp)) { + case '4': /* FIXME: 1-bit images are not yet supported */ + goto error; + case '5': + mode = "L"; + break; + case '6': + mode = "RGB"; + break; + default: + goto error; + } + + i = 0; + c = fgetc(fp); + + x = y = max = 0; + + while (i < 3) { + + /* Ignore optional comment fields */ + while (c == '\n') { + c = fgetc(fp); + if (c == '#') { + do { + c = fgetc(fp); + if (c == EOF) + goto error; + } while (c != '\n'); + c = fgetc(fp); + } + } + + /* Skip forward to next value */ + while (isspace(c)) + c = fgetc(fp); + + /* And parse it */ + v = 0; + while (isdigit(c)) { + v = v * 10 + (c - '0'); + c = fgetc(fp); + } + + if (c == EOF) + goto error; + + switch (i++) { + case 0: + x = v; + break; + case 1: + y = v; + break; + case 2: + max = v; + break; + } + } + + im = ImagingNew(mode, x, y); + if (!im) + return NULL; + + /* if (max != 255) ... FIXME: does anyone ever use this feature? */ + + if (strcmp(im->mode, "L") == 0) { + + /* PPM "L" */ + for (y = 0; y < im->ysize; y++) + if (fread(im->image[y], im->xsize, 1, fp) != 1) + goto error; + + } else { + + /* PPM "RGB" or PyPPM mode */ + for (y = 0; y < im->ysize; y++) + for (x = i = 0; x < im->xsize; x++, i += im->pixelsize) + if (fread(im->image[y]+i, im->bands, 1, fp) != 1) + goto error; + } + + fclose(fp); + + return im; + +error: + fclose(fp); + return ImagingError_IOError(); +} + + +int +ImagingSaveRaw(Imaging im, FILE* fp) +{ + int x, y, i; + + if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) { + + /* @PIL227: FIXME: for mode "1", map != 0 to 255 */ + + /* PGM "L" */ + for (y = 0; y < im->ysize; y++) + fwrite(im->image[y], 1, im->xsize, fp); + + } else { + + /* PPM "RGB" or other internal format */ + for (y = 0; y < im->ysize; y++) + for (x = i = 0; x < im->xsize; x++, i += im->pixelsize) + fwrite(im->image[y]+i, 1, im->bands, fp); + + } + + return 1; +} + + +int +ImagingSavePPM(Imaging im, const char* outfile) +{ + FILE* fp; + + if (!im) { + (void) ImagingError_ValueError(NULL); + return 0; + } + + fp = fopen(outfile, "wb"); + if (!fp) { + (void) ImagingError_IOError(); + return 0; + } + + if (strcmp(im->mode, "1") == 0 || strcmp(im->mode, "L") == 0) { + /* Write "PGM" */ + fprintf(fp, "P5\n%d %d\n255\n", im->xsize, im->ysize); + } else if (strcmp(im->mode, "RGB") == 0) { + /* Write "PPM" */ + fprintf(fp, "P6\n%d %d\n255\n", im->xsize, im->ysize); + } else { + (void) ImagingError_ModeError(); + return 0; + } + + ImagingSaveRaw(im, fp); + + fclose(fp); + + return 1; +} + diff --git a/libImaging/Fill.c b/libImaging/Fill.c new file mode 100644 index 000000000..ce00e6c15 --- /dev/null +++ b/libImaging/Fill.c @@ -0,0 +1,101 @@ +/* + * The Python Imaging Library + * $Id$ + * + * fill image with constant pixel value + * + * history: + * 95-11-26 fl moved from Imaging.c + * 96-05-17 fl added radial fill, renamed wedge to linear + * 98-06-23 fl changed ImageFill signature + * + * Copyright (c) Secret Labs AB 1997-98. All rights reserved. + * Copyright (c) Fredrik Lundh 1995-96. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include "math.h" + +Imaging +ImagingFill(Imaging im, const void* colour) +{ + int x, y; + + if (im->type == IMAGING_TYPE_SPECIAL) { + /* use generic API */ + ImagingAccess access = ImagingAccessNew(im); + if (access) { + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + access->put_pixel(im, x, y, colour); + ImagingAccessDelete(im, access); + } else { + /* wipe the image */ + for (y = 0; y < im->ysize; y++) + memset(im->image[y], 0, im->linesize); + } + } else { + INT32 c = 0L; + memcpy(&c, colour, im->pixelsize); + if (im->image32 && c != 0L) { + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + im->image32[y][x] = c; + } else { + unsigned char cc = (unsigned char) *(UINT8*) colour; + for (y = 0; y < im->ysize; y++) + memset(im->image[y], cc, im->linesize); + } + } + + return im; +} + +Imaging +ImagingFillLinearGradient(const char *mode) +{ + Imaging im; + int y; + + if (strlen(mode) != 1) + return (Imaging) ImagingError_ModeError(); + + im = ImagingNew(mode, 256, 256); + if (!im) + return NULL; + + for (y = 0; y < 256; y++) + memset(im->image8[y], (unsigned char) y, 256); + + return im; +} + +Imaging +ImagingFillRadialGradient(const char *mode) +{ + Imaging im; + int x, y; + int d; + + if (strlen(mode) != 1) + return (Imaging) ImagingError_ModeError(); + + im = ImagingNew(mode, 256, 256); + if (!im) + return NULL; + + for (y = 0; y < 256; y++) + for (x = 0; x < 256; x++) { + d = (int) sqrt((double) ((x-128)*(x-128) + (y-128)*(y-128)) * 2.0); + if (d >= 255) + im->image8[y][x] = 255; + else + im->image8[y][x] = d; + } + + return im; +} diff --git a/libImaging/Filter.c b/libImaging/Filter.c new file mode 100644 index 000000000..45c9bf060 --- /dev/null +++ b/libImaging/Filter.c @@ -0,0 +1,179 @@ +/* + * The Python Imaging Library + * $Id$ + * + * apply convolution kernel to image + * + * history: + * 1995-11-26 fl Created, supports 3x3 kernels + * 1995-11-27 fl Added 5x5 kernels, copy border + * 1999-07-26 fl Eliminated a few compiler warnings + * 2002-06-09 fl Moved kernel definitions to Python + * 2002-06-11 fl Support floating point kernels + * 2003-09-15 fl Added ImagingExpand helper + * + * Copyright (c) Secret Labs AB 1997-2002. All rights reserved. + * Copyright (c) Fredrik Lundh 1995. + * + * See the README file for information on usage and redistribution. + */ + +/* + * FIXME: Support RGB and RGBA/CMYK modes as well + * FIXME: Expand image border (current version leaves border as is) + * FIXME: Implement image processing gradient filters + */ + +#include "Imaging.h" + +Imaging +ImagingExpand(Imaging imIn, int xmargin, int ymargin, int mode) +{ + Imaging imOut; + int x, y; + + if (xmargin < 0 && ymargin < 0) + return (Imaging) ImagingError_ValueError("bad kernel size"); + + imOut = ImagingNew( + imIn->mode, imIn->xsize+2*xmargin, imIn->ysize+2*ymargin + ); + if (!imOut) + return NULL; + +#define EXPAND_LINE(type, image, yin, yout) {\ + for (x = 0; x < xmargin; x++)\ + imOut->image[yout][x] = imIn->image[yin][0];\ + for (x = 0; x < imIn->xsize; x++)\ + imOut->image[yout][x+xmargin] = imIn->image[yin][x];\ + for (x = 0; x < xmargin; x++)\ + imOut->image[yout][xmargin+imIn->xsize+x] =\ + imIn->image[yin][imIn->xsize-1];\ + } + +#define EXPAND(type, image) {\ + for (y = 0; y < ymargin; y++)\ + EXPAND_LINE(type, image, 0, y);\ + for (y = 0; y < imIn->ysize; y++)\ + EXPAND_LINE(type, image, y, y+ymargin);\ + for (y = 0; y < ymargin; y++)\ + EXPAND_LINE(type, image, imIn->ysize-1, ymargin+imIn->ysize+y);\ + } + + if (imIn->image8) { + EXPAND(UINT8, image8); + } else { + EXPAND(INT32, image32); + } + + ImagingCopyInfo(imOut, imIn); + + return imOut; +} + +Imaging +ImagingFilter(Imaging im, int xsize, int ysize, const FLOAT32* kernel, + FLOAT32 offset, FLOAT32 divisor) +{ + Imaging imOut; + int x, y; + FLOAT32 sum; + + if (!im || strcmp(im->mode, "L") != 0) + return (Imaging) ImagingError_ModeError(); + + if (im->xsize < xsize || im->ysize < ysize) + return ImagingCopy(im); + + if ((xsize != 3 && xsize != 5) || xsize != ysize) + return (Imaging) ImagingError_ValueError("bad kernel size"); + + imOut = ImagingNew(im->mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + /* brute force kernel implementations */ +#define KERNEL3x3(image, kernel, d) ( \ + (int) image[y+1][x-d] * kernel[0] + \ + (int) image[y+1][x] * kernel[1] + \ + (int) image[y+1][x+d] * kernel[2] + \ + (int) image[y][x-d] * kernel[3] + \ + (int) image[y][x] * kernel[4] + \ + (int) image[y][x+d] * kernel[5] + \ + (int) image[y-1][x-d] * kernel[6] + \ + (int) image[y-1][x] * kernel[7] + \ + (int) image[y-1][x+d] * kernel[8]) + +#define KERNEL5x5(image, kernel, d) ( \ + (int) image[y+2][x-d-d] * kernel[0] + \ + (int) image[y+2][x-d] * kernel[1] + \ + (int) image[y+2][x] * kernel[2] + \ + (int) image[y+2][x+d] * kernel[3] + \ + (int) image[y+2][x+d+d] * kernel[4] + \ + (int) image[y+1][x-d-d] * kernel[5] + \ + (int) image[y+1][x-d] * kernel[6] + \ + (int) image[y+1][x] * kernel[7] + \ + (int) image[y+1][x+d] * kernel[8] + \ + (int) image[y+1][x+d+d] * kernel[9] + \ + (int) image[y][x-d-d] * kernel[10] + \ + (int) image[y][x-d] * kernel[11] + \ + (int) image[y][x] * kernel[12] + \ + (int) image[y][x+d] * kernel[13] + \ + (int) image[y][x+d+d] * kernel[14] + \ + (int) image[y-1][x-d-d] * kernel[15] + \ + (int) image[y-1][x-d] * kernel[16] + \ + (int) image[y-1][x] * kernel[17] + \ + (int) image[y-1][x+d] * kernel[18] + \ + (int) image[y-1][x+d+d] * kernel[19] + \ + (int) image[y-2][x-d-d] * kernel[20] + \ + (int) image[y-2][x-d] * kernel[21] + \ + (int) image[y-2][x] * kernel[22] + \ + (int) image[y-2][x+d] * kernel[23] + \ + (int) image[y-2][x+d+d] * kernel[24]) + + if (xsize == 3) { + /* 3x3 kernel. */ + for (x = 0; x < im->xsize; x++) + imOut->image[0][x] = im->image8[0][x]; + for (y = 1; y < im->ysize-1; y++) { + imOut->image[y][0] = im->image8[y][0]; + for (x = 1; x < im->xsize-1; x++) { + sum = KERNEL3x3(im->image8, kernel, 1) / divisor + offset; + if (sum <= 0) + imOut->image8[y][x] = 0; + else if (sum >= 255) + imOut->image8[y][x] = 255; + else + imOut->image8[y][x] = (UINT8) sum; + } + imOut->image8[y][x] = im->image8[y][x]; + } + for (x = 0; x < im->xsize; x++) + imOut->image8[y][x] = im->image8[y][x]; + } else { + /* 5x5 kernel. */ + for (y = 0; y < 2; y++) + for (x = 0; x < im->xsize; x++) + imOut->image8[y][x] = im->image8[y][x]; + for (; y < im->ysize-2; y++) { + for (x = 0; x < 2; x++) + imOut->image8[y][x] = im->image8[y][x]; + for (; x < im->xsize-2; x++) { + sum = KERNEL5x5(im->image8, kernel, 1) / divisor + offset; + if (sum <= 0) + imOut->image8[y][x] = 0; + else if (sum >= 255) + imOut->image8[y][x] = 255; + else + imOut->image8[y][x] = (UINT8) sum; + } + for (; x < im->xsize; x++) + imOut->image8[y][x] = im->image8[y][x]; + } + for (; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + imOut->image8[y][x] = im->image8[y][x]; + } + return imOut; +} + diff --git a/libImaging/FliDecode.c b/libImaging/FliDecode.c new file mode 100644 index 000000000..75eebe86c --- /dev/null +++ b/libImaging/FliDecode.c @@ -0,0 +1,205 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for Autodesk Animator FLI/FLC animations + * + * history: + * 97-01-03 fl Created + * 97-01-17 fl Added SS2 support (FLC) + * + * Copyright (c) Fredrik Lundh 1997. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +#define I16(ptr)\ + ((ptr)[0] + ((ptr)[1] << 8)) + +#define I32(ptr)\ + ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24)) + + +int +ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* ptr; + int framesize; + int c, chunks; + int l, lines; + int i, j, x = 0, y, ymax; + + /* If not even the chunk size is present, we'd better leave */ + + if (bytes < 4) + return 0; + + /* We don't decode anything unless we have a full chunk in the + input buffer (on the other hand, the Python part of the driver + makes sure this is always the case) */ + + ptr = buf; + + framesize = I32(ptr); + if (framesize < I32(ptr)) + return 0; + + /* Make sure this is a frame chunk. The Python driver takes + case of other chunk types. */ + + if (I16(ptr+4) != 0xF1FA) { + state->errcode = IMAGING_CODEC_UNKNOWN; + return -1; + } + + chunks = I16(ptr+6); + ptr += 16; + + /* Process subchunks */ + for (c = 0; c < chunks; c++) { + UINT8 *data = ptr + 6; + switch (I16(ptr+4)) { + case 4: case 11: + /* FLI COLOR chunk */ + break; /* ignored; handled by Python code */ + case 7: + /* FLI SS2 chunk (word delta) */ + lines = I16(data); data += 2; + for (l = y = 0; l < lines && y < state->ysize; l++, y++) { + UINT8* buf = (UINT8*) im->image[y]; + int p, packets; + packets = I16(data); data += 2; + while (packets & 0x8000) { + /* flag word */ + if (packets & 0x4000) { + y += 65536 - packets; /* skip lines */ + if (y >= state->ysize) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + buf = (UINT8*) im->image[y]; + } else { + /* store last byte (used if line width is odd) */ + buf[state->xsize-1] = (UINT8) packets; + } + packets = I16(data); data += 2; + } + for (p = x = 0; p < packets; p++) { + x += data[0]; /* pixel skip */ + if (data[1] >= 128) { + i = 256-data[1]; /* run */ + if (x + i + i > state->xsize) + break; + for (j = 0; j < i; j++) { + buf[x++] = data[2]; + buf[x++] = data[3]; + } + data += 2 + 2; + } else { + i = 2 * (int) data[1]; /* chunk */ + if (x + i > state->xsize) + break; + memcpy(buf + x, data + 2, i); + data += 2 + i; + x += i; + } + } + if (p < packets) + break; /* didn't process all packets */ + } + if (l < lines) { + /* didn't process all lines */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + break; + case 12: + /* FLI LC chunk (byte delta) */ + y = I16(data); ymax = y + I16(data+2); data += 4; + for (; y < ymax && y < state->ysize; y++) { + UINT8* out = (UINT8*) im->image[y]; + int p, packets = *data++; + for (p = x = 0; p < packets; p++, x += i) { + x += data[0]; /* skip pixels */ + if (data[1] & 0x80) { + i = 256-data[1]; /* run */ + if (x + i > state->xsize) + break; + memset(out + x, data[2], i); + data += 3; + } else { + i = data[1]; /* chunk */ + if (x + i > state->xsize) + break; + memcpy(out + x, data + 2, i); + data += i + 2; + } + } + if (p < packets) + break; /* didn't process all packets */ + } + if (y < ymax) { + /* didn't process all lines */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + break; + case 13: + /* FLI BLACK chunk */ + for (y = 0; y < state->ysize; y++) + memset(im->image[y], 0, state->xsize); + break; + case 15: + /* FLI BRUN chunk */ + for (y = 0; y < state->ysize; y++) { + UINT8* out = (UINT8*) im->image[y]; + data += 1; /* ignore packetcount byte */ + for (x = 0; x < state->xsize; x += i) { + if (data[0] & 0x80) { + i = 256 - data[0]; + if (x + i > state->xsize) + break; /* safety first */ + memcpy(out + x, data + 1, i); + data += i + 1; + } else { + i = data[0]; + if (x + i > state->xsize) + break; /* safety first */ + memset(out + x, data[1], i); + data += 2; + } + } + if (x != state->xsize) { + /* didn't unpack whole line */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + } + break; + case 16: + /* COPY chunk */ + for (y = 0; y < state->ysize; y++) { + UINT8* buf = (UINT8*) im->image[y]; + memcpy(buf+x, data, state->xsize); + data += state->xsize; + } + break; + case 18: + /* PSTAMP chunk */ + break; /* ignored */ + default: + /* unknown chunk */ + /* printf("unknown FLI/FLC chunk: %d\n", I16(ptr+4)); */ + state->errcode = IMAGING_CODEC_UNKNOWN; + return -1; + } + ptr += I32(ptr); + } + + return -1; /* end of frame */ +} diff --git a/libImaging/Geometry.c b/libImaging/Geometry.c new file mode 100644 index 000000000..41f535b56 --- /dev/null +++ b/libImaging/Geometry.c @@ -0,0 +1,959 @@ +/* + * The Python Imaging Library + * $Id$ + * + * the imaging geometry methods + * + * history: + * 1995-06-15 fl Created + * 1996-04-15 fl Changed origin + * 1996-05-18 fl Fixed rotate90/270 for rectangular images + * 1996-05-27 fl Added general purpose transform + * 1996-11-22 fl Don't crash when resizing from outside source image + * 1997-08-09 fl Fixed rounding error in resize + * 1998-09-21 fl Incorporated transformation patches (from Zircon #2) + * 1998-09-22 fl Added bounding box to transform engines + * 1999-02-03 fl Fixed bicubic filtering for RGB images + * 1999-02-16 fl Added fixed-point version of affine transform + * 2001-03-28 fl Fixed transform(EXTENT) for xoffset < 0 + * 2003-03-10 fl Compiler tweaks + * 2004-09-19 fl Fixed bilinear/bicubic filtering of LA images + * + * Copyright (c) 1997-2003 by Secret Labs AB + * Copyright (c) 1995-1997 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +/* Undef if you don't need resampling filters */ +#define WITH_FILTERS + +#define COORD(v) ((v) < 0.0 ? -1 : ((int)(v))) +#define FLOOR(v) ((v) < 0.0 ? ((int)floor(v)) : ((int)(v))) + +/* -------------------------------------------------------------------- */ +/* Transpose operations */ + +Imaging +ImagingFlipLeftRight(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xr; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + +#define FLIP_HORIZ(image)\ + for (y = 0; y < imIn->ysize; y++) {\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[y][xr];\ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + FLIP_HORIZ(image8) + else + FLIP_HORIZ(image32) + + ImagingSectionLeave(&cookie); + + return imOut; +} + + +Imaging +ImagingFlipTopBottom(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int y, yr; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + + yr = imIn->ysize-1; + for (y = 0; y < imIn->ysize; y++, yr--) + memcpy(imOut->image[yr], imIn->image[y], imIn->linesize); + + ImagingSectionLeave(&cookie); + + return imOut; +} + + +Imaging +ImagingRotate90(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xr; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + +#define ROTATE_90(image)\ + for (y = 0; y < imIn->ysize; y++) {\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[xr][y] = imIn->image[y][x];\ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + ROTATE_90(image8) + else + ROTATE_90(image32) + + ImagingSectionLeave(&cookie); + + return imOut; +} + + +Imaging +ImagingRotate180(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, xr, yr; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->xsize || imIn->ysize != imOut->ysize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + + yr = imIn->ysize-1; + +#define ROTATE_180(image)\ + for (y = 0; y < imIn->ysize; y++, yr--) {\ + xr = imIn->xsize-1;\ + for (x = 0; x < imIn->xsize; x++, xr--)\ + imOut->image[y][x] = imIn->image[yr][xr];\ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + ROTATE_180(image8) + else + ROTATE_180(image32) + + ImagingSectionLeave(&cookie); + + return imOut; +} + + +Imaging +ImagingRotate270(Imaging imOut, Imaging imIn) +{ + ImagingSectionCookie cookie; + int x, y, yr; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + if (imIn->xsize != imOut->ysize || imIn->ysize != imOut->xsize) + return (Imaging) ImagingError_Mismatch(); + + ImagingCopyInfo(imOut, imIn); + + yr = imIn->ysize - 1; + +#define ROTATE_270(image)\ + for (y = 0; y < imIn->ysize; y++, yr--)\ + for (x = 0; x < imIn->xsize; x++)\ + imOut->image[x][y] = imIn->image[yr][x]; + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + ROTATE_270(image8) + else + ROTATE_270(image32) + + ImagingSectionLeave(&cookie); + + return imOut; +} + + +/* -------------------------------------------------------------------- */ +/* Transforms */ + +/* transform primitives (ImagingTransformMap) */ + +static int +affine_transform(double* xin, double* yin, int x, int y, void* data) +{ + /* full moon tonight. your compiler will generate bogus code + for simple expressions, unless you reorganize the code, or + install Service Pack 3 */ + + double* a = (double*) data; + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; + double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; + + xin[0] = a0 + a1*x + a2*y; + yin[0] = a3 + a4*x + a5*y; + + return 1; +} + +static int +perspective_transform(double* xin, double* yin, int x, int y, void* data) +{ + double* a = (double*) data; + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; + double a3 = a[3]; double a4 = a[4]; double a5 = a[5]; + double a6 = a[6]; double a7 = a[7]; + + xin[0] = (a0 + a1*x + a2*y) / (a6*x + a7*y + 1); + yin[0] = (a3 + a4*x + a5*y) / (a6*x + a7*y + 1); + + return 1; +} + +#if 0 +static int +quadratic_transform(double* xin, double* yin, int x, int y, void* data) +{ + double* a = (double*) data; + + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; + double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7]; + double a8 = a[8]; double a9 = a[9]; double a10 = a[10]; double a11 = a[11]; + + xin[0] = a0 + a1*x + a2*y + a3*x*x + a4*x*y + a5*y*y; + yin[0] = a6 + a7*x + a8*y + a9*x*x + a10*x*y + a11*y*y; + + return 1; +} +#endif + +static int +quad_transform(double* xin, double* yin, int x, int y, void* data) +{ + /* quad warp: map quadrilateral to rectangle */ + + double* a = (double*) data; + double a0 = a[0]; double a1 = a[1]; double a2 = a[2]; double a3 = a[3]; + double a4 = a[4]; double a5 = a[5]; double a6 = a[6]; double a7 = a[7]; + + xin[0] = a0 + a1*x + a2*y + a3*x*y; + yin[0] = a4 + a5*x + a6*y + a7*x*y; + + return 1; +} + +/* transform filters (ImagingTransformFilter) */ + +#ifdef WITH_FILTERS + +static int +nearest_filter8(void* out, Imaging im, double xin, double yin, void* data) +{ + int x = COORD(xin); + int y = COORD(yin); + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + return 0; + ((UINT8*)out)[0] = im->image8[y][x]; + return 1; +} + +static int +nearest_filter16(void* out, Imaging im, double xin, double yin, void* data) +{ + int x = COORD(xin); + int y = COORD(yin); + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + return 0; + ((INT16*)out)[0] = ((INT16*)(im->image8[y]))[x]; + return 1; +} + +static int +nearest_filter32(void* out, Imaging im, double xin, double yin, void* data) +{ + int x = COORD(xin); + int y = COORD(yin); + if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) + return 0; + ((INT32*)out)[0] = im->image32[y][x]; + return 1; +} + +#define XCLIP(im, x) ( ((x) < 0) ? 0 : ((x) < im->xsize) ? (x) : im->xsize-1 ) +#define YCLIP(im, y) ( ((y) < 0) ? 0 : ((y) < im->ysize) ? (y) : im->ysize-1 ) + +#define BILINEAR(v, a, b, d)\ + (v = (a) + ( (b) - (a) ) * (d)) + +#define BILINEAR_HEAD(type)\ + int x, y;\ + int x0, x1;\ + double v1, v2;\ + double dx, dy;\ + type* in;\ + if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\ + return 0;\ + xin -= 0.5;\ + yin -= 0.5;\ + x = FLOOR(xin);\ + y = FLOOR(yin);\ + dx = xin - x;\ + dy = yin - y; + +#define BILINEAR_BODY(type, image, step, offset) {\ + in = (type*) ((image)[YCLIP(im, y)] + offset);\ + x0 = XCLIP(im, x+0)*step;\ + x1 = XCLIP(im, x+1)*step;\ + BILINEAR(v1, in[x0], in[x1], dx);\ + if (y+1 >= 0 && y+1 < im->ysize) {\ + in = (type*) ((image)[y+1] + offset);\ + BILINEAR(v2, in[x0], in[x1], dx);\ + } else\ + v2 = v1;\ + BILINEAR(v1, v1, v2, dy);\ +} + +static int +bilinear_filter8(void* out, Imaging im, double xin, double yin, void* data) +{ + BILINEAR_HEAD(UINT8); + BILINEAR_BODY(UINT8, im->image8, 1, 0); + ((UINT8*)out)[0] = (UINT8) v1; + return 1; +} + +static int +bilinear_filter32I(void* out, Imaging im, double xin, double yin, void* data) +{ + BILINEAR_HEAD(INT32); + BILINEAR_BODY(INT32, im->image32, 1, 0); + ((INT32*)out)[0] = (INT32) v1; + return 1; +} + +static int +bilinear_filter32F(void* out, Imaging im, double xin, double yin, void* data) +{ + BILINEAR_HEAD(FLOAT32); + BILINEAR_BODY(FLOAT32, im->image32, 1, 0); + ((FLOAT32*)out)[0] = (FLOAT32) v1; + return 1; +} + +static int +bilinear_filter32LA(void* out, Imaging im, double xin, double yin, void* data) +{ + BILINEAR_HEAD(UINT8); + BILINEAR_BODY(UINT8, im->image, 4, 0); + ((UINT8*)out)[0] = (UINT8) v1; + ((UINT8*)out)[1] = (UINT8) v1; + ((UINT8*)out)[2] = (UINT8) v1; + BILINEAR_BODY(UINT8, im->image, 4, 3); + ((UINT8*)out)[3] = (UINT8) v1; + return 1; +} + +static int +bilinear_filter32RGB(void* out, Imaging im, double xin, double yin, void* data) +{ + int b; + BILINEAR_HEAD(UINT8); + for (b = 0; b < im->bands; b++) { + BILINEAR_BODY(UINT8, im->image, 4, b); + ((UINT8*)out)[b] = (UINT8) v1; + } + return 1; +} + +#define BICUBIC(v, v1, v2, v3, v4, d) {\ + double p1 = v2;\ + double p2 = -v1 + v3;\ + double p3 = 2*(v1 - v2) + v3 - v4;\ + double p4 = -v1 + v2 - v3 + v4;\ + v = p1 + (d)*(p2 + (d)*(p3 + (d)*p4));\ +} + +#define BICUBIC_HEAD(type)\ + int x = FLOOR(xin);\ + int y = FLOOR(yin);\ + int x0, x1, x2, x3;\ + double v1, v2, v3, v4;\ + double dx, dy;\ + type* in;\ + if (xin < 0.0 || xin >= im->xsize || yin < 0.0 || yin >= im->ysize)\ + return 0;\ + xin -= 0.5;\ + yin -= 0.5;\ + x = FLOOR(xin);\ + y = FLOOR(yin);\ + dx = xin - x;\ + dy = yin - y;\ + x--; y--; + +#define BICUBIC_BODY(type, image, step, offset) {\ + in = (type*) ((image)[YCLIP(im, y)] + offset);\ + x0 = XCLIP(im, x+0)*step;\ + x1 = XCLIP(im, x+1)*step;\ + x2 = XCLIP(im, x+2)*step;\ + x3 = XCLIP(im, x+3)*step;\ + BICUBIC(v1, in[x0], in[x1], in[x2], in[x3], dx);\ + if (y+1 >= 0 && y+1 < im->ysize) {\ + in = (type*) ((image)[y+1] + offset);\ + BICUBIC(v2, in[x0], in[x1], in[x2], in[x3], dx);\ + } else\ + v2 = v1;\ + if (y+2 >= 0 && y+2 < im->ysize) {\ + in = (type*) ((image)[y+2] + offset);\ + BICUBIC(v3, in[x0], in[x1], in[x2], in[x3], dx);\ + } else\ + v3 = v2;\ + if (y+3 >= 0 && y+3 < im->ysize) {\ + in = (type*) ((image)[y+3] + offset);\ + BICUBIC(v4, in[x0], in[x1], in[x2], in[x3], dx);\ + } else\ + v4 = v3;\ + BICUBIC(v1, v1, v2, v3, v4, dy);\ +} + + +static int +bicubic_filter8(void* out, Imaging im, double xin, double yin, void* data) +{ + BICUBIC_HEAD(UINT8); + BICUBIC_BODY(UINT8, im->image8, 1, 0); + if (v1 <= 0.0) + ((UINT8*)out)[0] = 0; + else if (v1 >= 255.0) + ((UINT8*)out)[0] = 255; + else + ((UINT8*)out)[0] = (UINT8) v1; + return 1; +} + +static int +bicubic_filter32I(void* out, Imaging im, double xin, double yin, void* data) +{ + BICUBIC_HEAD(INT32); + BICUBIC_BODY(INT32, im->image32, 1, 0); + ((INT32*)out)[0] = (INT32) v1; + return 1; +} + +static int +bicubic_filter32F(void* out, Imaging im, double xin, double yin, void* data) +{ + BICUBIC_HEAD(FLOAT32); + BICUBIC_BODY(FLOAT32, im->image32, 1, 0); + ((FLOAT32*)out)[0] = (FLOAT32) v1; + return 1; +} + +static int +bicubic_filter32LA(void* out, Imaging im, double xin, double yin, void* data) +{ + BICUBIC_HEAD(UINT8); + BICUBIC_BODY(UINT8, im->image, 4, 0); + if (v1 <= 0.0) { + ((UINT8*)out)[0] = 0; + ((UINT8*)out)[1] = 0; + ((UINT8*)out)[2] = 0; + } else if (v1 >= 255.0) { + ((UINT8*)out)[0] = 255; + ((UINT8*)out)[1] = 255; + ((UINT8*)out)[2] = 255; + } else { + ((UINT8*)out)[0] = (UINT8) v1; + ((UINT8*)out)[1] = (UINT8) v1; + ((UINT8*)out)[2] = (UINT8) v1; + } + BICUBIC_BODY(UINT8, im->image, 4, 3); + if (v1 <= 0.0) + ((UINT8*)out)[3] = 0; + else if (v1 >= 255.0) + ((UINT8*)out)[3] = 255; + else + ((UINT8*)out)[3] = (UINT8) v1; + return 1; +} + +static int +bicubic_filter32RGB(void* out, Imaging im, double xin, double yin, void* data) +{ + int b; + BICUBIC_HEAD(UINT8); + for (b = 0; b < im->bands; b++) { + BICUBIC_BODY(UINT8, im->image, 4, b); + if (v1 <= 0.0) + ((UINT8*)out)[b] = 0; + else if (v1 >= 255.0) + ((UINT8*)out)[b] = 255; + else + ((UINT8*)out)[b] = (UINT8) v1; + } + return 1; +} + +static ImagingTransformFilter +getfilter(Imaging im, int filterid) +{ + switch (filterid) { + case IMAGING_TRANSFORM_NEAREST: + if (im->image8) + switch (im->type) { + case IMAGING_TYPE_UINT8: + return (ImagingTransformFilter) nearest_filter8; + case IMAGING_TYPE_SPECIAL: + switch (im->pixelsize) { + case 1: + return (ImagingTransformFilter) nearest_filter8; + case 2: + return (ImagingTransformFilter) nearest_filter16; + case 4: + return (ImagingTransformFilter) nearest_filter32; + } + } + else + return (ImagingTransformFilter) nearest_filter32; + break; + case IMAGING_TRANSFORM_BILINEAR: + if (im->image8) + return (ImagingTransformFilter) bilinear_filter8; + else if (im->image32) { + switch (im->type) { + case IMAGING_TYPE_UINT8: + if (im->bands == 2) + return (ImagingTransformFilter) bilinear_filter32LA; + else + return (ImagingTransformFilter) bilinear_filter32RGB; + case IMAGING_TYPE_INT32: + return (ImagingTransformFilter) bilinear_filter32I; + case IMAGING_TYPE_FLOAT32: + return (ImagingTransformFilter) bilinear_filter32F; + } + } + break; + case IMAGING_TRANSFORM_BICUBIC: + if (im->image8) + return (ImagingTransformFilter) bicubic_filter8; + else if (im->image32) { + switch (im->type) { + case IMAGING_TYPE_UINT8: + if (im->bands == 2) + return (ImagingTransformFilter) bicubic_filter32LA; + else + return (ImagingTransformFilter) bicubic_filter32RGB; + case IMAGING_TYPE_INT32: + return (ImagingTransformFilter) bicubic_filter32I; + case IMAGING_TYPE_FLOAT32: + return (ImagingTransformFilter) bicubic_filter32F; + } + } + break; + } + /* no such filter */ + return NULL; +} + +#else +#define getfilter(im, id) NULL +#endif + +/* transformation engines */ + +Imaging +ImagingTransform( + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + ImagingTransformMap transform, void* transform_data, + ImagingTransformFilter filter, void* filter_data, + int fill) +{ + /* slow generic transformation. use ImagingTransformAffine or + ImagingScaleAffine where possible. */ + + ImagingSectionCookie cookie; + int x, y; + char *out; + double xx, yy; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + + if (x0 < 0) + x0 = 0; + if (y0 < 0) + y0 = 0; + if (x1 > imOut->xsize) + x1 = imOut->xsize; + if (y1 > imOut->ysize) + y1 = imOut->ysize; + + for (y = y0; y < y1; y++) { + out = imOut->image[y] + x0*imOut->pixelsize; + for (x = x0; x < x1; x++) { + if (!transform(&xx, &yy, x-x0, y-y0, transform_data) || + !filter(out, imIn, xx, yy, filter_data)) { + if (fill) + memset(out, 0, imOut->pixelsize); + } + out += imOut->pixelsize; + } + } + + ImagingSectionLeave(&cookie); + + return imOut; +} + +static Imaging +ImagingScaleAffine(Imaging imOut, Imaging imIn, + int x0, int y0, int x1, int y1, + double a[6], int fill) +{ + /* scale, nearest neighbour resampling */ + + ImagingSectionCookie cookie; + int x, y; + int xin; + double xo, yo; + int xmin, xmax; + int *xintab; + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + + ImagingCopyInfo(imOut, imIn); + + if (x0 < 0) + x0 = 0; + if (y0 < 0) + y0 = 0; + if (x1 > imOut->xsize) + x1 = imOut->xsize; + if (y1 > imOut->ysize) + y1 = imOut->ysize; + + xintab = (int*) malloc(imOut->xsize * sizeof(int)); + if (!xintab) { + ImagingDelete(imOut); + return (Imaging) ImagingError_MemoryError(); + } + + xo = a[0]; + yo = a[3]; + + xmin = x1; + xmax = x0; + + /* Pretabulate horizontal pixel positions */ + for (x = x0; x < x1; x++) { + xin = COORD(xo); + if (xin >= 0 && xin < (int) imIn->xsize) { + xmax = x+1; + if (x < xmin) + xmin = x; + xintab[x] = xin; + } + xo += a[1]; + } + +#define AFFINE_SCALE(pixel, image)\ + for (y = y0; y < y1; y++) {\ + int yi = COORD(yo);\ + pixel *in, *out;\ + out = imOut->image[y];\ + if (fill && x1 > x0)\ + memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + if (yi >= 0 && yi < imIn->ysize) {\ + in = imIn->image[yi];\ + for (x = xmin; x < xmax; x++)\ + out[x] = in[xintab[x]];\ + }\ + yo += a[5];\ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) { + AFFINE_SCALE(UINT8, image8); + } else { + AFFINE_SCALE(INT32, image32); + } + + ImagingSectionLeave(&cookie); + + free(xintab); + + return imOut; +} + +static inline int +check_fixed(double a[6], int x, int y) +{ + return (fabs(a[0] + x*a[1] + y*a[2]) < 32768.0 && + fabs(a[3] + x*a[4] + y*a[5]) < 32768.0); +} + +static inline Imaging +affine_fixed(Imaging imOut, Imaging imIn, + int x0, int y0, int x1, int y1, + double a[6], int filterid, int fill) +{ + /* affine transform, nearest neighbour resampling, fixed point + arithmetics */ + + int x, y; + int xin, yin; + int xsize, ysize; + int xx, yy; + int a0, a1, a2, a3, a4, a5; + + ImagingCopyInfo(imOut, imIn); + + xsize = (int) imIn->xsize; + ysize = (int) imIn->ysize; + +/* use 16.16 fixed point arithmetics */ +#define FIX(v) FLOOR((v)*65536.0 + 0.5) + + a0 = FIX(a[0]); a1 = FIX(a[1]); a2 = FIX(a[2]); + a3 = FIX(a[3]); a4 = FIX(a[4]); a5 = FIX(a[5]); + +#define AFFINE_TRANSFORM_FIXED(pixel, image)\ + for (y = y0; y < y1; y++) {\ + pixel *out;\ + xx = a0;\ + yy = a3;\ + out = imOut->image[y];\ + if (fill && x1 > x0)\ + memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + for (x = x0; x < x1; x++, out++) {\ + xin = xx >> 16;\ + if (xin >= 0 && xin < xsize) {\ + yin = yy >> 16;\ + if (yin >= 0 && yin < ysize)\ + *out = imIn->image[yin][xin];\ + }\ + xx += a1;\ + yy += a4;\ + }\ + a0 += a2;\ + a3 += a5;\ + } + + if (imIn->image8) + AFFINE_TRANSFORM_FIXED(UINT8, image8) + else + AFFINE_TRANSFORM_FIXED(INT32, image32) + + return imOut; +} + +Imaging +ImagingTransformAffine(Imaging imOut, Imaging imIn, + int x0, int y0, int x1, int y1, + double a[6], int filterid, int fill) +{ + /* affine transform, nearest neighbour resampling, floating point + arithmetics*/ + + ImagingSectionCookie cookie; + int x, y; + int xin, yin; + int xsize, ysize; + double xx, yy; + double xo, yo; + + if (filterid || imIn->type == IMAGING_TYPE_SPECIAL) { + /* Filtered transform */ + ImagingTransformFilter filter = getfilter(imIn, filterid); + if (!filter) + return (Imaging) ImagingError_ValueError("unknown filter"); + return ImagingTransform( + imOut, imIn, + x0, y0, x1, y1, + affine_transform, a, + filter, NULL, fill); + } + + if (a[2] == 0 && a[4] == 0) + /* Scaling */ + return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill); + + if (!imOut || !imIn || strcmp(imIn->mode, imOut->mode) != 0) + return (Imaging) ImagingError_ModeError(); + + if (x0 < 0) + x0 = 0; + if (y0 < 0) + y0 = 0; + if (x1 > imOut->xsize) + x1 = imOut->xsize; + if (y1 > imOut->ysize) + y1 = imOut->ysize; + + ImagingCopyInfo(imOut, imIn); + + /* translate all four corners to check if they are within the + range that can be represented by the fixed point arithmetics */ + + if (check_fixed(a, 0, 0) && check_fixed(a, x1-x0, y1-y0) && + check_fixed(a, 0, y1-y0) && check_fixed(a, x1-x0, 0)) + return affine_fixed(imOut, imIn, x0, y0, x1, y1, a, filterid, fill); + + /* FIXME: cannot really think of any reasonable case when the + following code is used. maybe we should fall back on the slow + generic transform engine in this case? */ + + xsize = (int) imIn->xsize; + ysize = (int) imIn->ysize; + + xo = a[0]; + yo = a[3]; + +#define AFFINE_TRANSFORM(pixel, image)\ + for (y = y0; y < y1; y++) {\ + pixel *out;\ + xx = xo;\ + yy = yo;\ + out = imOut->image[y];\ + if (fill && x1 > x0)\ + memset(out+x0, 0, (x1-x0)*sizeof(pixel));\ + for (x = x0; x < x1; x++, out++) {\ + xin = COORD(xx);\ + if (xin >= 0 && xin < xsize) {\ + yin = COORD(yy);\ + if (yin >= 0 && yin < ysize)\ + *out = imIn->image[yin][xin];\ + }\ + xx += a[1];\ + yy += a[4];\ + }\ + xo += a[2];\ + yo += a[5];\ + } + + ImagingSectionEnter(&cookie); + + if (imIn->image8) + AFFINE_TRANSFORM(UINT8, image8) + else + AFFINE_TRANSFORM(INT32, image32) + + ImagingSectionLeave(&cookie); + + return imOut; +} + +Imaging +ImagingTransformPerspective(Imaging imOut, Imaging imIn, + int x0, int y0, int x1, int y1, + double a[8], int filterid, int fill) +{ + ImagingTransformFilter filter = getfilter(imIn, filterid); + if (!filter) + return (Imaging) ImagingError_ValueError("bad filter number"); + + return ImagingTransform( + imOut, imIn, + x0, y0, x1, y1, + perspective_transform, a, + filter, NULL, + fill); +} + +Imaging +ImagingTransformQuad(Imaging imOut, Imaging imIn, + int x0, int y0, int x1, int y1, + double a[8], int filterid, int fill) +{ + ImagingTransformFilter filter = getfilter(imIn, filterid); + if (!filter) + return (Imaging) ImagingError_ValueError("bad filter number"); + + return ImagingTransform( + imOut, imIn, + x0, y0, x1, y1, + quad_transform, a, + filter, NULL, + fill); +} + +/* -------------------------------------------------------------------- */ +/* Convenience functions */ + +Imaging +ImagingResize(Imaging imOut, Imaging imIn, int filterid) +{ + double a[6]; + + if (imOut->xsize == imIn->xsize && imOut->ysize == imIn->ysize) + return ImagingCopy2(imOut, imIn); + + memset(a, 0, sizeof a); + a[1] = (double) imIn->xsize / imOut->xsize; + a[5] = (double) imIn->ysize / imOut->ysize; + + if (!filterid && imIn->type != IMAGING_TYPE_SPECIAL) + return ImagingScaleAffine( + imOut, imIn, + 0, 0, imOut->xsize, imOut->ysize, + a, 1); + + return ImagingTransformAffine( + imOut, imIn, + 0, 0, imOut->xsize, imOut->ysize, + a, filterid, 1); +} + +Imaging +ImagingRotate(Imaging imOut, Imaging imIn, double theta, int filterid) +{ + int xsize, ysize; + double sintheta, costheta; + double a[6]; + + /* Setup an affine transform to rotate around the image center */ + theta = -theta * M_PI / 180.0; + sintheta = sin(theta); + costheta = cos(theta); + + xsize = imOut->xsize; + ysize = imOut->ysize; + + a[0] = -costheta * xsize/2 - sintheta * ysize/2 + xsize/2; + a[1] = costheta; + a[2] = sintheta; + a[3] = sintheta * xsize/2 - costheta * ysize/2 + ysize/2; + a[4] = -sintheta; + a[5] = costheta; + + return ImagingTransformAffine( + imOut, imIn, + 0, 0, imOut->xsize, imOut->ysize, + a, filterid, 1); +} diff --git a/libImaging/GetBBox.c b/libImaging/GetBBox.c new file mode 100644 index 000000000..d1722fe14 --- /dev/null +++ b/libImaging/GetBBox.c @@ -0,0 +1,316 @@ +/* + * The Python Imaging Library + * $Id$ + * + * helpers to bounding boxes, min/max values, number of colors, etc. + * + * history: + * 1996-07-22 fl Created + * 1996-12-30 fl Added projection stuff + * 1998-07-12 fl Added extrema stuff + * 2004-09-17 fl Added colors stuff + * + * Copyright (c) 1997-2004 by Secret Labs AB. + * Copyright (c) 1996-2004 by Fredrik Lundh. + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingGetBBox(Imaging im, int bbox[4]) +{ + /* Get the bounding box for any non-zero data in the image.*/ + + int x, y; + int has_data; + + /* Initialize bounding box to max values */ + bbox[0] = im->xsize; + bbox[1] = -1; + bbox[2] = bbox[3] = 0; + +#define GETBBOX(image, mask)\ + for (y = 0; y < im->ysize; y++) {\ + has_data = 0;\ + for (x = 0; x < im->xsize; x++)\ + if (im->image[y][x] & mask) {\ + has_data = 1;\ + if (x < bbox[0])\ + bbox[0] = x;\ + if (x >= bbox[2])\ + bbox[2] = x+1;\ + }\ + if (has_data) {\ + if (bbox[1] < 0)\ + bbox[1] = y;\ + bbox[3] = y+1;\ + }\ + } + + if (im->image8) { + GETBBOX(image8, 0xff); + } else { + INT32 mask = 0xffffffff; + if (im->bands == 3) + ((UINT8*) &mask)[3] = 0; + GETBBOX(image32, mask); + } + + /* Check that we got a box */ + if (bbox[1] < 0) + return 0; /* no data */ + + return 1; /* ok */ +} + + +int +ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj) +{ + /* Get projection arrays for non-zero data in the image.*/ + + int x, y; + int has_data; + + /* Initialize projection arrays */ + memset(xproj, 0, im->xsize); + memset(yproj, 0, im->ysize); + +#define GETPROJ(image, mask)\ + for (y = 0; y < im->ysize; y++) {\ + has_data = 0;\ + for (x = 0; x < im->xsize; x++)\ + if (im->image[y][x] & mask) {\ + has_data = 1;\ + xproj[x] = 1;\ + }\ + if (has_data)\ + yproj[y] = 1;\ + } + + if (im->image8) { + GETPROJ(image8, 0xff); + } else { + INT32 mask = 0xffffffff; + if (im->bands == 3) + ((UINT8*) &mask)[3] = 0; + GETPROJ(image32, mask); + } + + return 1; /* ok */ +} + + +int +ImagingGetExtrema(Imaging im, void *extrema) +{ + int x, y; + INT32 imin, imax; + FLOAT32 fmin, fmax; + + if (im->bands != 1) { + (void) ImagingError_ModeError(); + return -1; /* mismatch */ + } + + if (!im->xsize || !im->ysize) + return 0; /* zero size */ + + switch (im->type) { + case IMAGING_TYPE_UINT8: + imin = imax = im->image8[0][0]; + for (y = 0; y < im->ysize; y++) { + UINT8* in = im->image8[y]; + for (x = 0; x < im->xsize; x++) { + if (imin > in[x]) + imin = in[x]; + else if (imax < in[x]) + imax = in[x]; + } + } + ((UINT8*) extrema)[0] = (UINT8) imin; + ((UINT8*) extrema)[1] = (UINT8) imax; + break; + case IMAGING_TYPE_INT32: + imin = imax = im->image32[0][0]; + for (y = 0; y < im->ysize; y++) { + INT32* in = im->image32[y]; + for (x = 0; x < im->xsize; x++) { + if (imin > in[x]) + imin = in[x]; + else if (imax < in[x]) + imax = in[x]; + } + } + ((INT32*) extrema)[0] = imin; + ((INT32*) extrema)[1] = imax; + break; + case IMAGING_TYPE_FLOAT32: + fmin = fmax = ((FLOAT32*) im->image32[0])[0]; + for (y = 0; y < im->ysize; y++) { + FLOAT32* in = (FLOAT32*) im->image32[y]; + for (x = 0; x < im->xsize; x++) { + if (fmin > in[x]) + fmin = in[x]; + else if (fmax < in[x]) + fmax = in[x]; + } + } + ((FLOAT32*) extrema)[0] = fmin; + ((FLOAT32*) extrema)[1] = fmax; + break; + case IMAGING_TYPE_SPECIAL: + if (strcmp(im->mode, "I;16") == 0) { + imin = imax = ((UINT16*) im->image8[0])[0]; + for (y = 0; y < im->ysize; y++) { + UINT16* in = (UINT16 *) im->image[y]; + for (x = 0; x < im->xsize; x++) { + if (imin > in[x]) + imin = in[x]; + else if (imax < in[x]) + imax = in[x]; + } + } + ((UINT16*) extrema)[0] = (UINT16) imin; + ((UINT16*) extrema)[1] = (UINT16) imax; + break; + } + /* FALL THROUGH */ + default: + (void) ImagingError_ModeError(); + return -1; + } + return 1; /* ok */ +} + + +/* static ImagingColorItem* getcolors8(Imaging im, int maxcolors, int* size);*/ +static ImagingColorItem* getcolors32(Imaging im, int maxcolors, int* size); + +ImagingColorItem* +ImagingGetColors(Imaging im, int maxcolors, int* size) +{ + /* FIXME: add support for 8-bit images */ + return getcolors32(im, maxcolors, size); +} + +static ImagingColorItem* +getcolors32(Imaging im, int maxcolors, int* size) +{ + unsigned int h; + unsigned int i, incr; + int colors; + INT32 pixel_mask; + int x, y; + ImagingColorItem* table; + ImagingColorItem* v; + + unsigned int code_size; + unsigned int code_poly; + unsigned int code_mask; + + /* note: the hash algorithm used here is based on the dictionary + code in Python 2.1.3; the exact implementation is borrowed from + Python's Unicode property database (written by yours truly) /F */ + + static int SIZES[] = { + 4,3, 8,3, 16,3, 32,5, 64,3, 128,3, 256,29, 512,17, 1024,9, 2048,5, + 4096,83, 8192,27, 16384,43, 32768,3, 65536,45, 131072,9, 262144,39, + 524288,39, 1048576,9, 2097152,5, 4194304,3, 8388608,33, 16777216,27, + 33554432,9, 67108864,71, 134217728,39, 268435456,9, 536870912,5, + 1073741824,83, 0 + }; + + code_size = code_poly = code_mask = 0; + + for (i = 0; SIZES[i]; i += 2) { + if (SIZES[i] > maxcolors) { + code_size = SIZES[i]; + code_poly = SIZES[i+1]; + code_mask = code_size - 1; + break; + } + } + + /* printf("code_size=%d\n", code_size); */ + /* printf("code_poly=%d\n", code_poly); */ + + if (!code_size) + return ImagingError_MemoryError(); /* just give up */ + + if (!im->image32) + return ImagingError_ModeError(); + + table = calloc(code_size + 1, sizeof(ImagingColorItem)); + if (!table) + return ImagingError_MemoryError(); + + pixel_mask = 0xffffffff; + if (im->bands == 3) + ((UINT8*) &pixel_mask)[3] = 0; + + colors = 0; + + for (y = 0; y < im->ysize; y++) { + INT32* p = im->image32[y]; + for (x = 0; x < im->xsize; x++) { + INT32 pixel = p[x] & pixel_mask; + h = (pixel); /* null hashing */ + i = (~h) & code_mask; + v = &table[i]; + if (!v->count) { + /* add to table */ + if (colors++ == maxcolors) + goto overflow; + v->x = x; v->y = y; + v->pixel = pixel; + v->count = 1; + continue; + } else if (v->pixel == pixel) { + v->count++; + continue; + } + incr = (h ^ (h >> 3)) & code_mask; + if (!incr) + incr = code_mask; + for (;;) { + i = (i + incr) & code_mask; + v = &table[i]; + if (!v->count) { + /* add to table */ + if (colors++ == maxcolors) + goto overflow; + v->x = x; v->y = y; + v->pixel = pixel; + v->count = 1; + break; + } else if (v->pixel == pixel) { + v->count++; + break; + } + incr = incr << 1; + if (incr > code_mask) + incr = incr ^ code_poly; + } + } + } + +overflow: + + /* pack the table */ + for (x = y = 0; x < (int) code_size; x++) + if (table[x].count) { + if (x != y) + table[y] = table[x]; + y++; + } + table[y].count = 0; /* mark end of table */ + + *size = colors; + + return table; +} diff --git a/libImaging/Gif.h b/libImaging/Gif.h new file mode 100644 index 000000000..85d428b0d --- /dev/null +++ b/libImaging/Gif.h @@ -0,0 +1,109 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * Declarations for a fast, suspendable GIF decoder. + * + * Copyright (c) Fredrik Lundh 1995-96. + */ + + +/* Max size for a LZW code word. */ + +#define GIFBITS 12 + +#define GIFTABLE (1< +#include /* memcpy() */ + +#include "Gif.h" + + +#define NEWLINE(state, context) {\ + state->x = 0;\ + state->y += context->step;\ + while (state->y >= state->ysize)\ + switch (context->interlace) {\ + case 1:\ + context->repeat = state->y = 4;\ + context->interlace = 2;\ + break;\ + case 2:\ + context->step = 4;\ + context->repeat = state->y = 2;\ + context->interlace = 3;\ + break;\ + case 3:\ + context->step = 2;\ + context->repeat = state->y = 1;\ + context->interlace = 0;\ + break;\ + default:\ + return -1;\ + }\ + if (state->y < state->ysize)\ + out = im->image8[state->y + state->yoff] + state->xoff;\ +} + + +int +ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) +{ + UINT8* p; + UINT8* out; + int c, i; + int thiscode; + GIFDECODERSTATE *context = (GIFDECODERSTATE*) state->context; + + UINT8 *ptr = buffer; + + if (!state->state) { + + /* Initialise state */ + if (context->bits < 0 || context->bits > 8) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + /* Clear code */ + context->clear = 1 << context->bits; + + /* End code */ + context->end = context->clear + 1; + + /* Interlace */ + if (context->interlace) { + context->interlace = 1; + context->step = context->repeat = 8; + } else + context->step = 1; + + state->state = 1; + } + + out = im->image8[state->y + state->yoff] + state->xoff + state->x; + + for (;;) { + + if (state->state == 1) { + + /* First free entry in table */ + context->next = context->clear + 2; + + /* Initial code size */ + context->codesize = context->bits + 1; + context->codemask = (1 << context->codesize) - 1; + + /* Buffer pointer. We fill the buffer from right, which + allows us to return all of it in one operation. */ + context->bufferindex = GIFBUFFER; + + state->state = 2; + } + + if (context->bufferindex < GIFBUFFER) { + + /* Return whole buffer in one chunk */ + i = GIFBUFFER - context->bufferindex; + p = &context->buffer[context->bufferindex]; + + context->bufferindex = GIFBUFFER; + + } else { + + /* Get current symbol */ + + while (context->bitcount < context->codesize) { + + if (context->blocksize > 0) { + + /* Read next byte */ + c = *ptr++; bytes--; + + context->blocksize--; + + /* New bits are shifted in from from the left. */ + context->bitbuffer |= (INT32) c << context->bitcount; + context->bitcount += 8; + + } else { + + /* New GIF block */ + + /* We don't start decoding unless we have a full block */ + if (bytes < 1) + return ptr - buffer; + c = *ptr; + if (bytes < c+1) + return ptr - buffer; + + context->blocksize = c; + + ptr++; bytes--; + + } + } + + /* Extract current symbol from bit buffer. */ + c = (int) context->bitbuffer & context->codemask; + + /* Adjust buffer */ + context->bitbuffer >>= context->codesize; + context->bitcount -= context->codesize; + + /* If c is less than "clear", it's a data byte. Otherwise, + it's either clear/end or a code symbol which should be + expanded. */ + + if (c == context->clear) { + if (state->state != 2) + state->state = 1; + continue; + } + + if (c == context->end) + break; + + i = 1; + p = &context->lastdata; + + if (state->state == 2) { + + /* First valid symbol after clear; use as is */ + if (c > context->clear) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->lastdata = context->lastcode = c; + state->state = 3; + + } else { + + thiscode = c; + + if (c > context->next) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + if (c == context->next) { + + /* c == next is allowed. not sure why. */ + + if (context->bufferindex <= 0) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->buffer[--context->bufferindex] = + context->lastdata; + + c = context->lastcode; + + } + + while (c >= context->clear) { + + /* Copy data string to buffer (beginning from right) */ + + if (context->bufferindex <= 0 || c >= GIFTABLE) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->buffer[--context->bufferindex] = + context->data[c]; + + c = context->link[c]; + } + + context->lastdata = c; + + if (context->next < GIFTABLE) { + + /* We'll only add this symbol if we have room + for it (take advise, Netscape!) */ + context->data[context->next] = c; + context->link[context->next] = context->lastcode; + + if (context->next == context->codemask && + context->codesize < GIFBITS) { + + /* Expand code size */ + context->codesize++; + context->codemask = (1 << context->codesize) - 1; + } + + context->next++; + + } + + context->lastcode = thiscode; + + } + } + + /* Copy the bytes into the image */ + if (state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + /* To squeeze some extra pixels out of this loop, we test for + some common cases and handle them separately. */ + + /* FIXME: should we handle the transparency index in here??? */ + + if (i == 1) { + if (state->x < state->xsize-1) { + /* Single pixel, not at the end of the line. */ + *out++ = p[0]; + state->x++; + continue; + } + } else if (state->x + i <= state->xsize) { + /* This string fits into current line. */ + memcpy(out, p, i); + out += i; + state->x += i; + if (state->x == state->xsize) { + NEWLINE(state, context); + } + continue; + } + + /* No shortcut, copy pixel by pixel */ + for (c = 0; c < i; c++) { + *out++ = p[c]; + if (++state->x >= state->xsize) { + NEWLINE(state, context); + } + } + } + + return ptr - buffer; +} diff --git a/libImaging/GifEncode.c b/libImaging/GifEncode.c new file mode 100644 index 000000000..f4d07598f --- /dev/null +++ b/libImaging/GifEncode.c @@ -0,0 +1,319 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * encoder for uncompressed GIF data + * + * history: + * 97-01-05 fl created (writes uncompressed data) + * 97-08-27 fl fixed off-by-one error in buffer size test + * 98-07-09 fl added interlace write support + * 99-02-07 fl rewritten, now uses a run-length encoding strategy + * 99-02-08 fl improved run-length encoding for long runs + * + * Copyright (c) Secret Labs AB 1997-99. + * Copyright (c) Fredrik Lundh 1997. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +#include "Gif.h" + +/* codes from 0 to 255 are literals */ +#define CLEAR_CODE 256 +#define EOF_CODE 257 +#define FIRST_CODE 258 +#define LAST_CODE 511 + +enum { INIT, ENCODE, ENCODE_EOF, FLUSH, EXIT }; + +/* to make things a little less complicated, we use a simple output + queue to hold completed blocks. the following inlined function + adds a byte to the current block. it allocates a new block if + necessary. */ + +static inline int +emit(GIFENCODERSTATE *context, int byte) +{ + /* write a byte to the output buffer */ + + if (!context->block || context->block->size == 255) { + GIFENCODERBLOCK* block; + + /* no room in the current block (or no current block); + allocate a new one */ + + /* add current block to end of flush queue */ + if (context->block) { + block = context->flush; + while (block && block->next) + block = block->next; + if (block) + block->next = context->block; + else + context->flush = context->block; + } + + /* get a new block */ + if (context->free) { + block = context->free; + context->free = NULL; + } else { + block = malloc(sizeof(GIFENCODERBLOCK)); + if (!block) + return 0; + } + + block->size = 0; + block->next = NULL; + + context->block = block; + + } + + /* write new byte to block */ + context->block->data[context->block->size++] = byte; + + return 1; +} + +/* write a code word to the current block. this is a macro to make + sure it's inlined on all platforms */ + +#define EMIT(code) {\ + context->bitbuffer |= ((INT32) (code)) << context->bitcount;\ + context->bitcount += 9;\ + while (context->bitcount >= 8) {\ + if (!emit(context, (UINT8) context->bitbuffer)) {\ + state->errcode = IMAGING_CODEC_MEMORY;\ + return 0;\ + }\ + context->bitbuffer >>= 8;\ + context->bitcount -= 8;\ + }\ +} + +/* write a run. we use a combination of literals and combinations of + literals. this can give quite decent compression for images with + long stretches of identical pixels. but remember: if you want + really good compression, use another file format. */ + +#define EMIT_RUN(label) {\ +label:\ + while (context->count > 0) {\ + int run = 2;\ + EMIT(context->last);\ + context->count--;\ + if (state->count++ == LAST_CODE) {\ + EMIT(CLEAR_CODE);\ + state->count = FIRST_CODE;\ + goto label;\ + }\ + while (context->count >= run) {\ + EMIT(state->count - 1);\ + context->count -= run;\ + run++;\ + if (state->count++ == LAST_CODE) {\ + EMIT(CLEAR_CODE);\ + state->count = FIRST_CODE;\ + goto label;\ + }\ + }\ + if (context->count > 1) {\ + EMIT(state->count - 1 - (run - context->count));\ + context->count = 0;\ + if (state->count++ == LAST_CODE) {\ + EMIT(CLEAR_CODE);\ + state->count = FIRST_CODE;\ + }\ + break;\ + }\ + }\ +} + +int +ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* ptr; + int this; + + GIFENCODERBLOCK* block; + GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context; + + if (!state->state) { + + /* place a clear code in the output buffer */ + context->bitbuffer = CLEAR_CODE; + context->bitcount = 9; + + state->count = FIRST_CODE; + + if (context->interlace) { + context->interlace = 1; + context->step = 8; + } else + context->step = 1; + + context->last = -1; + + /* sanity check */ + if (state->xsize <= 0 || state->ysize <= 0) + state->state = ENCODE_EOF; + + } + + ptr = buf; + + for (;;) + + switch (state->state) { + + case INIT: + case ENCODE: + + /* identify and store a run of pixels */ + + if (state->x == 0 || state->x >= state->xsize) { + + if (!context->interlace && state->y >= state->ysize) { + state->state = ENCODE_EOF; + break; + } + + if (context->flush) { + state->state = FLUSH; + break; + } + + /* get another line of data */ + state->shuffle( + state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize + ); + + state->x = 0; + + if (state->state == INIT) { + /* preload the run-length buffer and get going */ + context->last = state->buffer[0]; + context->count = state->x = 1; + state->state = ENCODE; + } + + /* step forward, according to the interlace settings */ + state->y += context->step; + while (context->interlace && state->y >= state->ysize) + switch (context->interlace) { + case 1: + state->y = 4; + context->interlace = 2; + break; + case 2: + context->step = 4; + state->y = 2; + context->interlace = 3; + break; + case 3: + context->step = 2; + state->y = 1; + context->interlace = 0; + break; + default: + /* just make sure we don't loop forever */ + context->interlace = 0; + } + + } + + this = state->buffer[state->x++]; + + if (this == context->last) + context->count++; + else { + EMIT_RUN(label1); + context->last = this; + context->count = 1; + } + break; + + + case ENCODE_EOF: + + /* write the final run */ + EMIT_RUN(label2); + + /* write an end of image marker */ + EMIT(EOF_CODE); + + /* empty the bit buffer */ + while (context->bitcount > 0) { + if (!emit(context, (UINT8) context->bitbuffer)) { + state->errcode = IMAGING_CODEC_MEMORY; + return 0; + } + context->bitbuffer >>= 8; + context->bitcount -= 8; + } + + /* flush the last block, and exit */ + if (context->block) { + GIFENCODERBLOCK* block; + block = context->flush; + while (block && block->next) + block = block->next; + if (block) + block->next = context->block; + else + context->flush = context->block; + context->block = NULL; + } + + state->state = EXIT; + + /* fall through... */ + + case EXIT: + case FLUSH: + + while (context->flush) { + + /* get a block from the flush queue */ + block = context->flush; + + if (block->size > 0) { + + /* make sure it fits into the output buffer */ + if (bytes < block->size+1) + return ptr - buf; + + ptr[0] = block->size; + memcpy(ptr+1, block->data, block->size); + + ptr += block->size+1; + bytes -= block->size+1; + + } + + context->flush = block->next; + + if (context->free) + free(context->free); + context->free = block; + + } + + if (state->state == EXIT) { + /* this was the last block! */ + if (context->free) + free(context->free); + state->errcode = IMAGING_CODEC_END; + return ptr - buf; + } + + state->state = ENCODE; + break; + } +} diff --git a/libImaging/HexDecode.c b/libImaging/HexDecode.c new file mode 100644 index 000000000..14f5241dc --- /dev/null +++ b/libImaging/HexDecode.c @@ -0,0 +1,67 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for hex encoded image data + * + * history: + * 96-05-16 fl Created + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ + (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ + (v >= 'A' && v <= 'F') ? v - 'A' + 10 : -1) + +int +ImagingHexDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* ptr; + int a, b; + + ptr = buf; + + for (;;) { + + if (bytes < 2) + return ptr - buf; + + a = HEX(ptr[0]); + b = HEX(ptr[1]); + + if (a < 0 || b < 0) { + + ptr++; + bytes--; + + } else { + + ptr += 2; + bytes -= 2; + + state->buffer[state->x] = (a<<4) + b; + + if (++state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y], state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + } + } +} diff --git a/libImaging/Histo.c b/libImaging/Histo.c new file mode 100644 index 000000000..513c84475 --- /dev/null +++ b/libImaging/Histo.c @@ -0,0 +1,169 @@ +/* + * The Python Imaging Library + * $Id$ + * + * histogram support + * + * history: + * 1995-06-15 fl Created. + * 1996-04-05 fl Fixed histogram for multiband images. + * 1997-02-23 fl Added mask support + * 1998-07-01 fl Added basic 32-bit float/integer support + * + * Copyright (c) 1997-2003 by Secret Labs AB. + * Copyright (c) 1995-2003 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +/* HISTOGRAM */ +/* -------------------------------------------------------------------- + * Take a histogram of an image. Returns a histogram object containing + * 256 slots per band in the input image. + */ + +void +ImagingHistogramDelete(ImagingHistogram h) +{ + if (h->histogram) + free(h->histogram); + free(h); +} + +ImagingHistogram +ImagingHistogramNew(Imaging im) +{ + ImagingHistogram h; + + /* Create histogram descriptor */ + h = calloc(1, sizeof(struct ImagingHistogramInstance)); + strcpy(h->mode, im->mode); + h->bands = im->bands; + h->histogram = calloc(im->pixelsize, 256 * sizeof(long)); + + return h; +} + +ImagingHistogram +ImagingGetHistogram(Imaging im, Imaging imMask, void* minmax) +{ + ImagingSectionCookie cookie; + int x, y, i; + ImagingHistogram h; + INT32 imin, imax; + FLOAT32 fmin, fmax, scale; + + if (!im) + return ImagingError_ModeError(); + + if (imMask) { + /* Validate mask */ + if (im->xsize != imMask->xsize || im->ysize != imMask->ysize) + return ImagingError_Mismatch(); + if (strcmp(imMask->mode, "1") != 0 && strcmp(imMask->mode, "L") != 0) + return ImagingError_ValueError("bad transparency mask"); + } + + h = ImagingHistogramNew(im); + + if (imMask) { + /* mask */ + if (im->image8) { + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + if (imMask->image8[y][x] != 0) + h->histogram[im->image8[y][x]]++; + ImagingSectionLeave(&cookie); + } else { /* yes, we need the braces. C isn't Python! */ + if (im->type != IMAGING_TYPE_UINT8) + return ImagingError_ModeError(); + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image32[y]; + for (x = 0; x < im->xsize; x++) + if (imMask->image8[y][x] != 0) { + h->histogram[(*in++)]++; + h->histogram[(*in++)+256]++; + h->histogram[(*in++)+512]++; + h->histogram[(*in++)+768]++; + } else + in += 4; + } + ImagingSectionLeave(&cookie); + } + } else { + /* mask not given; process pixels in image */ + if (im->image8) { + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++) + h->histogram[im->image8[y][x]]++; + ImagingSectionLeave(&cookie); + } else { + switch (im->type) { + case IMAGING_TYPE_UINT8: + ImagingSectionEnter(&cookie); + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + for (x = 0; x < im->xsize; x++) { + h->histogram[(*in++)]++; + h->histogram[(*in++)+256]++; + h->histogram[(*in++)+512]++; + h->histogram[(*in++)+768]++; + } + } + ImagingSectionLeave(&cookie); + break; + case IMAGING_TYPE_INT32: + if (!minmax) + return ImagingError_ValueError("min/max not given"); + if (!im->xsize || !im->ysize) + break; + imin = ((INT32*) minmax)[0]; + imax = ((INT32*) minmax)[1]; + if (imin >= imax) + break; + ImagingSectionEnter(&cookie); + scale = 255.0F / (imax - imin); + for (y = 0; y < im->ysize; y++) { + INT32* in = im->image32[y]; + for (x = 0; x < im->xsize; x++) { + i = (int) (((*in++)-imin)*scale); + if (i >= 0 && i < 256) + h->histogram[i]++; + } + } + ImagingSectionLeave(&cookie); + break; + case IMAGING_TYPE_FLOAT32: + if (!minmax) + return ImagingError_ValueError("min/max not given"); + if (!im->xsize || !im->ysize) + break; + fmin = ((FLOAT32*) minmax)[0]; + fmax = ((FLOAT32*) minmax)[1]; + if (fmin >= fmax) + break; + ImagingSectionEnter(&cookie); + scale = 255.0F / (fmax - fmin); + for (y = 0; y < im->ysize; y++) { + FLOAT32* in = (FLOAT32*) im->image32[y]; + for (x = 0; x < im->xsize; x++) { + i = (int) (((*in++)-fmin)*scale); + if (i >= 0 && i < 256) + h->histogram[i]++; + } + } + ImagingSectionLeave(&cookie); + break; + } + } + } + + return h; +} diff --git a/libImaging/ImDib.h b/libImaging/ImDib.h new file mode 100644 index 000000000..2effd3870 --- /dev/null +++ b/libImaging/ImDib.h @@ -0,0 +1,64 @@ +/* + * The Python Imaging Library + * $Id$ + * + * Windows DIB specifics + * + * Copyright (c) Secret Labs AB 1997-98. + * Copyright (c) Fredrik Lundh 1996. + * + * See the README file for information on usage and redistribution. + */ + +#ifdef WIN32 + +#if (defined(_MSC_VER) && _MSC_VER >= 1200) || (defined __GNUC__) +/* already defined in basetsd.h */ +#undef INT32 +#undef INT64 +#undef UINT32 +#endif + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct ImagingDIBInstance { + /* Windows interface */ + HDC dc; + HBITMAP bitmap; + HGDIOBJ old_bitmap; + BITMAPINFO *info; + UINT8 *bits; + HPALETTE palette; + /* Used by cut and paste */ + char mode[4]; + int xsize, ysize; + int pixelsize; + int linesize; + ImagingShuffler pack; + ImagingShuffler unpack; +}; + +typedef struct ImagingDIBInstance* ImagingDIB; + +extern char* ImagingGetModeDIB(int size_out[2]); + +extern ImagingDIB ImagingNewDIB(const char *mode, int xsize, int ysize); + +extern void ImagingDeleteDIB(ImagingDIB im); + +extern void ImagingDrawDIB(ImagingDIB dib, int dc, int dst[4], int src[4]); +extern void ImagingExposeDIB(ImagingDIB dib, int dc); + +extern int ImagingQueryPaletteDIB(ImagingDIB dib, int dc); + +extern void ImagingPasteDIB(ImagingDIB dib, Imaging im, int xy[4]); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/libImaging/ImPlatform.h b/libImaging/ImPlatform.h new file mode 100644 index 000000000..4ffd6fdef --- /dev/null +++ b/libImaging/ImPlatform.h @@ -0,0 +1,72 @@ +/* + * The Python Imaging Library + * $Id$ + * + * platform declarations for the imaging core library + * + * Copyright (c) Fredrik Lundh 1995-2003. + */ + +#include "Python.h" + +/* Check that we have an ANSI compliant compiler */ +#ifndef HAVE_PROTOTYPES +#error Sorry, this library requires support for ANSI prototypes. +#endif +#ifndef STDC_HEADERS +#error Sorry, this library requires ANSI header files. +#endif + +#if defined(_MSC_VER) +#ifndef WIN32 +#define WIN32 +#endif +/* VC++ 4.0 is a bit annoying when it comes to precision issues (like + claiming that "float a = 0.0;" would lead to loss of precision). I + don't like to see warnings from my code, but since I still want to + keep it readable, I simply switch off a few warnings instead of adding + the tons of casts that VC++ seem to require. This code is compiled + with numerous other compilers as well, so any real errors are likely + to be catched anyway. */ +#pragma warning(disable: 4244) /* conversion from 'float' to 'int' */ +#endif + +#if defined(_MSC_VER) +#define inline __inline +#elif !defined(USE_INLINE) +#define inline +#endif + +#if SIZEOF_SHORT == 2 +#define INT16 short +#elif SIZEOF_INT == 2 +#define INT16 int +#else +#define INT16 short /* most things works just fine anyway... */ +#endif + +#if SIZEOF_SHORT == 4 +#define INT32 short +#elif SIZEOF_INT == 4 +#define INT32 int +#elif SIZEOF_LONG == 4 +#define INT32 long +#else +#error Cannot find required 32-bit integer type +#endif + +#if SIZEOF_LONG == 8 +#define INT64 long +#elif SIZEOF_LONG_LONG == 8 +#define INT64 long +#endif + +/* assume IEEE; tweak if necessary (patches are welcome) */ +#define FLOAT32 float +#define FLOAT64 double + +#define INT8 signed char +#define UINT8 unsigned char + +#define UINT16 unsigned INT16 +#define UINT32 unsigned INT32 diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h new file mode 100644 index 000000000..4609376ad --- /dev/null +++ b/libImaging/Imaging.h @@ -0,0 +1,497 @@ +/* + * The Python Imaging Library + * $Id$ + * + * declarations for the imaging core library + * + * Copyright (c) 1997-2005 by Secret Labs AB + * Copyright (c) 1995-2005 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + + +#include "ImPlatform.h" + + +#if defined(__cplusplus) +extern "C" { +#endif + + +#ifndef M_PI +#define M_PI 3.14159265359 +#endif + + +/* -------------------------------------------------------------------- */ + +/* + * Image data organization: + * + * mode bytes byte order + * ------------------------------- + * 1 1 1 + * L 1 L + * P 1 P + * I 4 I (32-bit integer, native byte order) + * F 4 F (32-bit IEEE float, native byte order) + * RGB 4 R, G, B, - + * RGBA 4 R, G, B, A + * CMYK 4 C, M, Y, K + * YCbCr 4 Y, Cb, Cr, - + * + * experimental modes (incomplete): + * LA 4 L, -, -, A + * PA 4 P, -, -, A + * I;16 2 I (16-bit integer, native byte order) + * + * "P" is an 8-bit palette mode, which should be mapped through the + * palette member to get an output image. Check palette->mode to + * find the corresponding "real" mode. + * + * For information on how to access Imaging objects from your own C + * extensions, see http://www.effbot.org/zone/pil-extending.htm + */ + +/* Handles */ + +typedef struct ImagingMemoryInstance* Imaging; + +typedef struct ImagingAccessInstance* ImagingAccess; +typedef struct ImagingHistogramInstance* ImagingHistogram; +typedef struct ImagingOutlineInstance* ImagingOutline; +typedef struct ImagingPaletteInstance* ImagingPalette; + +/* handle magics (used with PyCObject). */ +#define IMAGING_MAGIC "PIL Imaging" + +/* pixel types */ +#define IMAGING_TYPE_UINT8 0 +#define IMAGING_TYPE_INT32 1 +#define IMAGING_TYPE_FLOAT32 2 +#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */ + +struct ImagingMemoryInstance { + + /* Format */ + char mode[4+1]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK") */ + int type; /* Data type (IMAGING_TYPE_*) */ + int depth; /* Depth (ignored in this version) */ + int bands; /* Number of bands (1, 2, 3, or 4) */ + int xsize; /* Image dimension. */ + int ysize; + + /* Colour palette (for "P" images only) */ + ImagingPalette palette; + + /* Data pointers */ + UINT8 **image8; /* Set for 8-bit images (pixelsize=1). */ + INT32 **image32; /* Set for 32-bit images (pixelsize=4). */ + + /* Internals */ + char **image; /* Actual raster data. */ + char *block; /* Set if data is allocated in a single block. */ + + int pixelsize; /* Size of a pixel, in bytes (1, 2 or 4) */ + int linesize; /* Size of a line, in bytes (xsize * pixelsize) */ + + /* Virtual methods */ + void (*destroy)(Imaging im); +}; + + +#define IMAGING_PIXEL_1(im,x,y) ((im)->image8[(y)][(x)]) +#define IMAGING_PIXEL_L(im,x,y) ((im)->image8[(y)][(x)]) +#define IMAGING_PIXEL_LA(im,x,y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_P(im,x,y) ((im)->image8[(y)][(x)]) +#define IMAGING_PIXEL_PA(im,x,y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_I(im,x,y) ((im)->image32[(y)][(x)]) +#define IMAGING_PIXEL_F(im,x,y) (((FLOAT32*)(im)->image32[y])[x]) +#define IMAGING_PIXEL_RGB(im,x,y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_RGBA(im,x,y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_CMYK(im,x,y) ((im)->image[(y)][(x)*4]) +#define IMAGING_PIXEL_YCbCr(im,x,y) ((im)->image[(y)][(x)*4]) + +#define IMAGING_PIXEL_UINT8(im,x,y) ((im)->image8[(y)][(x)]) +#define IMAGING_PIXEL_INT32(im,x,y) ((im)->image32[(y)][(x)]) +#define IMAGING_PIXEL_FLOAT32(im,x,y) (((FLOAT32*)(im)->image32[y])[x]) + +struct ImagingAccessInstance { + const char* mode; + void* (*line)(Imaging im, int x, int y); + void (*get_pixel)(Imaging im, int x, int y, void* pixel); + void (*put_pixel)(Imaging im, int x, int y, const void* pixel); +}; + +struct ImagingHistogramInstance { + + /* Format */ + char mode[4+1]; /* Band names (of corresponding source image) */ + int bands; /* Number of bands (1, 3, or 4) */ + + /* Data */ + long *histogram; /* Histogram (bands*256 longs) */ + +}; + + +struct ImagingPaletteInstance { + + /* Format */ + char mode[4+1]; /* Band names */ + + /* Data */ + UINT8 palette[1024];/* Palette data (same format as image data) */ + + INT16* cache; /* Palette cache (used for predefined palettes) */ + int keep_cache; /* This palette will be reused; keep cache */ + +}; + + +/* Objects */ +/* ------- */ + +extern int ImagingNewCount; + +extern Imaging ImagingNew(const char* mode, int xsize, int ysize); +extern Imaging ImagingNew2(const char* mode, Imaging imOut, Imaging imIn); +extern void ImagingDelete(Imaging im); + +extern Imaging ImagingNewBlock(const char* mode, int xsize, int ysize); +extern Imaging ImagingNewArray(const char* mode, int xsize, int ysize); +extern Imaging ImagingNewMap(const char* filename, int readonly, + const char* mode, int xsize, int ysize); + +extern Imaging ImagingNewPrologue(const char *mode, + unsigned xsize, unsigned ysize); +extern Imaging ImagingNewPrologueSubtype(const char *mode, + unsigned xsize, unsigned ysize, + int structure_size); +extern Imaging ImagingNewEpilogue(Imaging im); + +extern void ImagingCopyInfo(Imaging destination, Imaging source); + +extern void ImagingHistogramDelete(ImagingHistogram histogram); + +extern void ImagingAccessInit(void); +extern ImagingAccess ImagingAccessNew(Imaging im); +extern void _ImagingAccessDelete(Imaging im, ImagingAccess access); +#define ImagingAccessDelete(im, access) /* nop, for now */ +/*#define ImagingAccessDelete(im, access) \ + ((access)->dynamic ? _ImagingAccessDelete((im), (access)), 0 : 0)) */ + +extern ImagingPalette ImagingPaletteNew(const char *mode); +extern ImagingPalette ImagingPaletteNewBrowser(void); +extern ImagingPalette ImagingPaletteDuplicate(ImagingPalette palette); +extern void ImagingPaletteDelete(ImagingPalette palette); + +extern int ImagingPaletteCachePrepare(ImagingPalette palette); +extern void ImagingPaletteCacheUpdate(ImagingPalette palette, + int r, int g, int b); +extern void ImagingPaletteCacheDelete(ImagingPalette palette); + +#define ImagingPaletteCache(p, r, g, b)\ + p->cache[(r>>2) + (g>>2)*64 + (b>>2)*64*64] + +extern Imaging ImagingQuantize(Imaging im, int colours, int mode, int kmeans); + +/* Threading */ +/* --------- */ + +typedef void* ImagingSectionCookie; + +extern void ImagingSectionEnter(ImagingSectionCookie* cookie); +extern void ImagingSectionLeave(ImagingSectionCookie* cookie); + +/* Exceptions */ +/* ---------- */ + +extern void* ImagingError_IOError(void); +extern void* ImagingError_MemoryError(void); +extern void* ImagingError_ModeError(void); /* maps to ValueError by default */ +extern void* ImagingError_Mismatch(void); /* maps to ValueError by default */ +extern void* ImagingError_ValueError(const char* message); +extern void ImagingError_Clear(void); + +/* Transform callbacks */ +/* ------------------- */ + +/* standard transforms */ +#define IMAGING_TRANSFORM_AFFINE 0 +#define IMAGING_TRANSFORM_PERSPECTIVE 2 +#define IMAGING_TRANSFORM_QUAD 3 + + +/* standard filters */ +#define IMAGING_TRANSFORM_NEAREST 0 +#define IMAGING_TRANSFORM_ANTIALIAS 1 +#define IMAGING_TRANSFORM_BILINEAR 2 +#define IMAGING_TRANSFORM_BICUBIC 3 + +typedef int (*ImagingTransformMap)(double* X, double* Y, + int x, int y, void* data); +typedef int (*ImagingTransformFilter)(void* out, Imaging im, + double x, double y, + void* data); + +/* Image Manipulation Methods */ +/* -------------------------- */ + +extern Imaging ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha); +extern Imaging ImagingCopy(Imaging im); +extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither); +extern Imaging ImagingConvertInPlace(Imaging im, const char* mode); +extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]); +extern Imaging ImagingCrop(Imaging im, int x0, int y0, int x1, int y1); +extern Imaging ImagingExpand(Imaging im, int x, int y, int mode); +extern Imaging ImagingFill(Imaging im, const void* ink); +extern int ImagingFill2( + Imaging into, const void* ink, Imaging mask, + int x0, int y0, int x1, int y1); +extern Imaging ImagingFillBand(Imaging im, int band, int color); +extern Imaging ImagingFillLinearGradient(const char* mode); +extern Imaging ImagingFillRadialGradient(const char* mode); +extern Imaging ImagingFilter( + Imaging im, int xsize, int ysize, const FLOAT32* kernel, + FLOAT32 offset, FLOAT32 divisor); +extern Imaging ImagingFlipLeftRight(Imaging imOut, Imaging imIn); +extern Imaging ImagingFlipTopBottom(Imaging imOut, Imaging imIn); +extern Imaging ImagingGaussianBlur(Imaging im, Imaging imOut, float radius); +extern Imaging ImagingGetBand(Imaging im, int band); +extern int ImagingGetBBox(Imaging im, int bbox[4]); +typedef struct { int x, y; INT32 count; INT32 pixel; } ImagingColorItem; +extern ImagingColorItem* ImagingGetColors(Imaging im, int maxcolors, + int *colors); +extern int ImagingGetExtrema(Imaging im, void *extrema); +extern int ImagingGetProjection(Imaging im, UINT8* xproj, UINT8* yproj); +extern ImagingHistogram ImagingGetHistogram( + Imaging im, Imaging mask, void *extrema); +extern Imaging ImagingModeFilter(Imaging im, int size); +extern Imaging ImagingNegative(Imaging im); +extern Imaging ImagingOffset(Imaging im, int xoffset, int yoffset); +extern int ImagingPaste( + Imaging into, Imaging im, Imaging mask, + int x0, int y0, int x1, int y1); +extern Imaging ImagingPoint( + Imaging im, const char* tablemode, const void* table); +extern Imaging ImagingPointTransform( + Imaging imIn, double scale, double offset); +extern Imaging ImagingPutBand(Imaging im, Imaging imIn, int band); +extern Imaging ImagingRankFilter(Imaging im, int size, int rank); +extern Imaging ImagingResize(Imaging imOut, Imaging imIn, int filter); +extern Imaging ImagingRotate( + Imaging imOut, Imaging imIn, double theta, int filter); +extern Imaging ImagingRotate90(Imaging imOut, Imaging imIn); +extern Imaging ImagingRotate180(Imaging imOut, Imaging imIn); +extern Imaging ImagingRotate270(Imaging imOut, Imaging imIn); +extern Imaging ImagingStretch(Imaging imOut, Imaging imIn, int filter); +extern Imaging ImagingTransformPerspective( + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + double a[8], int filter, int fill); +extern Imaging ImagingTransformAffine( + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + double a[6], int filter, int fill); +extern Imaging ImagingTransformQuad( + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + double a[8], int filter, int fill); +extern Imaging ImagingTransform( + Imaging imOut, Imaging imIn, int x0, int y0, int x1, int y1, + ImagingTransformMap transform, void* transform_data, + ImagingTransformFilter filter, void* filter_data, + int fill); +extern Imaging ImagingUnsharpMask( + Imaging im, Imaging imOut, float radius, int percent, int threshold); + +extern Imaging ImagingCopy2(Imaging imOut, Imaging imIn); +extern Imaging ImagingConvert2(Imaging imOut, Imaging imIn); + +/* Channel operations */ +/* any mode, except "F" */ +extern Imaging ImagingChopLighter(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopDarker(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopDifference(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopMultiply(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopScreen(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopAdd( + Imaging imIn1, Imaging imIn2, float scale, int offset); +extern Imaging ImagingChopSubtract( + Imaging imIn1, Imaging imIn2, float scale, int offset); +extern Imaging ImagingChopAddModulo(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopSubtractModulo(Imaging imIn1, Imaging imIn2); + +/* "1" images only */ +extern Imaging ImagingChopAnd(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopOr(Imaging imIn1, Imaging imIn2); +extern Imaging ImagingChopXor(Imaging imIn1, Imaging imIn2); + +/* Image measurement */ +extern void ImagingCrack(Imaging im, int x0, int y0); + +/* Graphics */ +struct ImagingAffineMatrixInstance { + float a[9]; +}; + +typedef struct ImagingAffineMatrixInstance *ImagingAffineMatrix; + +extern int ImagingDrawArc(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int op); +extern int ImagingDrawBitmap(Imaging im, int x0, int y0, Imaging bitmap, + const void* ink, int op); +extern int ImagingDrawChord(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int fill, + int op); +extern int ImagingDrawEllipse(Imaging im, int x0, int y0, int x1, int y1, + const void* ink, int fill, int op); +extern int ImagingDrawLine(Imaging im, int x0, int y0, int x1, int y1, + const void* ink, int op); +extern int ImagingDrawWideLine(Imaging im, int x0, int y0, int x1, int y1, + const void* ink, int width, int op); +extern int ImagingDrawPieslice(Imaging im, int x0, int y0, int x1, int y1, + int start, int end, const void* ink, int fill, + int op); +extern int ImagingDrawPoint(Imaging im, int x, int y, const void* ink, int op); +extern int ImagingDrawPolygon(Imaging im, int points, int *xy, + const void* ink, int fill, int op); +extern int ImagingDrawRectangle(Imaging im, int x0, int y0, int x1, int y1, + const void* ink, int fill, int op); + +/* Level 2 graphics (WORK IN PROGRESS) */ +extern ImagingOutline ImagingOutlineNew(void); +extern void ImagingOutlineDelete(ImagingOutline outline); + +extern int ImagingDrawOutline(Imaging im, ImagingOutline outline, + const void* ink, int fill, int op); + +extern int ImagingOutlineMove(ImagingOutline outline, float x, float y); +extern int ImagingOutlineLine(ImagingOutline outline, float x, float y); +extern int ImagingOutlineCurve(ImagingOutline outline, float x1, float y1, + float x2, float y2, float x3, float y3); +extern int ImagingOutlineTransform(ImagingOutline outline, double a[6]); + +extern int ImagingOutlineClose(ImagingOutline outline); + +/* Special effects */ +extern Imaging ImagingEffectSpread(Imaging imIn, int distance); +extern Imaging ImagingEffectNoise(int xsize, int ysize, float sigma); +extern Imaging ImagingEffectMandelbrot(int xsize, int ysize, + double extent[4], int quality); + +/* Obsolete */ +extern int ImagingToString(Imaging im, int orientation, char *buffer); +extern int ImagingFromString(Imaging im, int orientation, char *buffer); + + +/* File I/O */ +/* -------- */ + +/* Built-in drivers */ +extern Imaging ImagingOpenPPM(const char* filename); +extern int ImagingSavePPM(Imaging im, const char* filename); + +/* Utility functions */ +extern UINT32 ImagingCRC32(UINT32 crc, UINT8* buffer, int bytes); + +/* Codecs */ +typedef struct ImagingCodecStateInstance *ImagingCodecState; +typedef int (*ImagingCodec)(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); + +extern int ImagingBitDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingEpsEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingFliDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingGifDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingGifEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingHexDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#ifdef HAVE_LIBJPEG +extern int ImagingJpegDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingJpegEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#endif +extern int ImagingLzwDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#ifdef HAVE_LIBMPEG +extern int ImagingMpegDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#endif +extern int ImagingMspDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingPackbitsDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingPcdDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingPcxDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingPcxEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingRawDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingRawEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingSunRleDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingTgaRleDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingXbmDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingXbmEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#ifdef HAVE_LIBZ +extern int ImagingZipDecode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +extern int ImagingZipEncode(Imaging im, ImagingCodecState state, + UINT8* buffer, int bytes); +#endif + +typedef void (*ImagingShuffler)(UINT8* out, const UINT8* in, int pixels); + +/* Public shufflers */ +extern void ImagingPackRGB(UINT8* out, const UINT8* in, int pixels); +extern void ImagingPackBGR(UINT8* out, const UINT8* in, int pixels); +extern void ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels); +extern void ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels); +extern void ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels); +extern void ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels); +extern void ImagingUnpackYCbCr(UINT8* out, const UINT8* in, int pixels); + +extern void ImagingConvertRGB2YCbCr(UINT8* out, const UINT8* in, int pixels); +extern void ImagingConvertYCbCr2RGB(UINT8* out, const UINT8* in, int pixels); + +extern ImagingShuffler ImagingFindUnpacker(const char* mode, + const char* rawmode, int* bits_out); +extern ImagingShuffler ImagingFindPacker(const char* mode, + const char* rawmode, int* bits_out); + +struct ImagingCodecStateInstance { + int count; + int state; + int errcode; + int x, y; + int ystep; + int xsize, ysize, xoff, yoff; + ImagingShuffler shuffle; + int bits, bytes; + UINT8 *buffer; + void *context; +}; + +/* Errcodes */ +#define IMAGING_CODEC_END 1 +#define IMAGING_CODEC_OVERRUN -1 +#define IMAGING_CODEC_BROKEN -2 +#define IMAGING_CODEC_UNKNOWN -3 +#define IMAGING_CODEC_CONFIG -8 +#define IMAGING_CODEC_MEMORY -9 + +#if defined(__cplusplus) +} +#endif diff --git a/libImaging/Jpeg.h b/libImaging/Jpeg.h new file mode 100644 index 000000000..d39165f3c --- /dev/null +++ b/libImaging/Jpeg.h @@ -0,0 +1,104 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * declarations for the IJG JPEG codec interface. + * + * Copyright (c) 1995-2001 by Secret Labs AB + * Copyright (c) 1995-1996 by Fredrik Lundh + */ + +#include "jpeglib.h" + +#include + + +typedef struct { + struct jpeg_error_mgr pub; /* "public" fields */ + jmp_buf setjmp_buffer; /* for return to caller */ +} JPEGERROR; + + +/* -------------------------------------------------------------------- */ +/* Decoder */ + +typedef struct { + struct jpeg_source_mgr pub; + int skip; +} JPEGSOURCE; + +typedef struct { + + /* CONFIGURATION */ + + /* Jpeg file mode (empty if not known) */ + char jpegmode[8+1]; + + /* Converter output mode (input to the shuffler). If empty, + convert conversions are disabled */ + char rawmode[8+1]; + + /* If set, trade quality for speed */ + int draft; + + /* Scale factor (1, 2, 4, 8) */ + int scale; + + /* PRIVATE CONTEXT (set by decoder) */ + + struct jpeg_decompress_struct cinfo; + + JPEGERROR error; + + JPEGSOURCE source; + +} JPEGSTATE; + + +/* -------------------------------------------------------------------- */ +/* Encoder */ + +typedef struct { + struct jpeg_destination_mgr pub; + /* might add something some other day */ +} JPEGDESTINATION; + +typedef struct { + + /* CONFIGURATION */ + + /* Quality (1-100, 0 means default) */ + int quality; + + /* Progressive mode */ + int progressive; + + /* Smoothing factor (1-100, 0 means none) */ + int smooth; + + /* Optimize Huffman tables (slow) */ + int optimize; + + /* Stream type (0=full, 1=tables only, 2=image only) */ + int streamtype; + + /* DPI setting (0=square pixels, otherwide DPI) */ + int xdpi, ydpi; + + /* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */ + int subsampling; + + /* Extra data (to be injected after header) */ + char* extra; int extra_size; + + /* PRIVATE CONTEXT (set by encoder) */ + + struct jpeg_compress_struct cinfo; + + JPEGERROR error; + + JPEGDESTINATION destination; + + int extra_offset; + +} JPEGENCODERSTATE; diff --git a/libImaging/JpegDecode.c b/libImaging/JpegDecode.c new file mode 100644 index 000000000..11b2767ba --- /dev/null +++ b/libImaging/JpegDecode.c @@ -0,0 +1,267 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for JPEG image data. + * + * history: + * 1996-05-02 fl Created + * 1996-05-05 fl Handle small JPEG files correctly + * 1996-05-28 fl Added "draft mode" support + * 1997-01-25 fl Added colour conversion override + * 1998-01-31 fl Adapted to libjpeg 6a + * 1998-07-12 fl Extended YCbCr support + * 1998-12-29 fl Added new state to handle suspension in multipass modes + * 2000-10-12 fl Suppress warnings + * 2000-12-04 fl Suppress errors beyond end of image data + * + * Copyright (c) 1998-2000 Secret Labs AB + * Copyright (c) 1996-2000 Fredrik Lundh + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + +#ifdef HAVE_LIBJPEG + +#undef HAVE_PROTOTYPES +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT16 +#undef INT32 + +#include "Jpeg.h" + + +/* -------------------------------------------------------------------- */ +/* Suspending input handler */ +/* -------------------------------------------------------------------- */ + +METHODDEF(void) +stub(j_decompress_ptr cinfo) +{ + /* empty */ +} + +METHODDEF(boolean) +fill_input_buffer(j_decompress_ptr cinfo) +{ + /* Suspension */ + return FALSE; +} + +METHODDEF(void) +skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + JPEGSOURCE* source = (JPEGSOURCE*) cinfo->src; + + if (num_bytes > (long) source->pub.bytes_in_buffer) { + /* We need to skip more data than we have in the buffer. + This will force the JPEG library to suspend decoding. */ + source->skip = num_bytes - source->pub.bytes_in_buffer; + source->pub.next_input_byte += source->pub.bytes_in_buffer; + source->pub.bytes_in_buffer = 0; + } else { + /* Skip portion of the buffer */ + source->pub.bytes_in_buffer -= num_bytes; + source->pub.next_input_byte += num_bytes; + source->skip = 0; + } +} + + +GLOBAL(void) +jpeg_buffer_src(j_decompress_ptr cinfo, JPEGSOURCE* source) +{ + cinfo->src = (void*) source; + + /* Prepare for suspending reader */ + source->pub.init_source = stub; + source->pub.fill_input_buffer = fill_input_buffer; + source->pub.skip_input_data = skip_input_data; + source->pub.resync_to_restart = jpeg_resync_to_restart; + source->pub.term_source = stub; + source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + + source->skip = 0; +} + + +/* -------------------------------------------------------------------- */ +/* Error handler */ +/* -------------------------------------------------------------------- */ + +METHODDEF(void) +error(j_common_ptr cinfo) +{ + JPEGERROR* error; + error = (JPEGERROR*) cinfo->err; + longjmp(error->setjmp_buffer, 1); +} + +METHODDEF(void) +output(j_common_ptr cinfo) +{ + /* nothing */ +} + +/* -------------------------------------------------------------------- */ +/* Decoder */ +/* -------------------------------------------------------------------- */ + +int +ImagingJpegDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + JPEGSTATE* context = (JPEGSTATE*) state->context; + int ok; + + if (setjmp(context->error.setjmp_buffer)) { + /* JPEG error handler */ + jpeg_destroy_decompress(&context->cinfo); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + if (!state->state) { + + /* Setup decompression context */ + context->cinfo.err = jpeg_std_error(&context->error.pub); + context->error.pub.error_exit = error; + context->error.pub.output_message = output; + jpeg_create_decompress(&context->cinfo); + jpeg_buffer_src(&context->cinfo, &context->source); + + /* Ready to decode */ + state->state = 1; + + } + + /* Load the source buffer */ + context->source.pub.next_input_byte = buf; + context->source.pub.bytes_in_buffer = bytes; + + if (context->source.skip > 0) { + skip_input_data(&context->cinfo, context->source.skip); + if (context->source.skip > 0) + return context->source.pub.next_input_byte - buf; + } + + switch (state->state) { + + case 1: + + /* Read JPEG header, until we find an image body. */ + do { + + /* Note that we cannot return unless we have decoded + as much data as possible. */ + ok = jpeg_read_header(&context->cinfo, FALSE); + + } while (ok == JPEG_HEADER_TABLES_ONLY); + + if (ok == JPEG_SUSPENDED) + break; + + /* Decoder settings */ + + /* jpegmode indicates whats in the file; if not set, we'll + trust the decoder */ + if (strcmp(context->jpegmode, "L") == 0) + context->cinfo.jpeg_color_space = JCS_GRAYSCALE; + else if (strcmp(context->jpegmode, "RGB") == 0) + context->cinfo.jpeg_color_space = JCS_RGB; + else if (strcmp(context->jpegmode, "CMYK") == 0) + context->cinfo.jpeg_color_space = JCS_CMYK; + else if (strcmp(context->jpegmode, "YCbCr") == 0) + context->cinfo.jpeg_color_space = JCS_YCbCr; + else if (strcmp(context->jpegmode, "YCbCrK") == 0) { + context->cinfo.jpeg_color_space = JCS_YCCK; + } + + /* rawmode indicates what we want from the decoder. if not + set, conversions are disabled */ + if (strcmp(context->rawmode, "L") == 0) + context->cinfo.out_color_space = JCS_GRAYSCALE; + else if (strcmp(context->rawmode, "RGB") == 0) + context->cinfo.out_color_space = JCS_RGB; + else if (strcmp(context->rawmode, "CMYK") == 0 || + strcmp(context->rawmode, "CMYK;I") == 0) + context->cinfo.out_color_space = JCS_CMYK; + else if (strcmp(context->rawmode, "YCbCr") == 0) + context->cinfo.out_color_space = JCS_YCbCr; + else if (strcmp(context->rawmode, "YCbCrK") == 0) + context->cinfo.out_color_space = JCS_YCCK; + else { + /* Disable decoder conversions */ + context->cinfo.jpeg_color_space = JCS_UNKNOWN; + context->cinfo.out_color_space = JCS_UNKNOWN; + } + + if (context->scale > 1) { + context->cinfo.scale_num = 1; + context->cinfo.scale_denom = context->scale; + } + if (context->draft) { + context->cinfo.do_fancy_upsampling = FALSE; + context->cinfo.dct_method = JDCT_FASTEST; + } + + state->state++; + /* fall through */ + + case 2: + + /* Set things up for decompression (this processes the entire + file if necessary to return data line by line) */ + if (!jpeg_start_decompress(&context->cinfo)) + break; + + state->state++; + /* fall through */ + + case 3: + + /* Decompress a single line of data */ + ok = 1; + while (state->y < state->ysize) { + ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1); + if (ok != 1) + break; + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + state->y++; + } + if (ok != 1) + break; + state->state++; + /* fall through */ + + case 4: + + /* Finish decompression */ + if (!jpeg_finish_decompress(&context->cinfo)) { + /* FIXME: add strictness mode test */ + if (state->y < state->ysize) + break; + } + + /* Clean up */ + jpeg_destroy_decompress(&context->cinfo); + /* if (jerr.pub.num_warnings) return BROKEN; */ + return -1; + + } + + /* Return number of bytes consumed */ + return context->source.pub.next_input_byte - buf; + +} + +#endif + diff --git a/libImaging/JpegEncode.c b/libImaging/JpegEncode.c new file mode 100644 index 000000000..1e2191a69 --- /dev/null +++ b/libImaging/JpegEncode.c @@ -0,0 +1,284 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * coder for JPEG data + * + * history: + * 1996-05-06 fl created + * 1996-07-16 fl don't drop last block of encoded data + * 1996-12-30 fl added quality and progressive settings + * 1997-01-08 fl added streamtype settings + * 1998-01-31 fl adapted to libjpeg 6a + * 1998-07-12 fl added YCbCr support + * 2001-04-16 fl added DPI write support + * + * Copyright (c) 1997-2001 by Secret Labs AB + * Copyright (c) 1995-1997 by Fredrik Lundh + * + * See the README file for details on usage and redistribution. + */ + + +#include "Imaging.h" + +#ifdef HAVE_LIBJPEG + +#undef HAVE_PROTOTYPES +#undef HAVE_STDLIB_H +#undef HAVE_STDDEF_H +#undef UINT8 +#undef UINT16 +#undef UINT32 +#undef INT16 +#undef INT32 + +#include "Jpeg.h" + +/* -------------------------------------------------------------------- */ +/* Suspending output handler */ +/* -------------------------------------------------------------------- */ + +METHODDEF(void) +stub(j_compress_ptr cinfo) +{ + /* empty */ +} + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + /* Suspension */ + return FALSE; +} + +GLOBAL(void) +jpeg_buffer_dest(j_compress_ptr cinfo, JPEGDESTINATION* destination) +{ + cinfo->dest = (void*) destination; + + destination->pub.init_destination = stub; + destination->pub.empty_output_buffer = empty_output_buffer; + destination->pub.term_destination = stub; +} + + +/* -------------------------------------------------------------------- */ +/* Error handler */ +/* -------------------------------------------------------------------- */ + +METHODDEF(void) +error(j_common_ptr cinfo) +{ + JPEGERROR* error; + error = (JPEGERROR*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(error->setjmp_buffer, 1); +} + + +/* -------------------------------------------------------------------- */ +/* Encoder */ +/* -------------------------------------------------------------------- */ + +int +ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + JPEGENCODERSTATE* context = (JPEGENCODERSTATE*) state->context; + int ok; + + if (setjmp(context->error.setjmp_buffer)) { + /* JPEG error handler */ + jpeg_destroy_compress(&context->cinfo); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + if (!state->state) { + + /* Setup compression context (very similar to the decoder) */ + context->cinfo.err = jpeg_std_error(&context->error.pub); + context->error.pub.error_exit = error; + jpeg_create_compress(&context->cinfo); + jpeg_buffer_dest(&context->cinfo, &context->destination); + + context->extra_offset = 0; + + /* Ready to encode */ + state->state = 1; + + } + + /* Load the destination buffer */ + context->destination.pub.next_output_byte = buf; + context->destination.pub.free_in_buffer = bytes; + + switch (state->state) { + + case 1: + + context->cinfo.image_width = state->xsize; + context->cinfo.image_height = state->ysize; + + switch (state->bits) { + case 8: + context->cinfo.input_components = 1; + context->cinfo.in_color_space = JCS_GRAYSCALE; + break; + case 24: + context->cinfo.input_components = 3; + if (strcmp(im->mode, "YCbCr") == 0) + context->cinfo.in_color_space = JCS_YCbCr; + else + context->cinfo.in_color_space = JCS_RGB; + break; + case 32: + context->cinfo.input_components = 4; + context->cinfo.in_color_space = JCS_CMYK; + break; + default: + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + /* Compressor configuration */ + jpeg_set_defaults(&context->cinfo); + if (context->quality > 0) + jpeg_set_quality(&context->cinfo, context->quality, 1); + + /* Set subsampling options */ + switch (context->subsampling) + { + case 0: /* 1x1 1x1 1x1 (4:4:4) : None */ + { + context->cinfo.comp_info[0].h_samp_factor = 1; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + case 1: /* 2x1, 1x1, 1x1 (4:2:2) : Medium */ + { + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 1; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + case 2: /* 2x2, 1x1, 1x1 (4:1:1) : High */ + { + context->cinfo.comp_info[0].h_samp_factor = 2; + context->cinfo.comp_info[0].v_samp_factor = 2; + context->cinfo.comp_info[1].h_samp_factor = 1; + context->cinfo.comp_info[1].v_samp_factor = 1; + context->cinfo.comp_info[2].h_samp_factor = 1; + context->cinfo.comp_info[2].v_samp_factor = 1; + break; + } + default: + { + /* Use the lib's default */ + break; + } + } + if (context->progressive) + jpeg_simple_progression(&context->cinfo); + context->cinfo.smoothing_factor = context->smooth; + context->cinfo.optimize_coding = (boolean) context->optimize; + if (context->xdpi > 0 && context->ydpi > 0) { + context->cinfo.density_unit = 1; /* dots per inch */ + context->cinfo.X_density = context->xdpi; + context->cinfo.Y_density = context->ydpi; + } + switch (context->streamtype) { + case 1: + /* tables only -- not yet implemented */ + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + case 2: + /* image only */ + jpeg_suppress_tables(&context->cinfo, TRUE); + jpeg_start_compress(&context->cinfo, FALSE); + /* suppress extra section */ + context->extra_offset = context->extra_size; + break; + default: + /* interchange stream */ + jpeg_start_compress(&context->cinfo, TRUE); + break; + } + state->state++; + /* fall through */ + + case 2: + + if (context->extra) { + /* copy extra buffer to output buffer */ + unsigned int n = context->extra_size - context->extra_offset; + if (n > context->destination.pub.free_in_buffer) + n = context->destination.pub.free_in_buffer; + memcpy(context->destination.pub.next_output_byte, + context->extra + context->extra_offset, n); + context->destination.pub.next_output_byte += n; + context->destination.pub.free_in_buffer -= n; + context->extra_offset += n; + if (context->extra_offset >= context->extra_size) + state->state++; + else + break; + } else + state->state++; + + case 3: + + ok = 1; + while (state->y < state->ysize) { + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); + ok = jpeg_write_scanlines(&context->cinfo, &state->buffer, 1); + if (ok != 1) + break; + state->y++; + } + + if (ok != 1) + break; + state->state++; + /* fall through */ + + case 4: + + /* Finish compression */ + if (context->destination.pub.free_in_buffer < 100) + break; + jpeg_finish_compress(&context->cinfo); + + /* Clean up */ + if (context->extra) + free(context->extra); + jpeg_destroy_compress(&context->cinfo); + /* if (jerr.pub.num_warnings) return BROKEN; */ + state->errcode = IMAGING_CODEC_END; + break; + + } + + /* Return number of bytes in output buffer */ + return context->destination.pub.next_output_byte - buf; + +} + +const char* +ImagingJpegVersion(void) +{ + static char version[20]; + sprintf(version, "%d.%d", JPEG_LIB_VERSION / 10, JPEG_LIB_VERSION % 10); + return version; +} + +#endif diff --git a/libImaging/Lzw.h b/libImaging/Lzw.h new file mode 100644 index 000000000..7d087ac47 --- /dev/null +++ b/libImaging/Lzw.h @@ -0,0 +1,52 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * declarations for the TIFF LZW decoder. + * + * Copyright (c) Fredrik Lundh 1995-96. + */ + + +/* Max size for LZW code words */ + +#define LZWBITS 12 + +#define LZWTABLE (1< +#include /* memcpy() */ + +#include "Lzw.h" + + +int +ImagingLzwDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* p; + int c, i; + int thiscode; + LZWSTATE* context = (LZWSTATE*) state->context; + + unsigned char *ptr = buf; + + if (!state->state) { + + /* Clear code */ + context->clear = 1 << 8; + + /* End code */ + context->end = context->clear + 1; + + state->state = 1; + } + + for (;;) { + + if (state->state == 1) { + + /* First free entry in table */ + context->next = context->clear + 2; + + /* Initial code size */ + context->codesize = 8 + 1; + context->codemask = (1 << context->codesize) - 1; + + /* Buffer pointer. We fill the buffer from right, which + allows us to return all of it in one operation. */ + context->bufferindex = LZWBUFFER; + + state->state = 2; + } + + if (context->bufferindex < LZWBUFFER) { + + /* Return whole buffer in one chunk */ + i = LZWBUFFER - context->bufferindex; + p = &context->buffer[context->bufferindex]; + + context->bufferindex = LZWBUFFER; + + } else { + + /* Get current symbol */ + while (context->bitcount < context->codesize) { + + if (bytes < 1) + return ptr - buf;; + + /* Read next byte */ + c = *ptr++; bytes--; + + /* New bits are shifted in from from the right. */ + context->bitbuffer = (context->bitbuffer << 8) | c; + context->bitcount += 8; + + } + + /* Extract current symbol from bit buffer. */ + c = (context->bitbuffer >> (context->bitcount - + context->codesize)) + & context->codemask; + + /* Adjust buffer */ + context->bitcount -= context->codesize; + + /* If c is less than clear, it's a data byte. Otherwise, + it's either clear/end or a code symbol which should be + expanded. */ + + if (c == context->clear) { + if (state->state != 2) + state->state = 1; + continue; + } + + if (c == context->end) + break; + + i = 1; + p = &context->lastdata; + + if (state->state == 2) { + + /* First valid symbol after clear; use as is */ + if (c > context->clear) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->lastdata = context->lastcode = c; + state->state = 3; + + } else { + + thiscode = c; + + if (c > context->next) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + if (c == context->next) { + + /* c == next is allowed, by some strange reason */ + if (context->bufferindex <= 0) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->buffer[--context->bufferindex] = context->lastdata; + c = context->lastcode; + } + + while (c >= context->clear) { + + /* Copy data string to buffer (beginning from right) */ + + if (context->bufferindex <= 0 || c >= LZWTABLE) { + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } + + context->buffer[--context->bufferindex] = + context->data[c]; + c = context->link[c]; + } + + context->lastdata = c; + + if (context->next < LZWTABLE) { + + /* While we still have room for it, add this + symbol to the table. */ + context->data[context->next] = c; + context->link[context->next] = context->lastcode; + + context->next++; + + if (context->next == context->codemask && + context->codesize < LZWBITS) { + + /* Expand code size */ + context->codesize++; + context->codemask = (1 << context->codesize) - 1; + + } + } + context->lastcode = thiscode; + } + } + + /* Update the output image */ + for (c = 0; c < i; c++) { + + state->buffer[state->x] = p[c]; + + if (++state->x >= state->bytes) { + + int x, bpp; + + /* Apply filter */ + switch (context->filter) { + case 2: + /* Horizontal differing ("prior") */ + bpp = (state->bits + 7) / 8; + for (x = bpp; x < state->bytes; x++) + state->buffer[x] += state->buffer[x-bpp]; + } + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) + /* End of file (errcode = 0) */ + return -1; + } + } + } + + return ptr - buf; +} diff --git a/libImaging/Matrix.c b/libImaging/Matrix.c new file mode 100644 index 000000000..5d2f031cf --- /dev/null +++ b/libImaging/Matrix.c @@ -0,0 +1,74 @@ +/* + * The Python Imaging Library + * $Id$ + * + * colour and luminance matrix transforms + * + * history: + * 1996-05-18 fl: created (brute force implementation) + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +#define CLIPF(v) ((v <= 0.0) ? 0 : (v >= 255.0F) ? 255 : (UINT8) v) + + +Imaging +ImagingConvertMatrix(Imaging im, const char *mode, float m[]) +{ + Imaging imOut; + int x, y; + + /* Assume there's enough data in the buffer */ + if (!im) + return (Imaging) ImagingError_ModeError(); + + if (strcmp(mode, "L") == 0 && im->bands == 3) { + + imOut = ImagingNew("L", im->xsize, im->ysize); + if (!imOut) + return NULL; + + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + + for (x = 0; x < im->xsize; x++) { + float v = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; + out[x] = CLIPF(v); + in += 4; + } + } + + } else if (strlen(mode) == 3 && im->bands == 3) { + + imOut = ImagingNew(mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + for (y = 0; y < im->ysize; y++) { + UINT8* in = (UINT8*) im->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + + for (x = 0; x < im->xsize; x++) { + float v0 = m[0]*in[0] + m[1]*in[1] + m[2]*in[2] + m[3] + 0.5; + float v1 = m[4]*in[0] + m[5]*in[1] + m[6]*in[2] + m[7] + 0.5; + float v2 = m[8]*in[0] + m[9]*in[1] + m[10]*in[2] + m[11] + 0.5; + out[0] = CLIPF(v0); + out[1] = CLIPF(v1); + out[2] = CLIPF(v2); + in += 4; out += 4; + } + } + } else + return (Imaging) ImagingError_ModeError(); + + return imOut; +} diff --git a/libImaging/ModeFilter.c b/libImaging/ModeFilter.c new file mode 100644 index 000000000..ad11d87d4 --- /dev/null +++ b/libImaging/ModeFilter.c @@ -0,0 +1,78 @@ +/* + * The Python Imaging Library + * $Id$ + * + * mode filter + * + * history: + * 2002-06-08 fl Created (based on code from IFUNC95) + * 2004-10-05 fl Rewritten; use a simpler brute-force algorithm + * + * Copyright (c) Secret Labs AB 2002-2004. All rights reserved. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +Imaging +ImagingModeFilter(Imaging im, int size) +{ + Imaging imOut; + int x, y, i; + int xx, yy; + int maxcount; + UINT8 maxpixel; + int histogram[256]; + + if (!im || im->bands != 1 || im->type != IMAGING_TYPE_UINT8) + return (Imaging) ImagingError_ModeError(); + + imOut = ImagingNew(im->mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + size = size / 2; + + for (y = 0; y < imOut->ysize; y++) { + UINT8* out = &IMAGING_PIXEL_L(imOut, 0, y); + for (x = 0; x < imOut->xsize; x++) { + + /* calculate histogram over current area */ + + /* FIXME: brute force! to improve, update the histogram + incrementally. may also add a "frequent list", like in + the old implementation, but I'm not sure that's worth + the added complexity... */ + + memset(histogram, 0, sizeof(histogram)); + for (yy = y - size; yy <= y + size; yy++) + if (yy >= 0 && yy < imOut->ysize) { + UINT8* in = &IMAGING_PIXEL_L(im, 0, yy); + for (xx = x - size; xx <= x + size; xx++) + if (xx >= 0 && xx < imOut->xsize) + histogram[in[xx]]++; + } + + /* find most frequent pixel value in this region */ + maxpixel = 0; + maxcount = histogram[maxpixel]; + for (i = 1; i < 256; i++) + if (histogram[i] > maxcount) { + maxcount = histogram[i]; + maxpixel = (UINT8) i; + } + + if (maxcount > 2) + out[x] = maxpixel; + else + out[x] = IMAGING_PIXEL_L(im, x, y); + + } + + } + + ImagingCopyInfo(imOut, im); + + return imOut; +} diff --git a/libImaging/MspDecode.c b/libImaging/MspDecode.c new file mode 100644 index 000000000..b611098d8 --- /dev/null +++ b/libImaging/MspDecode.c @@ -0,0 +1,91 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for MSP version 2 data. + * + * history: + * 97-01-03 fl Created + * + * Copyright (c) Fredrik Lundh 1997. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingMspDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + int n; + UINT8* ptr; + + ptr = buf; + + for (;;) { + + if (bytes < 1) + return ptr - buf; + + if (ptr[0] == 0) { + + /* Run (3 bytes block) */ + if (bytes < 3) + break; + + n = ptr[1]; + + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + memset(state->buffer + state->x, ptr[2], n); + + ptr += 3; + bytes -= 3; + + } else { + + /* Literal (1+n bytes block) */ + n = ptr[0]; + + if (bytes < 1 + n) + break; + + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + memcpy(state->buffer + state->x, ptr + 1, n); + + ptr += 1 + n; + bytes -= 1 + n; + + } + + state->x += n; + + if (state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + } + + return ptr - buf; +} diff --git a/libImaging/Negative.c b/libImaging/Negative.c new file mode 100644 index 000000000..eaa790768 --- /dev/null +++ b/libImaging/Negative.c @@ -0,0 +1,42 @@ +/* + * The Python Imaging Library + * $Id$ + * + * negate image + * + * to do: + * FIXME: Maybe this should be implemented using ImagingPoint() + * + * history: + * 95-11-27 fl: Created + * + * Copyright (c) Fredrik Lundh 1995. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +Imaging +ImagingNegative(Imaging im) +{ + Imaging imOut; + int x, y; + + if (!im) + return (Imaging) ImagingError_ModeError(); + + imOut = ImagingNew(im->mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + for (y = 0; y < im->ysize; y++) + for (x = 0; x < im->linesize; x++) + imOut->image[y][x] = ~im->image[y][x]; + + return imOut; +} + diff --git a/libImaging/Offset.c b/libImaging/Offset.c new file mode 100644 index 000000000..f6d6e510e --- /dev/null +++ b/libImaging/Offset.c @@ -0,0 +1,61 @@ +/* + * The Python Imaging Library + * $Id$ + * + * offset an image in x and y directions + * + * history: + * 96-07-22 fl: Created + * 98-11-01 cgw@pgt.com: Fixed negative-array index bug + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +Imaging +ImagingOffset(Imaging im, int xoffset, int yoffset) +{ + int x, y; + Imaging imOut; + + if (!im) + return (Imaging) ImagingError_ModeError(); + + imOut = ImagingNew(im->mode, im->xsize, im->ysize); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, im); + + /* make offsets positive to avoid negative coordinates */ + xoffset %= im->xsize; + xoffset = im->xsize - xoffset; + if (xoffset < 0) + xoffset += im->xsize; + + yoffset %= im->ysize; + yoffset = im->ysize - yoffset; + if (yoffset < 0) + yoffset += im->ysize; + +#define OFFSET(image)\ + for (y = 0; y < im->ysize; y++)\ + for (x = 0; x < im->xsize; x++) {\ + int yi = (y + yoffset) % im->ysize;\ + int xi = (x + xoffset) % im->xsize;\ + imOut->image[y][x] = im->image[yi][xi];\ + } + + if (im->image8) + OFFSET(image8) + else + OFFSET(image32) + + return imOut; +} diff --git a/libImaging/Pack.c b/libImaging/Pack.c new file mode 100644 index 000000000..478de7499 --- /dev/null +++ b/libImaging/Pack.c @@ -0,0 +1,566 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * code to pack raw data + * + * history: + * 1996-04-30 fl Created + * 1996-05-12 fl Published a few RGB packers + * 1996-11-01 fl More RGB packers (Tk booster stuff) + * 1996-12-30 fl Added P;1, P;2 and P;4 packers + * 1997-06-02 fl Added F (F;32NF) packer + * 1997-08-28 fl Added 1 as L packer + * 1998-02-08 fl Added I packer + * 1998-03-09 fl Added mode field, RGBA/RGBX as RGB packers + * 1998-07-01 fl Added YCbCr support + * 1998-07-12 fl Added I 16 packer + * 1999-02-03 fl Added BGR packers + * 2003-09-26 fl Added LA/PA packers + * 2006-06-22 fl Added CMYK;I packer + * + * Copyright (c) 1997-2006 by Secret Labs AB. + * Copyright (c) 1996-1997 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#define R 0 +#define G 1 +#define B 2 +#define X 3 +#define A 3 + +#define C 0 +#define M 1 +#define Y 2 +#define K 3 + +/* byte swapping macros */ + +#define C16N\ + (out[0]=tmp[0], out[1]=tmp[1]); +#define C16S\ + (out[1]=tmp[0], out[0]=tmp[1]); +#define C32N\ + (out[0]=tmp[0], out[1]=tmp[1], out[2]=tmp[2], out[3]=tmp[3]); +#define C32S\ + (out[3]=tmp[0], out[2]=tmp[1], out[1]=tmp[2], out[0]=tmp[3]); +#define C64N\ + (out[0]=tmp[0], out[1]=tmp[1], out[2]=tmp[2], out[3]=tmp[3],\ + out[4]=tmp[4], out[5]=tmp[5], out[6]=tmp[6], out[7]=tmp[7]); +#define C64S\ + (out[7]=tmp[0], out[6]=tmp[1], out[5]=tmp[2], out[4]=tmp[3],\ + out[3]=tmp[4], out[2]=tmp[5], out[1]=tmp[6], out[0]=tmp[7]); + +#ifdef WORDS_BIGENDIAN +#define C16B C16N +#define C16L C16S +#define C32B C32N +#define C32L C32S +#define C64B C64N +#define C64L C64S +#else +#define C16B C16S +#define C16L C16N +#define C32B C32S +#define C32L C32N +#define C64B C64S +#define C64L C64N +#endif + +static void +pack1(UINT8* out, const UINT8* in, int pixels) +{ + int i, m, b; + /* bilevel (black is 0) */ + b = 0; m = 128; + for (i = 0; i < pixels; i++) { + if (in[i] != 0) + b |= m; + m >>= 1; + if (m == 0) { + *out++ = b; + b = 0; m = 128; + } + } + if (m != 128) + *out++ = b; +} + +static void +pack1I(UINT8* out, const UINT8* in, int pixels) +{ + int i, m, b; + /* bilevel (black is 1) */ + b = 0; m = 128; + for (i = 0; i < pixels; i++) { + if (in[i] == 0) + b |= m; + m >>= 1; + if (m == 0) { + *out++ = b; + b = 0; m = 128; + } + } + if (m != 128) + *out++ = b; +} + +static void +pack1R(UINT8* out, const UINT8* in, int pixels) +{ + int i, m, b; + /* bilevel, lsb first (black is 0) */ + b = 0; m = 1; + for (i = 0; i < pixels; i++) { + if (in[i] != 0) + b |= m; + m <<= 1; + if (m == 256){ + *out++ = b; + b = 0; m = 1; + } + } + if (m != 1) + *out++ = b; +} + +static void +pack1IR(UINT8* out, const UINT8* in, int pixels) +{ + int i, m, b; + /* bilevel, lsb first (black is 1) */ + b = 0; m = 1; + for (i = 0; i < pixels; i++) { + if (in[i] == 0) + b |= m; + m <<= 1; + if (m == 256){ + *out++ = b; + b = 0; m = 1; + } + } + if (m != 1) + *out++ = b; +} + +static void +pack1L(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* bilevel, stored as bytes */ + for (i = 0; i < pixels; i++) + out[i] = (in[i] != 0) ? 255 : 0; +} + +static void +packP4(UINT8* out, const UINT8* in, int pixels) +{ + while (pixels >= 2) { + *out++ = (in[0] << 4) | + (in[1] & 15); + in += 2; pixels -= 2; + } + + if (pixels) + out[0] = (in[0] << 4); +} + +static void +packP2(UINT8* out, const UINT8* in, int pixels) +{ + while (pixels >= 4) { + *out++ = (in[0] << 6) | + ((in[1] & 3) << 4) | + ((in[2] & 3) << 2) | + (in[3] & 3); + in += 4; pixels -= 4; + } + + switch (pixels) { + case 3: + out[0] = (in[0] << 6) | + ((in[1] & 3) << 4) | + ((in[2] & 3) << 2); + break; + case 2: + out[0] = (in[0] << 6) | + ((in[1] & 3) << 4); + case 1: + out[0] = (in[0] << 6); + } +} + +static void +packLA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LA, pixel interleaved */ + for (i = 0; i < pixels; i++) { + out[0] = in[R]; + out[1] = in[A]; + out += 2; in += 4; + } +} + +static void +packLAL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LA, line interleaved */ + for (i = 0; i < pixels; i++) { + out[i] = in[R]; + out[i+pixels] = in[A]; + in += 4; + } +} + +void +ImagingPackRGB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB triplets */ + for (i = 0; i < pixels; i++) { + out[0] = in[R]; + out[1] = in[G]; + out[2] = in[B]; + out += 3; in += 4; + } +} + +void +ImagingPackXRGB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* XRGB, triplets with left padding */ + for (i = 0; i < pixels; i++) { + out[0] = 0; + out[1] = in[R]; + out[2] = in[G]; + out[3] = in[B]; + out += 4; in += 4; + } +} + +void +ImagingPackBGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, reversed bytes */ + for (i = 0; i < pixels; i++) { + out[0] = in[B]; + out[1] = in[G]; + out[2] = in[R]; + out += 3; in += 4; + } +} + +void +ImagingPackBGRX(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* BGRX, reversed bytes with right padding */ + for (i = 0; i < pixels; i++) { + out[0] = in[B]; + out[1] = in[G]; + out[2] = in[R]; + out[3] = 0; + out += 4; in += 4; + } +} + +void +ImagingPackXBGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* XBGR, reversed bytes with left padding */ + for (i = 0; i < pixels; i++) { + out[0] = 0; + out[1] = in[B]; + out[2] = in[G]; + out[3] = in[R]; + out += 4; in += 4; + } +} + +void +ImagingPackBGRA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* BGRX, reversed bytes with right padding */ + for (i = 0; i < pixels; i++) { + out[0] = in[B]; + out[1] = in[G]; + out[2] = in[R]; + out[3] = in[A]; + out += 4; in += 4; + } +} + +void +ImagingPackABGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* XBGR, reversed bytes with left padding */ + for (i = 0; i < pixels; i++) { + out[0] = in[A]; + out[1] = in[B]; + out[2] = in[G]; + out[3] = in[R]; + out += 4; in += 4; + } +} + +static void +packRGBL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, line interleaved */ + for (i = 0; i < pixels; i++) { + out[i] = in[R]; + out[i+pixels] = in[G]; + out[i+pixels+pixels] = in[B]; + in += 4; + } +} + +static void +packRGBXL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGBX, line interleaved */ + for (i = 0; i < pixels; i++) { + out[i] = in[R]; + out[i+pixels] = in[G]; + out[i+pixels+pixels] = in[B]; + out[i+pixels+pixels+pixels] = in[X]; + in += 4; + } +} + +static void +packI16B(UINT8* out, const UINT8* in_, int pixels) +{ + int i; + INT32* in = (INT32*) in_; + UINT16 tmp_; + UINT8* tmp = (UINT8*) &tmp_; + for (i = 0; i < pixels; i++) { + if (in[0] <= 0) + tmp_ = 0; + else if (in[0] > 65535) + tmp_ = 65535; + else + tmp_ = in[0]; + C16B; + out += 2; in++; + } +} + +static void +packI32S(UINT8* out, const UINT8* in, int pixels) +{ + int i; + UINT8* tmp = (UINT8*) in; + for (i = 0; i < pixels; i++) { + C32L; + out += 4; tmp += 4; + } +} + +static void +copy1(UINT8* out, const UINT8* in, int pixels) +{ + /* L, P */ + memcpy(out, in, pixels); +} + +static void +copy2(UINT8* out, const UINT8* in, int pixels) +{ + /* I;16, etc */ + memcpy(out, in, pixels*2); +} + +static void +copy3(UINT8* out, const UINT8* in, int pixels) +{ + /* BGR;24, etc */ + memcpy(out, in, pixels*3); +} + +static void +copy4(UINT8* out, const UINT8* in, int pixels) +{ + /* RGBA, CMYK quadruples */ + memcpy(out, in, 4*pixels); +} + +static void +copy4I(UINT8* out, const UINT8* in, int pixels) +{ + /* RGBA, CMYK quadruples, inverted */ + int i; + for (i = 0; i < pixels*4; i++) + out[i] = ~in[i]; +} + +static void +band0(UINT8* out, const UINT8* in, int pixels) +{ + int i; + for (i = 0; i < pixels; i++, in += 4) + out[i] = in[0]; +} + +static void +band1(UINT8* out, const UINT8* in, int pixels) +{ + int i; + for (i = 0; i < pixels; i++, in += 4) + out[i] = in[1]; +} + +static void +band2(UINT8* out, const UINT8* in, int pixels) +{ + int i; + for (i = 0; i < pixels; i++, in += 4) + out[i] = in[2]; +} + +static void +band3(UINT8* out, const UINT8* in, int pixels) +{ + int i; + for (i = 0; i < pixels; i++, in += 4) + out[i] = in[3]; +} + +static struct { + const char* mode; + const char* rawmode; + int bits; + ImagingShuffler pack; +} packers[] = { + + /* bilevel */ + {"1", "1", 1, pack1}, + {"1", "1;I", 1, pack1I}, + {"1", "1;R", 1, pack1R}, + {"1", "1;IR", 1, pack1IR}, + {"1", "L", 8, pack1L}, + + /* greyscale */ + {"L", "L", 8, copy1}, + + /* greyscale w. alpha */ + {"LA", "LA", 16, packLA}, + {"LA", "LA;L", 16, packLAL}, + + /* palette */ + {"P", "P;1", 1, pack1}, + {"P", "P;2", 2, packP2}, + {"P", "P;4", 4, packP4}, + {"P", "P", 8, copy1}, + + /* palette w. alpha */ + {"PA", "PA", 16, packLA}, + {"PA", "PA;L", 16, packLAL}, + + /* true colour */ + {"RGB", "RGB", 24, ImagingPackRGB}, + {"RGB", "RGBX", 32, copy4}, + {"RGB", "XRGB", 32, ImagingPackXRGB}, + {"RGB", "BGR", 24, ImagingPackBGR}, + {"RGB", "BGRX", 32, ImagingPackBGRX}, + {"RGB", "XBGR", 32, ImagingPackXBGR}, + {"RGB", "RGB;L", 24, packRGBL}, + {"RGB", "R", 8, band0}, + {"RGB", "G", 8, band1}, + {"RGB", "B", 8, band2}, + + /* true colour w. alpha */ + {"RGBA", "RGBA", 32, copy4}, + {"RGBA", "RGBA;L", 32, packRGBXL}, + {"RGBA", "RGB", 24, ImagingPackRGB}, + {"RGBA", "BGR", 24, ImagingPackBGR}, + {"RGBA", "BGRA", 32, ImagingPackBGRA}, + {"RGBA", "ABGR", 32, ImagingPackABGR}, + {"RGBA", "R", 8, band0}, + {"RGBA", "G", 8, band1}, + {"RGBA", "B", 8, band2}, + {"RGBA", "A", 8, band3}, + + /* true colour w. padding */ + {"RGBX", "RGBX", 32, copy4}, + {"RGBX", "RGBX;L", 32, packRGBXL}, + {"RGBX", "RGB", 32, ImagingPackRGB}, + {"RGBX", "BGR", 32, ImagingPackBGR}, + {"RGBX", "BGRX", 32, ImagingPackBGRX}, + {"RGBX", "XBGR", 32, ImagingPackXBGR}, + {"RGBX", "R", 8, band0}, + {"RGBX", "G", 8, band1}, + {"RGBX", "B", 8, band2}, + {"RGBX", "X", 8, band3}, + + /* colour separation */ + {"CMYK", "CMYK", 32, copy4}, + {"CMYK", "CMYK;I", 32, copy4I}, + {"CMYK", "CMYK;L", 32, packRGBXL}, + {"CMYK", "C", 8, band0}, + {"CMYK", "M", 8, band1}, + {"CMYK", "Y", 8, band2}, + {"CMYK", "K", 8, band3}, + + /* video (YCbCr) */ + {"YCbCr", "YCbCr", 24, ImagingPackRGB}, + {"YCbCr", "YCbCr;L", 24, packRGBL}, + {"YCbCr", "YCbCrX", 32, copy4}, + {"YCbCr", "YCbCrK", 32, copy4}, + {"YCbCr", "Y", 8, band0}, + {"YCbCr", "Cb", 8, band1}, + {"YCbCr", "Cr", 8, band2}, + + /* integer */ + {"I", "I", 32, copy4}, + {"I", "I;16B", 16, packI16B}, + {"I", "I;32S", 32, packI32S}, + {"I", "I;32NS", 32, copy4}, + + /* floating point */ + {"F", "F", 32, copy4}, + {"F", "F;32F", 32, packI32S}, + {"F", "F;32NF", 32, copy4}, + + /* storage modes */ + {"I;16", "I;16", 16, copy2}, + {"I;16B", "I;16B", 16, copy2}, + {"I;16L", "I;16L", 16, copy2}, + {"BGR;15", "BGR;15", 16, copy2}, + {"BGR;16", "BGR;16", 16, copy2}, + {"BGR;24", "BGR;24", 24, copy3}, + + {NULL} /* sentinel */ +}; + + +ImagingShuffler +ImagingFindPacker(const char* mode, const char* rawmode, int* bits_out) +{ + int i; + + /* find a suitable pixel packer */ + for (i = 0; packers[i].rawmode; i++) + if (strcmp(packers[i].mode, mode) == 0 && + strcmp(packers[i].rawmode, rawmode) == 0) { + if (bits_out) + *bits_out = packers[i].bits; + return packers[i].pack; + } + return NULL; +} diff --git a/libImaging/PackDecode.c b/libImaging/PackDecode.c new file mode 100644 index 000000000..aea8f04e3 --- /dev/null +++ b/libImaging/PackDecode.c @@ -0,0 +1,92 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for PackBits image data. + * + * history: + * 96-04-19 fl Created + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +int +ImagingPackbitsDecode(Imaging im, ImagingCodecState state, + UINT8* buf, int bytes) +{ + UINT8 n; + UINT8* ptr; + int i; + + ptr = buf; + + for (;;) { + + if (bytes < 1) + return ptr - buf; + + if (ptr[0] & 0x80) { + + if (ptr[0] == 0x80) { + /* Nop */ + ptr++; bytes--; + continue; + } + + /* Run */ + if (bytes < 2) + return ptr - buf; + + for (n = 257 - ptr[0]; n > 0; n--) { + if (state->x >= state->bytes) { + /* state->errcode = IMAGING_CODEC_OVERRUN; */ + break; + } + state->buffer[state->x++] = ptr[1]; + } + + ptr += 2; bytes -= 2; + + } else { + + /* Literal */ + n = ptr[0]+2; + + if (bytes < n) + return ptr - buf; + + for (i = 1; i < n; i++) { + if (state->x >= state->bytes) { + /* state->errcode = IMAGING_CODEC_OVERRUN; */ + break; + } + state->buffer[state->x++] = ptr[i]; + } + + ptr += n; bytes -= n; + + } + + if (state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + } +} diff --git a/libImaging/Palette.c b/libImaging/Palette.c new file mode 100644 index 000000000..bd4f4f1d6 --- /dev/null +++ b/libImaging/Palette.c @@ -0,0 +1,316 @@ +/* + * The Python Imaging Library + * $Id$ + * + * imaging palette object + * + * history: + * 1996-05-05 fl Added to library + * 1996-05-27 fl Added colour mapping stuff + * 1997-05-12 fl Support RGBA palettes + * 2005-02-09 fl Removed grayscale entries from web palette + * + * Copyright (c) Secret Labs AB 1997-2005. All rights reserved. + * Copyright (c) Fredrik Lundh 1995-1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include + + +ImagingPalette +ImagingPaletteNew(const char* mode) +{ + /* Create a palette object */ + + int i; + ImagingPalette palette; + + if (strcmp(mode, "RGB") && strcmp(mode, "RGBA")) + return (ImagingPalette) ImagingError_ModeError(); + + palette = calloc(1, sizeof(struct ImagingPaletteInstance)); + if (!palette) + return (ImagingPalette) ImagingError_MemoryError(); + + strcpy(palette->mode, mode); + + /* Initialize to ramp */ + for (i = 0; i < 256; i++) { + palette->palette[i*4+0] = + palette->palette[i*4+1] = + palette->palette[i*4+2] = (UINT8) i; + palette->palette[i*4+3] = 255; /* opaque */ + } + + return palette; +} + +ImagingPalette +ImagingPaletteNewBrowser(void) +{ + /* Create a standard "browser" palette object */ + + int i, r, g, b; + ImagingPalette palette; + + palette = ImagingPaletteNew("RGB"); + if (!palette) + return NULL; + + /* Blank out unused entries */ + /* FIXME: Add 10-level windows palette here? */ + + for (i = 0; i < 10; i++) { + palette->palette[i*4+0] = + palette->palette[i*4+1] = + palette->palette[i*4+2] = 0; + } + + /* Simple 6x6x6 colour cube */ + + for (b = 0; b < 256; b += 51) + for (g = 0; g < 256; g += 51) + for (r = 0; r < 256; r += 51) { + palette->palette[i*4+0] = r; + palette->palette[i*4+1] = g; + palette->palette[i*4+2] = b; + i++; + } + + /* Blank out unused entries */ + /* FIXME: add 30-level greyscale wedge here? */ + + for (; i < 256; i++) { + palette->palette[i*4+0] = + palette->palette[i*4+1] = + palette->palette[i*4+2] = 0; + } + + return palette; +} + +ImagingPalette +ImagingPaletteDuplicate(ImagingPalette palette) +{ + /* Duplicate palette descriptor */ + + ImagingPalette new_palette; + + if (!palette) + return NULL; + + new_palette = malloc(sizeof(struct ImagingPaletteInstance)); + if (!new_palette) + return (ImagingPalette) ImagingError_MemoryError(); + + memcpy(new_palette, palette, sizeof(struct ImagingPaletteInstance)); + + /* Don't share the cache */ + new_palette->cache = NULL; + + return new_palette; +} + +void +ImagingPaletteDelete(ImagingPalette palette) +{ + /* Destroy palette object */ + + if (palette) { + if (palette->cache) + free(palette->cache); + free(palette); + } +} + + +/* -------------------------------------------------------------------- */ +/* Colour mapping */ +/* -------------------------------------------------------------------- */ + +/* This code is used to map RGB triplets to palette indices, using + a palette index cache. */ + +/* + * This implementation is loosely based on the corresponding code in + * the IJG JPEG library by Thomas G. Lane. Original algorithms by + * Paul Heckbert and Spencer W. Thomas. + * + * The IJG JPEG library is copyright (C) 1991-1995, Thomas G. Lane. */ + +#define DIST(a, b, s) (a - b) * (a - b) * s + +/* Colour weights (no scaling, for now) */ +#define RSCALE 1 +#define GSCALE 1 +#define BSCALE 1 + +/* Calculated scaled distances */ +#define RDIST(a, b) DIST(a, b, RSCALE*RSCALE) +#define GDIST(a, b) DIST(a, b, GSCALE*GSCALE) +#define BDIST(a, b) DIST(a, b, BSCALE*BSCALE) + +/* Incremental steps */ +#define RSTEP (4 * RSCALE) +#define GSTEP (4 * GSCALE) +#define BSTEP (4 * BSCALE) + +#define BOX 8 + +#define BOXVOLUME BOX*BOX*BOX + +void +ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) +{ + int i, j; + unsigned int dmin[256], dmax; + int r0, g0, b0; + int r1, g1, b1; + int rc, gc, bc; + unsigned int d[BOXVOLUME]; + UINT8 c[BOXVOLUME]; + + /* Get box boundaries for the given (r,g,b)-triplet. Each box + covers eight cache slots (32 colour values, that is). */ + + r0 = r & 0xe0; r1 = r0 + 0x1f; rc = (r0 + r1) / 2; + g0 = g & 0xe0; g1 = g0 + 0x1f; gc = (g0 + g1) / 2; + b0 = b & 0xe0; b1 = b0 + 0x1f; bc = (b0 + b1) / 2; + + /* Step 1 -- Select relevant palette entries (after Heckbert) */ + + /* For each palette entry, calculate the min and max distances to + * any position in the box given by the colour we're looking for. */ + + dmax = (unsigned int) ~0; + + for (i = 0; i < 256; i++) { + + int r, g, b; + unsigned int tmin, tmax; + + /* Find min and max distances to any point in the box */ + r = palette->palette[i*4+0]; + tmin = (r < r0) ? RDIST(r, r1) : (r > r1) ? RDIST(r, r0) : 0; + tmax = (r <= rc) ? RDIST(r, r1) : RDIST(r, r0); + + g = palette->palette[i*4+1]; + tmin += (g < g0) ? GDIST(g, g1) : (g > g1) ? GDIST(g, g0) : 0; + tmax += (g <= gc) ? GDIST(g, g1) : GDIST(g, g0); + + b = palette->palette[i*4+2]; + tmin += (b < b0) ? BDIST(b, b1) : (b > b1) ? BDIST(b, b0) : 0; + tmax += (b <= bc) ? BDIST(b, b1) : BDIST(b, b0); + + dmin[i] = tmin; + if (tmax < dmax) + dmax = tmax; /* keep the smallest max distance only */ + + } + + /* Step 2 -- Incrementally update cache slot (after Thomas) */ + + /* Find the box containing the nearest palette entry, and update + * all slots in that box. We only check boxes for which the min + * distance is less than or equal the smallest max distance */ + + for (i = 0; i < BOXVOLUME; i++) + d[i] = (unsigned int) ~0; + + for (i = 0; i < 256; i++) + + if (dmin[i] <= dmax) { + + int rd, gd, bd; + int ri, gi, bi; + int rx, gx, bx; + + ri = (r0 - palette->palette[i*4+0]) * RSCALE; + gi = (g0 - palette->palette[i*4+1]) * GSCALE; + bi = (b0 - palette->palette[i*4+2]) * BSCALE; + + rd = ri*ri + gi*gi + bi*bi; + + ri = ri * (2 * RSTEP) + RSTEP * RSTEP; + gi = gi * (2 * GSTEP) + GSTEP * GSTEP; + bi = bi * (2 * BSTEP) + BSTEP * BSTEP; + + rx = ri; + for (r = j = 0; r < BOX; r++) { + gd = rd; gx = gi; + for (g = 0; g < BOX; g++) { + bd = gd; bx = bi; + for (b = 0; b < BOX; b++) { + if ((unsigned int) bd < d[j]) { + d[j] = bd; + c[j] = (UINT8) i; + } + bd += bx; + bx += 2 * BSTEP * BSTEP; + j++; + } + gd += gx; + gx += 2 * GSTEP * GSTEP; + } + rd += rx; + rx += 2 * RSTEP * RSTEP; + } + } + + /* Step 3 -- Update cache */ + + /* The c array now contains the closest match for each + * cache slot in the box. Update the cache. */ + + j = 0; + for (r = r0; r < r1; r+=4) + for (g = g0; g < g1; g+=4) + for (b = b0; b < b1; b+=4) + ImagingPaletteCache(palette, r, g, b) = c[j++]; +} + + +int +ImagingPaletteCachePrepare(ImagingPalette palette) +{ + /* Add a colour cache to a palette */ + + int i; + int entries = 64*64*64; + + if (palette->cache == NULL) { + + /* The cache is 512k. It might be a good idea to break it + up into a pointer array (e.g. an 8-bit image?) */ + + palette->cache = (INT16*) malloc(entries * sizeof(INT16)); + if (!palette->cache) { + (void) ImagingError_MemoryError(); + return -1; + } + + /* Mark all entries as empty */ + for (i = 0; i < entries; i++) + palette->cache[i] = 0x100; + + } + + return 0; +} + + +void +ImagingPaletteCacheDelete(ImagingPalette palette) +{ + /* Release the colour cache, if any */ + + if (palette && palette->cache) { + free(palette->cache); + palette->cache = NULL; + } +} diff --git a/libImaging/Paste.c b/libImaging/Paste.c new file mode 100644 index 000000000..b89dd6404 --- /dev/null +++ b/libImaging/Paste.c @@ -0,0 +1,555 @@ +/* + * The Python Imaging Library + * $Id$ + * + * paste image on another image + * + * history: + * 96-03-27 fl Created + * 96-07-16 fl Support "1", "L" and "RGBA" masks + * 96-08-16 fl Merged with opaque paste + * 97-01-17 fl Faster blending, added support for RGBa images + * 97-08-27 fl Faster masking for 32-bit images + * 98-02-02 fl Fixed MULDIV255 macro for gcc + * 99-02-02 fl Added "RGBa" mask support + * 99-02-06 fl Rewritten. Added support for masked fill operations. + * 99-12-08 fl Fixed matte fill. + * + * Copyright (c) Fredrik Lundh 1996-97. + * Copyright (c) Secret Labs AB 1997-99. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +/* like (a * b + 127) / 255), but much faster on most platforms */ +#define MULDIV255NEW(a, b, tmp)\ + (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8)) + +#define MULDIV255OLD(a, b, tmp)\ + (((a) * (b) + 127) / 255) + +#define MULDIV255 MULDIV255NEW + +#define BLEND(mask, in1, in2, tmp1, tmp2)\ + (MULDIV255(in1, 255 - mask, tmp1) + MULDIV255(in2, mask, tmp2)) + +#define PREBLEND(mask, in1, in2, tmp1)\ + (MULDIV255(in1, 255 - mask, tmp1) + in2) + +static inline void +paste(Imaging imOut, Imaging imIn, int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* paste opaque region */ + + int y; + + dx *= pixelsize; + sx *= pixelsize; + + xsize *= pixelsize; + + for (y = 0; y < ysize; y++) + memcpy(imOut->image[y+dy]+dx, imIn->image[y+sy]+sx, xsize); +} + +static inline void +paste_mask_1(Imaging imOut, Imaging imIn, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* paste with mode "1" mask */ + + int x, y; + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* in = imIn->image8[y+sy]+sx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + if (*mask++) + *out = *in; + out++, in++; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + INT32* out = imOut->image32[y+dy]+dx; + INT32* in = imIn->image32[y+sy]+sx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + if (*mask++) + *out = *in; + out++, in++; + } + } + } +} + +static inline void +paste_mask_L(Imaging imOut, Imaging imIn, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* paste with mode "L" matte */ + + int x, y, i; + unsigned int tmp1, tmp2; + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* in = imIn->image8[y+sy]+sx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + *out = BLEND(*mask, *out, *in, tmp1, tmp2); + out++, in++, mask++; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; + UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = BLEND(*mask, *out, *in, tmp1, tmp2); + out++, in++; + } + mask++; + } + } + } +} + +static inline void +paste_mask_RGBA(Imaging imOut, Imaging imIn, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* paste with mode "RGBA" matte */ + + int x, y, i; + unsigned int tmp1, tmp2; + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* in = imIn->image8[y+sy]+sx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + for (x = 0; x < xsize; x++) { + *out = BLEND(*mask, *out, *in, tmp1, tmp2); + out++, in++, mask += 4; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; + UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = BLEND(*mask, *out, *in, tmp1, tmp2); + out++, in++; + } + mask += 4; + } + } + } +} + + +static inline void +paste_mask_RGBa(Imaging imOut, Imaging imIn, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* paste with mode "RGBa" matte */ + + int x, y, i; + unsigned int tmp1; + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* in = imIn->image8[y+sy]+sx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + for (x = 0; x < xsize; x++) { + *out = PREBLEND(*mask, *out, *in, tmp1); + out++, in++, mask += 4; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; + UINT8* in = (UINT8*) imIn->image[y+sy]+sx*pixelsize; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx*4+3; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = PREBLEND(*mask, *out, *in, tmp1); + out++, in++; + } + mask += 4; + } + } + } +} + +int +ImagingPaste(Imaging imOut, Imaging imIn, Imaging imMask, + int dx0, int dy0, int dx1, int dy1) +{ + int xsize, ysize; + int pixelsize; + int sx0, sy0; + ImagingSectionCookie cookie; + + if (!imOut || !imIn) { + (void) ImagingError_ModeError(); + return -1; + } + + pixelsize = imOut->pixelsize; + + xsize = dx1 - dx0; + ysize = dy1 - dy0; + + if (xsize != imIn->xsize || ysize != imIn->ysize || + pixelsize != imIn->pixelsize) { + (void) ImagingError_Mismatch(); + return -1; + } + + if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) { + (void) ImagingError_Mismatch(); + return -1; + } + + /* Determine which region to copy */ + sx0 = sy0 = 0; + if (dx0 < 0) + xsize += dx0, sx0 = -dx0, dx0 = 0; + if (dx0 + xsize > imOut->xsize) + xsize = imOut->xsize - dx0; + if (dy0 < 0) + ysize += dy0, sy0 = -dy0, dy0 = 0; + if (dy0 + ysize > imOut->ysize) + ysize = imOut->ysize - dy0; + + if (xsize <= 0 || ysize <= 0) + return 0; + + if (!imMask) { + ImagingSectionEnter(&cookie); + paste(imOut, imIn, dx0, dy0, sx0, sy0, xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "1") == 0) { + ImagingSectionEnter(&cookie); + paste_mask_1(imOut, imIn, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "L") == 0) { + ImagingSectionEnter(&cookie); + paste_mask_L(imOut, imIn, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "RGBA") == 0) { + ImagingSectionEnter(&cookie); + paste_mask_RGBA(imOut, imIn, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "RGBa") == 0) { + ImagingSectionEnter(&cookie); + paste_mask_RGBa(imOut, imIn, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else { + (void) ImagingError_ValueError("bad transparency mask"); + return -1; + } + + return 0; +} + +static inline void +fill(Imaging imOut, const void* ink_, int dx, int dy, + int xsize, int ysize, int pixelsize) +{ + /* fill opaque region */ + + int x, y; + UINT8 ink8 = 0; + INT32 ink32 = 0L; + + memcpy(&ink32, ink_, pixelsize); + memcpy(&ink8, ink_, sizeof(ink8)); + + if (imOut->image8 || ink32 == 0L) { + + dx *= pixelsize; + xsize *= pixelsize; + for (y = 0; y < ysize; y++) + memset(imOut->image[y+dy]+dx, ink8, xsize); + + } else { + + for (y = 0; y < ysize; y++) { + INT32* out = imOut->image32[y+dy]+dx; + for (x = 0; x < xsize; x++) + out[x] = ink32; + } + + } +} + +static inline void +fill_mask_1(Imaging imOut, const void* ink_, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* fill with mode "1" mask */ + + int x, y; + UINT8 ink8 = 0; + INT32 ink32 = 0L; + + memcpy(&ink32, ink_, pixelsize); + memcpy(&ink8, ink_, sizeof(ink8)); + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + if (*mask++) + *out = ink8; + out++; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + INT32* out = imOut->image32[y+dy]+dx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + if (*mask++) + *out = ink32; + out++; + } + } + } +} + +static inline void +fill_mask_L(Imaging imOut, const UINT8* ink, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* fill with mode "L" matte */ + + int x, y, i; + unsigned int tmp1, tmp2; + + if (imOut->image8) { + + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* mask = imMask->image8[y+sy]+sx; + for (x = 0; x < xsize; x++) { + *out = BLEND(*mask, *out, ink[0], tmp1, tmp2); + out++, mask++; + } + } + + } else { + + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx*pixelsize; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = BLEND(*mask, *out, ink[i], tmp1, tmp2); + out++; + } + mask++; + } + } + } +} + +static inline void +fill_mask_RGBA(Imaging imOut, const UINT8* ink, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* fill with mode "RGBA" matte */ + + int x, y, i; + unsigned int tmp1, tmp2; + + if (imOut->image8) { + + sx = sx*4+3; + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + *out = BLEND(*mask, *out, ink[0], tmp1, tmp2); + out++, mask += 4; + } + } + + } else { + + dx *= pixelsize; + sx = sx*4 + 3; + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = BLEND(*mask, *out, ink[i], tmp1, tmp2); + out++; + } + mask += 4; + } + } + } +} + +static inline void +fill_mask_RGBa(Imaging imOut, const UINT8* ink, Imaging imMask, + int dx, int dy, int sx, int sy, + int xsize, int ysize, int pixelsize) +{ + /* fill with mode "RGBa" matte */ + + int x, y, i; + unsigned int tmp1; + + if (imOut->image8) { + + sx = sx*4 + 3; + for (y = 0; y < ysize; y++) { + UINT8* out = imOut->image8[y+dy]+dx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + *out = PREBLEND(*mask, *out, ink[0], tmp1); + out++, mask += 4; + } + } + + } else { + + dx *= pixelsize; + sx = sx*4 + 3; + for (y = 0; y < ysize; y++) { + UINT8* out = (UINT8*) imOut->image[y+dy]+dx; + UINT8* mask = (UINT8*) imMask->image[y+sy]+sx; + for (x = 0; x < xsize; x++) { + for (i = 0; i < pixelsize; i++) { + *out = PREBLEND(*mask, *out, ink[i], tmp1); + out++; + } + mask += 4; + } + } + } +} + +int +ImagingFill2(Imaging imOut, const void* ink, Imaging imMask, + int dx0, int dy0, int dx1, int dy1) +{ + ImagingSectionCookie cookie; + int xsize, ysize; + int pixelsize; + int sx0, sy0; + + if (!imOut || !ink) { + (void) ImagingError_ModeError(); + return -1; + } + + pixelsize = imOut->pixelsize; + + xsize = dx1 - dx0; + ysize = dy1 - dy0; + + if (imMask && (xsize != imMask->xsize || ysize != imMask->ysize)) { + (void) ImagingError_Mismatch(); + return -1; + } + + /* Determine which region to fill */ + sx0 = sy0 = 0; + if (dx0 < 0) + xsize += dx0, sx0 = -dx0, dx0 = 0; + if (dx0 + xsize > imOut->xsize) + xsize = imOut->xsize - dx0; + if (dy0 < 0) + ysize += dy0, sy0 = -dy0, dy0 = 0; + if (dy0 + ysize > imOut->ysize) + ysize = imOut->ysize - dy0; + + if (xsize <= 0 || ysize <= 0) + return 0; + + if (!imMask) { + ImagingSectionEnter(&cookie); + fill(imOut, ink, dx0, dy0, xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "1") == 0) { + ImagingSectionEnter(&cookie); + fill_mask_1(imOut, ink, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "L") == 0) { + ImagingSectionEnter(&cookie); + fill_mask_L(imOut, ink, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "RGBA") == 0) { + ImagingSectionEnter(&cookie); + fill_mask_RGBA(imOut, ink, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else if (strcmp(imMask->mode, "RGBa") == 0) { + ImagingSectionEnter(&cookie); + fill_mask_RGBa(imOut, ink, imMask, dx0, dy0, sx0, sy0, + xsize, ysize, pixelsize); + ImagingSectionLeave(&cookie); + + } else { + (void) ImagingError_ValueError("bad transparency mask"); + return -1; + } + + return 0; +} diff --git a/libImaging/PcdDecode.c b/libImaging/PcdDecode.c new file mode 100644 index 000000000..b6898e301 --- /dev/null +++ b/libImaging/PcdDecode.c @@ -0,0 +1,78 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for uncompressed PCD image data. + * + * history: + * 96-05-10 fl Created + * 96-05-18 fl New tables + * 97-01-25 fl Use PhotoYCC unpacker + * + * notes: + * This driver supports uncompressed PCD modes only + * (resolutions up to 768x512). + * + * Copyright (c) Fredrik Lundh 1996-97. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingPcdDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + int x; + int chunk; + UINT8* out; + UINT8* ptr; + + ptr = buf; + + chunk = 3 * state->xsize; + + for (;;) { + + /* We need data for two full lines before we can do anything */ + if (bytes < chunk) + return ptr - buf; + + /* Unpack first line */ + out = state->buffer; + for (x = 0; x < state->xsize; x++) { + out[0] = ptr[x]; + out[1] = ptr[(x+4*state->xsize)/2]; + out[2] = ptr[(x+5*state->xsize)/2]; + out += 4; + } + + state->shuffle((UINT8*) im->image[state->y], + state->buffer, state->xsize); + + if (++state->y >= state->ysize) + return -1; /* This can hardly happen */ + + /* Unpack second line */ + out = state->buffer; + for (x = 0; x < state->xsize; x++) { + out[0] = ptr[x+state->xsize]; + out[1] = ptr[(x+4*state->xsize)/2]; + out[2] = ptr[(x+5*state->xsize)/2]; + out += 4; + } + + state->shuffle((UINT8*) im->image[state->y], + state->buffer, state->xsize); + + if (++state->y >= state->ysize) + return -1; + + ptr += chunk; + bytes -= chunk; + + } +} diff --git a/libImaging/PcxDecode.c b/libImaging/PcxDecode.c new file mode 100644 index 000000000..ab82b23ad --- /dev/null +++ b/libImaging/PcxDecode.c @@ -0,0 +1,75 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for PCX image data. + * + * history: + * 95-09-14 fl Created + * + * Copyright (c) Fredrik Lundh 1995. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +int +ImagingPcxDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8 n; + UINT8* ptr; + + ptr = buf; + + for (;;) { + + if (bytes < 1) + return ptr - buf; + + if ((*ptr & 0xC0) == 0xC0) { + + /* Run */ + if (bytes < 2) + return ptr - buf; + + n = ptr[0] & 0x3F; + + while (n > 0) { + if (state->x >= state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + break; + } + state->buffer[state->x++] = ptr[1]; + n--; + } + + ptr += 2; bytes -= 2; + + } else { + + /* Literal */ + state->buffer[state->x++] = ptr[0]; + ptr++; bytes--; + + } + + if (state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + } +} diff --git a/libImaging/PcxEncode.c b/libImaging/PcxEncode.c new file mode 100644 index 000000000..87d599463 --- /dev/null +++ b/libImaging/PcxEncode.c @@ -0,0 +1,148 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * encoder for PCX data + * + * history: + * 99-02-07 fl created + * + * Copyright (c) Fredrik Lundh 1999. + * Copyright (c) Secret Labs AB 1999. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +enum { INIT, FETCH, ENCODE }; + +/* we're reusing "ystep" to store the last value */ +#define LAST ystep + +int +ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* ptr; + int this; + + ptr = buf; + + if (!state->state) { + + /* sanity check */ + if (state->xsize <= 0 || state->ysize <= 0) { + state->errcode = IMAGING_CODEC_END; + return 0; + } + + state->bytes = (state->xsize*state->bits + 7) / 8; + state->state = FETCH; + + } + + for (;;) + + switch (state->state) { + case FETCH: + + /* get a line of data */ + if (state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_END; + return ptr - buf; + } + + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); + + state->y++; + + state->count = 1; + state->LAST = state->buffer[0]; + + state->x = 1; + + state->state = ENCODE; + /* fall through */ + + case ENCODE: + + /* compress this line */ + + /* when we arrive here, "count" contains the number of + bytes having the value of "LAST" that we've already + seen */ + + while (state->x < state->bytes) { + + if (state->count == 63) { + + /* this run is full; flush it */ + if (bytes < 2) + return ptr - buf; + *ptr++ = 0xff; + *ptr++ = state->LAST; + bytes -= 2; + + state->count = 0; + + } + + this = state->buffer[state->x]; + + if (this == state->LAST) { + + /* extend the current run */ + state->x++; + state->count++; + + } else { + + /* start a new run */ + if (state->count == 1 && (state->LAST < 0xc0)) { + if (bytes < 1) + return ptr - buf; + *ptr++ = state->LAST; + bytes--; + } else { + if (state->count > 0) { + if (bytes < 2) + return ptr - buf; + *ptr++ = 0xc0 | state->count; + *ptr++ = state->LAST; + bytes -= 2; + } + } + + state->LAST = this; + state->count = 1; + + state->x++; + + } + } + + /* end of line; flush the current run */ + if (state->count == 1 && (state->LAST < 0xc0)) { + if (bytes < 1) + return ptr - buf; + *ptr++ = state->LAST; + bytes--; + } else { + if (state->count > 0) { + if (bytes < 2) + return ptr - buf; + *ptr++ = 0xc0 | state->count; + *ptr++ = state->LAST; + bytes -= 2; + } + } + + /* read next line */ + state->state = FETCH; + break; + + } +} diff --git a/libImaging/Point.c b/libImaging/Point.c new file mode 100644 index 000000000..2593dca57 --- /dev/null +++ b/libImaging/Point.c @@ -0,0 +1,263 @@ +/* + * The Python Imaging Library + * $Id$ + * + * point (pixel) translation + * + * history: + * 1995-11-27 fl Created + * 1996-03-31 fl Fixed colour support + * 1996-08-13 fl Support 8-bit to "1" thresholding + * 1997-05-31 fl Added floating point transform + * 1998-07-02 fl Added integer point transform + * 1998-07-17 fl Support L to anything lookup + * 2004-12-18 fl Refactored; added I to L lookup + * + * Copyright (c) 1997-2004 by Secret Labs AB. + * Copyright (c) 1995-2004 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +typedef struct { + const void* table; +} im_point_context; + +static void +im_point_8_8(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 8-bit source, 8-bit destination */ + UINT8* table = (UINT8*) context->table; + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = imIn->image8[y]; + UINT8* out = imOut->image8[y]; + for (x = 0; x < imIn->xsize; x++) + out[x] = table[in[x]]; + } +} + +static void +im_point_2x8_2x8(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 2x8-bit source, 2x8-bit destination */ + UINT8* table = (UINT8*) context->table; + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn->xsize; x++) { + out[0] = table[in[0]]; + out[3] = table[in[3]+256]; + in += 4; out += 4; + } + } +} + +static void +im_point_3x8_3x8(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 3x8-bit source, 3x8-bit destination */ + UINT8* table = (UINT8*) context->table; + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn->xsize; x++) { + out[0] = table[in[0]]; + out[1] = table[in[1]+256]; + out[2] = table[in[2]+512]; + in += 4; out += 4; + } + } +} + +static void +im_point_4x8_4x8(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 4x8-bit source, 4x8-bit destination */ + UINT8* table = (UINT8*) context->table; + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = (UINT8*) imIn->image[y]; + UINT8* out = (UINT8*) imOut->image[y]; + for (x = 0; x < imIn->xsize; x++) { + out[0] = table[in[0]]; + out[1] = table[in[1]+256]; + out[2] = table[in[2]+512]; + out[3] = table[in[3]+768]; + in += 4; out += 4; + } + } +} + +static void +im_point_8_32(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 8-bit source, 32-bit destination */ + INT32* table = (INT32*) context->table; + for (y = 0; y < imIn->ysize; y++) { + UINT8* in = imIn->image8[y]; + INT32* out = imOut->image32[y]; + for (x = 0; x < imIn->xsize; x++) + out[x] = table[in[x]]; + } +} + +static void +im_point_32_8(Imaging imOut, Imaging imIn, im_point_context* context) +{ + int x, y; + /* 32-bit source, 8-bit destination */ + UINT8* table = (UINT8*) context->table; + for (y = 0; y < imIn->ysize; y++) { + INT32* in = imIn->image32[y]; + UINT8* out = imOut->image8[y]; + for (x = 0; x < imIn->xsize; x++) { + int v = in[x]; + if (v < 0) + v = 0; + else if (v > 65535) + v = 65535; + out[x] = table[v]; + } + } +} + +Imaging +ImagingPoint(Imaging imIn, const char* mode, const void* table) +{ + /* lookup table transform */ + + ImagingSectionCookie cookie; + Imaging imOut; + im_point_context context; + void (*point)(Imaging imIn, Imaging imOut, im_point_context* context); + + if (!imIn) + return (Imaging) ImagingError_ModeError(); + + if (!mode) + mode = imIn->mode; + + if (imIn->type != IMAGING_TYPE_UINT8) { + if (imIn->type != IMAGING_TYPE_INT32 || strcmp(mode, "L") != 0) + goto mode_mismatch; + } else if (!imIn->image8 && strcmp(imIn->mode, mode) != 0) + goto mode_mismatch; + + imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + + /* find appropriate handler */ + if (imIn->type == IMAGING_TYPE_UINT8) { + if (imIn->bands == imOut->bands && imIn->type == imOut->type) { + switch (imIn->bands) { + case 1: + point = im_point_8_8; + break; + case 2: + point = im_point_2x8_2x8; + break; + case 3: + point = im_point_3x8_3x8; + break; + case 4: + point = im_point_4x8_4x8; + break; + default: + /* this cannot really happen */ + point = im_point_8_8; + break; + } + } else + point = im_point_8_32; + } else + point = im_point_32_8; + + ImagingCopyInfo(imOut, imIn); + + ImagingSectionEnter(&cookie); + + context.table = table; + point(imOut, imIn, &context); + + ImagingSectionLeave(&cookie); + + return imOut; + + mode_mismatch: + return (Imaging) ImagingError_ValueError( + "point operation not supported for this mode" + ); +} + + +Imaging +ImagingPointTransform(Imaging imIn, double scale, double offset) +{ + /* scale/offset transform */ + + ImagingSectionCookie cookie; + Imaging imOut; + int x, y; + + if (!imIn || (strcmp(imIn->mode, "I") != 0 && + strcmp(imIn->mode, "I;16") != 0 && + strcmp(imIn->mode, "F") != 0)) + return (Imaging) ImagingError_ModeError(); + + imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + + ImagingCopyInfo(imOut, imIn); + + switch (imIn->type) { + case IMAGING_TYPE_INT32: + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + INT32* in = imIn->image32[y]; + INT32* out = imOut->image32[y]; + /* FIXME: add clipping? */ + for (x = 0; x < imIn->xsize; x++) + out[x] = in[x] * scale + offset; + } + ImagingSectionLeave(&cookie); + break; + case IMAGING_TYPE_FLOAT32: + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + FLOAT32* in = (FLOAT32*) imIn->image32[y]; + FLOAT32* out = (FLOAT32*) imOut->image32[y]; + for (x = 0; x < imIn->xsize; x++) + out[x] = in[x] * scale + offset; + } + ImagingSectionLeave(&cookie); + break; + case IMAGING_TYPE_SPECIAL: + if (strcmp(imIn->mode,"I;16") == 0) { + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + UINT16* in = (UINT16 *)imIn->image[y]; + UINT16* out = (UINT16 *)imOut->image[y]; + /* FIXME: add clipping? */ + for (x = 0; x < imIn->xsize; x++) + out[x] = in[x] * scale + offset; + } + ImagingSectionLeave(&cookie); + break; + } + /* FALL THROUGH */ + default: + ImagingDelete(imOut); + return (Imaging) ImagingError_ValueError("internal error"); + } + + return imOut; +} diff --git a/libImaging/Quant.c b/libImaging/Quant.c new file mode 100644 index 000000000..1b76a6a0f --- /dev/null +++ b/libImaging/Quant.c @@ -0,0 +1,1610 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * history: + * 1998-09-10 tjs Contributed + * 1998-12-29 fl Added to PIL 1.0b1 + * 2004-02-21 fl Fixed bogus free() on quantization error + * 2005-02-07 fl Limit number of colors to 256 + * + * Written by Toby J Sargeant . + * + * Copyright (c) 1998 by Toby J Sargeant + * Copyright (c) 1998-2004 by Secret Labs AB. All rights reserved. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +#include +#include +#include +#include + +#include "Quant.h" + +#include "QuantDefines.h" +#include "QuantHash.h" +#include "QuantHeap.h" + +#define NO_OUTPUT + +typedef struct { + unsigned long scale; +} PixelHashData; + +typedef struct _PixelList { + struct _PixelList *next[3],*prev[3]; + Pixel p; + unsigned int flag:1; + int count; +} PixelList; + +typedef struct _BoxNode { + struct _BoxNode *l,*r; + PixelList *head[3],*tail[3]; + int axis; + int volume; + unsigned long pixelCount; +} BoxNode; + +#define _SQR(x) ((x)*(x)) +#define _DISTSQR(p1,p2) \ + _SQR((int)((p1)->c.r)-(int)((p2)->c.r))+ \ + _SQR((int)((p1)->c.g)-(int)((p2)->c.g))+ \ + _SQR((int)((p1)->c.b)-(int)((p2)->c.b)) + +#define MAX_HASH_ENTRIES 65536 + +#define PIXEL_HASH(r,g,b) \ + (((unsigned int)(r) )*463 ^ \ + ((unsigned int)(g)<< 8)*10069 ^ \ + ((unsigned int)(b)<<16)*64997) + +#define PIXEL_UNSCALE(p,q,s) \ + ((q)->c.r=(p)->c.r<<(s)), \ + ((q)->c.g=(p)->c.g<<(s)), \ + ((q)->c.b=(p)->c.b<<(s)) + +#define PIXEL_SCALE(p,q,s)\ + ((q)->c.r=(p)->c.r>>(s)), \ + ((q)->c.g=(p)->c.g>>(s)), \ + ((q)->c.b=(p)->c.b>>(s)) + +static unsigned long +unshifted_pixel_hash(const HashTable h, const void *p) +{ + Pixel *pixel=(Pixel *)&p; + unsigned long hash=PIXEL_HASH(pixel->c.r, + pixel->c.g, + pixel->c.b); + return hash; +} + +static int +unshifted_pixel_cmp(const HashTable h, const void *a, const void *b) +{ + Pixel *pixel1=(Pixel *)&a; + Pixel *pixel2=(Pixel *)&b; + if (pixel1->c.r==pixel2->c.r) { + if (pixel1->c.g==pixel2->c.g) { + if (pixel1->c.b==pixel2->c.b) { + return 0; + } else { + return (int)(pixel1->c.b)-(int)(pixel2->c.b); + } + } else { + return (int)(pixel1->c.g)-(int)(pixel2->c.g); + } + } else { + return (int)(pixel1->c.r)-(int)(pixel2->c.r); + } +} + +static unsigned long +pixel_hash(const HashTable h,const void *p) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + Pixel *pixel=(Pixel *)&p; + unsigned long hash=PIXEL_HASH(pixel->c.r>>d->scale, + pixel->c.g>>d->scale, + pixel->c.b>>d->scale); + return hash; +} + +static int +pixel_cmp(const HashTable h,const void *a,const void *b) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + Pixel *pixel1=(Pixel *)&a; + Pixel *pixel2=(Pixel *)&b; + unsigned long A,B; + A=PIXEL_HASH(pixel1->c.r>>d->scale, + pixel1->c.g>>d->scale, + pixel1->c.b>>d->scale); + B=PIXEL_HASH(pixel2->c.r>>d->scale, + pixel2->c.g>>d->scale, + pixel2->c.b>>d->scale); + return (A==B)?0:((Ascale=0; + timer=timer3=clock(); + for (i=0;iMAX_HASH_ENTRIES) { + d->scale++; +#ifndef NO_OUTPUT + printf ("rehashing - new scale: %d\n",(int)d->scale); +#endif + timer2=clock(); + hashtable_rehash_compute(hash,rehash_collide); + timer2=clock()-timer2; +#ifndef NO_OUTPUT + printf ("rehash took %f sec\n",timer2/(double)CLOCKS_PER_SEC); +#endif + timer+=timer2; + } + } +#ifndef NO_OUTPUT + printf ("inserts took %f sec\n",(clock()-timer)/(double)CLOCKS_PER_SEC); +#endif +#ifndef NO_OUTPUT + printf ("total %f sec\n",(clock()-timer3)/(double)CLOCKS_PER_SEC); +#endif + return hash; +} + +static void +destroy_pixel_hash(HashTable hash) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(hash); + if (d) free(d); + hashtable_free(hash); +} + + +/* 1. hash quantized pixels. */ +/* 2. create R,G,B lists of sorted quantized pixels. */ +/* 3. median cut. */ +/* 4. build hash table from median cut boxes. */ +/* 5. for each pixel, compute entry in hash table, and hence median cut box. */ +/* 6. compute median cut box pixel averages. */ +/* 7. map each pixel to nearest average. */ + +static int +compute_box_volume(BoxNode *b) +{ + unsigned char rl,rh,gl,gh,bl,bh; + if (b->volume>=0) return b->volume; + if (!b->head[0]) { + b->volume=0; + } else { + rh=b->head[0]->p.c.r; + rl=b->tail[0]->p.c.r; + gh=b->head[1]->p.c.g; + gl=b->tail[1]->p.c.g; + bh=b->head[2]->p.c.b; + bl=b->tail[2]->p.c.b; + b->volume=(rh-rl+1)*(gh-gl+1)*(bh-bl+1); + } + return b->volume; +} + +static void +hash_to_list(HashTable h, const void *key, const void *val, void *u) +{ + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + PixelList **pl=(PixelList **)u; + PixelList *p; + Pixel *pixel=(Pixel *)&key; + int i; + Pixel q; + int count=*(int *)&val; + + PIXEL_SCALE(pixel,&q,d->scale); + + p=malloc(sizeof(PixelList)); + if (!p) return; + + p->flag=0; + p->p=q; + p->count=count; + for (i=0;i<3;i++) { + p->next[i]=pl[i]; + p->prev[i]=NULL; + if (pl[i]) pl[i]->prev[i]=p; + pl[i]=p; + } +} + +static PixelList * +mergesort_pixels(PixelList *head, int i) +{ + PixelList *c,*t,*a,*b,*p; + if (!head||!head->next[i]) { + if (head) { + head->next[i]=NULL; + head->prev[i]=NULL; + } + return head; + } + for (c=t=head;c&&t;c=c->next[i],t=(t->next[i])?t->next[i]->next[i]:NULL); + if (c) { + if (c->prev[i]) c->prev[i]->next[i]=NULL; + c->prev[i]=NULL; + } + a=mergesort_pixels(head,i); + b=mergesort_pixels(c,i); + head=NULL; + p=NULL; + while (a&&b) { + if (a->p.a.v[i]>b->p.a.v[i]) { + c=a; + a=a->next[i]; + } else { + c=b; + b=b->next[i]; + } + c->prev[i]=p; + c->next[i]=NULL; + if (p) p->next[i]=c; + p=c; + if (!head) head=c; + } + if (a) { + c->next[i]=a; + a->prev[i]=c; + } else if (b) { + c->next[i]=b; + b->prev[i]=c; + } + return head; +} + +#if defined(TEST_MERGESORT) || defined(TEST_SORTED) +static int +test_sorted(PixelList *pl[3]) +{ + int i,n,l; + PixelList *t; + + for(i=0;i<3;i++) { + n=0; + l=256; + for (t=pl[i];t;t=t->next[i]) { + if (lp.a.v[i]) return 0; + l=t->p.a.v[i]; + } + } + return 1; +} +#endif + +static int +box_heap_cmp(const Heap h, const void *A, const void *B) +{ + BoxNode *a=(BoxNode *)A; + BoxNode *b=(BoxNode *)B; + return (int)a->pixelCount-(int)b->pixelCount; +} + +#define LUMINANCE(p) (77*(p)->c.r+150*(p)->c.g+29*(p)->c.b) + +static int +splitlists(PixelList *h[3], + PixelList *t[3], + PixelList *nh[2][3], + PixelList *nt[2][3], + unsigned long nCount[2], + int axis, + unsigned long pixelCount) +{ + unsigned long left; + + PixelList *l,*r,*c,*n; + int i; + int nRight,nLeft; + int splitColourVal; + +#ifdef TEST_SPLIT + { + PixelList *_prevTest,*_nextTest; + int _i,_nextCount[3],_prevCount[3]; + for (_i=0;_i<3;_i++) { + for (_nextCount[_i]=0,_nextTest=h[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++); + for (_prevCount[_i]=0,_prevTest=t[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++); + if (_nextTest!=t[_i]) { + printf ("next-list of axis %d does not end at tail\n",_i); + exit(1); + } + if (_prevTest!=h[_i]) { + printf ("prev-list of axis %d does not end at head\n",_i); + exit(1); + } + for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]); + for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]); + if (_nextTest!=h[_i]) { + printf ("next-list of axis %d does not loop back to head\n",_i); + exit(1); + } + if (_prevTest!=t[_i]) { + printf ("prev-list of axis %d does not loop back to tail\n",_i); + exit(1); + } + } + for (_i=1;_i<3;_i++) { + if (_prevCount[_i]!=_prevCount[_i-1] || + _nextCount[_i]!=_nextCount[_i-1] || + _prevCount[_i]!=_nextCount[_i]) { + printf ("{%d %d %d} {%d %d %d}\n", + _prevCount[0], + _prevCount[1], + _prevCount[2], + _nextCount[0], + _nextCount[1], + _nextCount[2]); + exit(1); + } + } + } +#endif + nCount[0]=nCount[1]=0; + nLeft=nRight=0; + for (left=0,c=h[axis];c;) { + left=left+c->count; + nCount[0]+=c->count; + c->flag=0; + nLeft++; + c=c->next[axis]; + if (left*2>pixelCount) { + break; + } + } + if (c) { + splitColourVal=c->prev[axis]->p.a.v[axis]; + for (;c;c=c->next[axis]) { + if (splitColourVal!=c->p.a.v[axis]) { + break; + } + c->flag=0; + nLeft++; + nCount[0]+=c->count; + } + } + for (;c;c=c->next[axis]) { + c->flag=1; + nRight++; + nCount[1]+=c->count; + } + if (!nRight) { + for (c=t[axis],splitColourVal=t[axis]->p.a.v[axis];c;c=c->prev[axis]) { + if (splitColourVal!=c->p.a.v[axis]) { + break; + } + c->flag=1; + nRight++; + nLeft--; + nCount[0]-=c->count; + nCount[1]+=c->count; + } + } +#ifndef NO_OUTPUT + if (!nLeft) { + for (c=h[axis];c;c=c->next[axis]) { + printf ("[%d %d %d]\n",c->p.c.r,c->p.c.g,c->p.c.b); + } + printf ("warning... trivial split\n"); + } +#endif + + for (i=0;i<3;i++) { + l=r=NULL; + nh[0][i]=nt[0][i]=NULL; + nh[1][i]=nt[1][i]=NULL; + for (c=h[i];c;c=n) { + n=c->next[i]; + if (c->flag) { /* move pixel to right list*/ + if (r) r->next[i]=c; else nh[1][i]=c; + c->prev[i]=r; + r=c; + } else { /* move pixel to left list */ + if (l) l->next[i]=c; else nh[0][i]=c; + c->prev[i]=l; + l=c; + } + } + if (l) l->next[i]=NULL; + if (r) r->next[i]=NULL; + nt[0][i]=l; + nt[1][i]=r; + } + return 1; +} + +static int +split(BoxNode *node) +{ + unsigned char rl,rh,gl,gh,bl,bh; + int f[3]; + int best,axis; + int i; + PixelList *heads[2][3]; + PixelList *tails[2][3]; + unsigned long newCounts[2]; + BoxNode *left,*right; + + rh=node->head[0]->p.c.r; + rl=node->tail[0]->p.c.r; + gh=node->head[1]->p.c.g; + gl=node->tail[1]->p.c.g; + bh=node->head[2]->p.c.b; + bl=node->tail[2]->p.c.b; +#ifdef TEST_SPLIT + printf ("splitting node [%d %d %d] [%d %d %d] ",rl,gl,bl,rh,gh,bh); +#endif + f[0]=(rh-rl)*77; + f[1]=(gh-gl)*150; + f[2]=(bh-bl)*29; + + best=f[0]; + axis=0; + for (i=1;i<3;i++) { + if (besttail[_i]->next[_i]) { + printf ("tail is not tail\n"); + printf ("node->tail[%d]->next[%d]=%p\n",_i,_i,node->tail[_i]->next[_i]); + } + if (node->head[_i]->prev[_i]) { + printf ("head is not head\n"); + printf ("node->head[%d]->prev[%d]=%p\n",_i,_i,node->head[_i]->prev[_i]); + } + } + + for (_i=0;_i<3;_i++) { + for (_nextCount[_i]=0,_nextTest=node->head[_i];_nextTest&&_nextTest->next[_i];_nextTest=_nextTest->next[_i],_nextCount[_i]++); + for (_prevCount[_i]=0,_prevTest=node->tail[_i];_prevTest&&_prevTest->prev[_i];_prevTest=_prevTest->prev[_i],_prevCount[_i]++); + if (_nextTest!=node->tail[_i]) { + printf ("next-list of axis %d does not end at tail\n",_i); + } + if (_prevTest!=node->head[_i]) { + printf ("prev-list of axis %d does not end at head\n",_i); + } + for (;_nextTest&&_nextTest->prev[_i];_nextTest=_nextTest->prev[_i]); + for (;_prevTest&&_prevTest->next[_i];_prevTest=_prevTest->next[_i]); + if (_nextTest!=node->head[_i]) { + printf ("next-list of axis %d does not loop back to head\n",_i); + } + if (_prevTest!=node->tail[_i]) { + printf ("prev-list of axis %d does not loop back to tail\n",_i); + } + } + for (_i=1;_i<3;_i++) { + if (_prevCount[_i]!=_prevCount[_i-1] || + _nextCount[_i]!=_nextCount[_i-1] || + _prevCount[_i]!=_nextCount[_i]) { + printf ("{%d %d %d} {%d %d %d}\n", + _prevCount[0], + _prevCount[1], + _prevCount[2], + _nextCount[0], + _nextCount[1], + _nextCount[2]); + } + } + } +#endif + node->axis=axis; + if (!splitlists(node->head, + node->tail, + heads, + tails, + newCounts, + axis, + node->pixelCount)) { +#ifndef NO_OUTPUT + printf ("list split failed.\n"); +#endif + return 0; + } +#ifdef TEST_SPLIT + if (!test_sorted(heads[0])) { + printf ("bug in split"); + exit(1); + } + if (!test_sorted(heads[1])) { + printf ("bug in split"); + exit(1); + } +#endif + left=malloc(sizeof(BoxNode)); + right=malloc(sizeof(BoxNode)); + if (!left||!right) { + return 0; + } + for(i=0;i<3;i++) { + left->head[i]=heads[0][i]; + left->tail[i]=tails[0][i]; + right->head[i]=heads[1][i]; + right->tail[i]=tails[1][i]; + node->head[i]=NULL; + node->tail[i]=NULL; + } +#ifdef TEST_SPLIT + if (left->head[0]) { + rh=left->head[0]->p.c.r; + rl=left->tail[0]->p.c.r; + gh=left->head[1]->p.c.g; + gl=left->tail[1]->p.c.g; + bh=left->head[2]->p.c.b; + bl=left->tail[2]->p.c.b; + printf (" left node [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh); + } + if (right->head[0]) { + rh=right->head[0]->p.c.r; + rl=right->tail[0]->p.c.r; + gh=right->head[1]->p.c.g; + gl=right->tail[1]->p.c.g; + bh=right->head[2]->p.c.b; + bl=right->tail[2]->p.c.b; + printf (" right node [%3d %3d %3d] [%3d %3d %3d]\n",rl,gl,bl,rh,gh,bh); + } +#endif + left->l=left->r=NULL; + right->l=right->r=NULL; + left->axis=right->axis=-1; + left->volume=right->volume=-1; + left->pixelCount=newCounts[0]; + right->pixelCount=newCounts[1]; + node->l=left; + node->r=right; + return 1; +} + +static BoxNode * +median_cut(PixelList *hl[3], + unsigned long imPixelCount, + int nPixels) +{ + PixelList *tl[3]; + int i; + BoxNode *root; + Heap h; + BoxNode *thisNode; + + h=ImagingQuantHeapNew(box_heap_cmp); + root=malloc(sizeof(BoxNode)); + if (!root) { ImagingQuantHeapFree(h); return NULL; } + for(i=0;i<3;i++) { + for (tl[i]=hl[i];tl[i]&&tl[i]->next[i];tl[i]=tl[i]->next[i]); + root->head[i]=hl[i]; + root->tail[i]=tl[i]; + } + root->l=root->r=NULL; + root->axis=-1; + root->volume=-1; + root->pixelCount=imPixelCount; + + ImagingQuantHeapAdd(h,(void *)root); + while (--nPixels) { + do { + if (!ImagingQuantHeapRemove(h,(void **)&thisNode)) { + goto done; + } + } while (compute_box_volume(thisNode)==1); + if (!split(thisNode)) { +#ifndef NO_OUTPUT + printf ("Oops, split failed...\n"); +#endif + exit (1); + } + ImagingQuantHeapAdd(h,(void *)(thisNode->l)); + ImagingQuantHeapAdd(h,(void *)(thisNode->r)); + } +done: + ImagingQuantHeapFree(h); + return root; +} + +static void +free_box_tree(BoxNode *n) +{ + PixelList *p,*pp; + if (n->l) free_box_tree(n->l); + if (n->r) free_box_tree(n->r); + for (p=n->head[0];p;p=pp) { + pp=p->next[0]; + free(p); + } + free(n); +} + +#ifdef TEST_SPLIT_INTEGRITY +static int +checkContained(BoxNode *n,Pixel *pp) +{ + if (n->l&&n->r) { + return checkContained(n->l,pp)+checkContained(n->r,pp); + } + if (n->l||n->r) { +#ifndef NO_OUTPUT + printf ("box tree is dead\n"); +#endif + return 0; + } + if ( + pp->c.r<=n->head[0]->p.c.r && + pp->c.r>=n->tail[0]->p.c.r && + pp->c.g<=n->head[1]->p.c.g && + pp->c.g>=n->tail[1]->p.c.g && + pp->c.b<=n->head[2]->p.c.b && + pp->c.b>=n->tail[2]->p.c.b) { + return 1; + } + return 0; +} +#endif + +static int +annotate_hash_table(BoxNode *n,HashTable h,unsigned long *box) +{ + PixelList *p; + PixelHashData *d=(PixelHashData *)hashtable_get_user_data(h); + Pixel q; + if (n->l&&n->r) { + return annotate_hash_table(n->l,h,box) && annotate_hash_table(n->r,h,box); + } + if (n->l||n->r) { +#ifndef NO_OUTPUT + printf ("box tree is dead\n"); +#endif + return 0; + } + for (p=n->head[0];p;p=p->next[0]) { + PIXEL_UNSCALE(&(p->p),&q,d->scale); + if (!hashtable_insert(h,(void *)q.v,(void *)*box)) { +#ifndef NO_OUTPUT + printf ("hashtable insert failed\n"); +#endif + return 0; + } + } + if (n->head[0]) (*box)++; + return 1; +} + +static int +_sort_ulong_ptr_keys(const void *a, const void *b) +{ + unsigned long A=**(unsigned long **)a; + unsigned long B=**(unsigned long **)b; + return (A==B)?0:((A*(skRow[k]));k--) { + skRow[k]=skRow[k-1]; + } + if (k!=j) skRow[k]=skElt; + } + } + return 1; +} + +static int +build_distance_tables(unsigned long *avgDist, + unsigned long **avgDistSortKey, + Pixel *p, + unsigned long nEntries) +{ + unsigned long i,j; + + for (i=0;i1) { + printf ("pixel in two boxes\n"); + for(i=0;i<3;i++) free (avg[i]); + free(count); + return 0; + } +#endif + if (!hashtable_lookup(medianBoxHash,(void *)pixelData[i].v,(void **)&paletteEntry)) { +#ifndef NO_OUTPUT + printf ("pixel lookup failed\n"); +#endif + for(i=0;i<3;i++) free (avg[i]); + free(count); + return 0; + } + if (paletteEntry>=nPaletteEntries) { +#ifndef NO_OUTPUT + printf ("panic - paletteEntry>=nPaletteEntries (%d>=%d)\n",(int)paletteEntry,(int)nPaletteEntries); +#endif + for(i=0;i<3;i++) free (avg[i]); + free(count); + return 0; + } + avg[0][paletteEntry]+=pixelData[i].c.r; + avg[1][paletteEntry]+=pixelData[i].c.g; + avg[2][paletteEntry]+=pixelData[i].c.b; + count[paletteEntry]++; + } + p=malloc(sizeof(Pixel)*nPaletteEntries); + if (!p) { + for(i=0;i<3;i++) free (avg[i]); + free(count); + return 0; + } + for (i=0;i=nPaletteEntries) { +#ifndef NO_OUTPUT + printf ("scream\n"); +#endif + return 0; + } + avg[0][qp[i]]+=pixelData[i].c.r; + avg[1][qp[i]]+=pixelData[i].c.g; + avg[2][qp[i]]+=pixelData[i].c.b; + count[qp[i]]++; + } + for (i=0;i + { + unsigned long bestmatch,bestdist,dist; + HashTable h2; + printf ("nearest neighbour search (full search)..."); fflush(stdout); timer=clock(); + h2=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); + for (i=0;inew),pixel); + if (data->secondPixel || newDistdata->furthestDistance) { + data->furthestDistance=oldDist; + data->furthest.v=pixel->v; + } +} + +int +quantize2(Pixel *pixelData, + unsigned long nPixels, + unsigned long nQuantPixels, + Pixel **palette, + unsigned long *paletteLength, + unsigned long **quantizedPixels, + int kmeans) +{ + HashTable h; + unsigned long i; + unsigned long mean[3]; + Pixel *p; + DistanceData data; + + unsigned long *qp; + unsigned long *avgDist; + unsigned long **avgDistSortKey; + + p=malloc(sizeof(Pixel)*nQuantPixels); + if (!p) return 0; + mean[0]=mean[1]=mean[2]=0; + h=hashtable_new(unshifted_pixel_hash,unshifted_pixel_cmp); + for (i=0;i 256) + /* FIXME: for colors > 256, consider returning an RGB image + instead (see @PIL205) */ + return (Imaging) ImagingError_ValueError("bad number of colors"); + + if (strcmp(im->mode, "L") != 0 && strcmp(im->mode, "P") != 0 && + strcmp(im->mode, "RGB")) + return ImagingError_ModeError(); + + p = malloc(sizeof(Pixel) * im->xsize * im->ysize); + if (!p) + return ImagingError_MemoryError(); + + /* collect statistics */ + + /* FIXME: maybe we could load the hash tables directly from the + image data? */ + + if (!strcmp(im->mode, "L")) { + /* greyscale */ + + /* FIXME: converting a "L" image to "P" with 256 colors + should be done by a simple copy... */ + + for (i = y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++, i++) + p[i].c.r = p[i].c.g = p[i].c.b = im->image8[y][x]; + + } else if (!strcmp(im->mode, "P")) { + /* palette */ + + pp = im->palette->palette; + + for (i = y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++, i++) { + v = im->image8[y][x]; + p[i].c.r = pp[v*4+0]; + p[i].c.g = pp[v*4+1]; + p[i].c.b = pp[v*4+2]; + } + + } else if (!strcmp(im->mode, "RGB")) { + /* true colour */ + + for (i = y = 0; y < im->ysize; y++) + for (x = 0; x < im->xsize; x++, i++) + p[i].v = im->image32[y][x]; + + } else { + free(p); + return (Imaging) ImagingError_ValueError("internal error"); + } + + switch (mode) { + case 0: + /* median cut */ + result = quantize( + p, + im->xsize*im->ysize, + colors, + &palette, + &paletteLength, + &newData, + kmeans + ); + break; + case 1: + /* maximum coverage */ + result = quantize2( + p, + im->xsize*im->ysize, + colors, + &palette, + &paletteLength, + &newData, + kmeans + ); + break; + default: + result = 0; + break; + } + + free(p); + + if (result) { + + imOut = ImagingNew("P", im->xsize, im->ysize); + + for (i = y = 0; y < im->ysize; y++) + for (x=0; x < im->xsize; x++) + imOut->image8[y][x] = (unsigned char) newData[i++]; + + free(newData); + + pp = imOut->palette->palette; + + for (i = j = 0; i < (int) paletteLength; i++) { + *pp++ = palette[i].c.r; + *pp++ = palette[i].c.g; + *pp++ = palette[i].c.b; + *pp++ = 255; + } + for (; i < 256; i++) { + *pp++ = 0; + *pp++ = 0; + *pp++ = 0; + *pp++ = 255; + } + + free(palette); + + return imOut; + + } else { + + return (Imaging) ImagingError_ValueError("quantization error"); + + } +} diff --git a/libImaging/Quant.h b/libImaging/Quant.h new file mode 100644 index 000000000..0de485a1a --- /dev/null +++ b/libImaging/Quant.h @@ -0,0 +1,40 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * Written by Toby J Sargeant . + * + * See the README file for information on usage and redistribution. + */ + +#ifndef __QUANT_H__ +#define __QUANT_H__ + +typedef union { + struct { + unsigned char r,g,b,a; + } c; + struct { + unsigned char v[4]; + } a; + unsigned long v; +} Pixel; + +int quantize(Pixel *, + unsigned long, + unsigned long, + Pixel **, + unsigned long *, + unsigned long **, + int); + +int quantize2(Pixel *, + unsigned long, + unsigned long, + Pixel **, + unsigned long *, + unsigned long **, + int); +#endif diff --git a/libImaging/QuantDefines.h b/libImaging/QuantDefines.h new file mode 100644 index 000000000..5b080104e --- /dev/null +++ b/libImaging/QuantDefines.h @@ -0,0 +1,25 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * Written by Toby J Sargeant . + * + * See the README file for information on usage and redistribution. + */ + +#ifndef __DEFINES_H__ +#define __DEFINES_H__ + +#if 0 + +void *newMalloc(size_t,const char *,const char *,int); +void newFree(void *,const char *,const char *,int); +void print_malloc_stats(); +#define malloc(x) newMalloc(x,__FILE__,__FUNCTION__,__LINE__) +#define free(x) newFree(x,__FILE__,__FUNCTION__,__LINE__) + +#endif + +#endif diff --git a/libImaging/QuantHash.c b/libImaging/QuantHash.c new file mode 100644 index 000000000..e6438402d --- /dev/null +++ b/libImaging/QuantHash.c @@ -0,0 +1,477 @@ +/* + * The Python Imaging Library + * $Id$ + * + * hash tables used by the image quantizer + * + * history: + * 98-09-10 tjs Contributed + * 98-12-29 fl Added to PIL 1.0b1 + * + * Written by Toby J Sargeant . + * + * Copyright (c) 1998 by Toby J Sargeant + * Copyright (c) 1998 by Secret Labs AB + * + * See the README file for information on usage and redistribution. + */ + +#include +#include +#include +#include + +#include "QuantHash.h" +#include "QuantDefines.h" + +typedef struct _IntHashNode { + struct _IntHashNode *next; + void *key,*value; +} IntHashNode; + +typedef struct _IntHashTable { + IntHashNode **table; + unsigned long length; + unsigned long count; + HashFunc hashFunc; + HashCmpFunc cmpFunc; + DestroyFunc keyDestroyFunc; + DestroyFunc valDestroyFunc; + void *userData; +} IntHashTable; + +#define MIN_LENGTH 11 +#define RESIZE_FACTOR 3 + +static int _hashtable_insert_node(IntHashTable *,IntHashNode *,int,int,CollisionFunc); +#if 0 +static int _hashtable_test(IntHashTable *); +#endif + +HashTable hashtable_new(HashFunc hf,HashCmpFunc cf) { + IntHashTable *h; + h=malloc(sizeof(IntHashTable)); + if (!h) { return NULL; } + h->hashFunc=hf; + h->cmpFunc=cf; + h->keyDestroyFunc=NULL; + h->valDestroyFunc=NULL; + h->length=MIN_LENGTH; + h->count=0; + h->userData=NULL; + h->table=malloc(sizeof(IntHashNode *)*h->length); + if (!h->table) { free(h); return NULL; } + memset (h->table,0,sizeof(IntHashNode *)*h->length); + return (HashTable)h; +} + +static void _hashtable_destroy(HashTable H,const void *key,const void *val,void *u) { + IntHashTable *h=(IntHashTable *)H; + if (h->keyDestroyFunc&&key) { + h->keyDestroyFunc((HashTable)h,(void *)key); + } + if (h->valDestroyFunc&&val) { + h->valDestroyFunc((HashTable)h,(void *)val); + } +} + +static unsigned long _findPrime(unsigned long start,int dir) { + static int unit[]={0,1,0,1,0,0,0,1,0,1,0,1,0,1,0,0}; + unsigned long t; + while (start>1) { + if (!unit[start&0x0f]) { + start+=dir; + continue; + } + for (t=2;t=sqrt((double)start)) { + break; + } + start+=dir; + } + return start; +} + +static void _hashtable_rehash(IntHashTable *h, + CollisionFunc cf, + unsigned long newSize) { + IntHashNode **oldTable=h->table; + unsigned long i; + IntHashNode *n,*nn; + unsigned long oldSize; + oldSize=h->length; + h->table=malloc(sizeof(IntHashNode *)*newSize); + if (!h->table) { + h->table=oldTable; + return; + } + h->length=newSize; + h->count=0; + memset (h->table,0,sizeof(IntHashNode *)*h->length); + for (i=0;inext; + _hashtable_insert_node(h,n,0,0,cf); + } + } + free(oldTable); +} + +static void _hashtable_resize(IntHashTable *h) { + unsigned long newSize; + unsigned long oldSize; + oldSize=h->length; + newSize=oldSize; + if (h->count*RESIZE_FACTORlength) { + newSize=_findPrime(h->length/2-1,-1); + } else if (h->length*RESIZE_FACTORcount) { + newSize=_findPrime(h->length*2+1,+1); + } + if (newSizelength;i++) { + for (n=h->table[i];n&&n->next;n=n->next) { + j=h->cmpFunc((HashTable)h,n->key,n->next->key); + printf ("%c",j?(j<0?'-':'+'):'='); + } + printf ("\n"); + } + return 0; +} +#endif + +static int _hashtable_insert_node(IntHashTable *h,IntHashNode *node,int resize,int update,CollisionFunc cf) { + unsigned long hash=h->hashFunc((HashTable)h,node->key)%h->length; + IntHashNode **n,*nv; + int i; + + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { + nv=*n; + i=h->cmpFunc((HashTable)h,nv->key,node->key); + if (!i) { + if (cf) { + nv->key=node->key; + cf((HashTable)h,&(nv->key),&(nv->value),node->key,node->value); + free(node); + return 1; + } else { + if (h->valDestroyFunc) { + h->valDestroyFunc((HashTable)h,nv->value); + } + if (h->keyDestroyFunc) { + h->keyDestroyFunc((HashTable)h,nv->key); + } + nv->key=node->key; + nv->value=node->value; + free(node); + return 1; + } + } else if (i>0) { + break; + } + } + if (!update) { + node->next=*n; + *n=node; + h->count++; + if (resize) _hashtable_resize(h); + return 1; + } else { + return 0; + } +} + +static int _hashtable_insert(IntHashTable *h,void *key,void *val,int resize,int update) { + IntHashNode **n,*nv; + IntHashNode *t; + int i; + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { + nv=*n; + i=h->cmpFunc((HashTable)h,nv->key,key); + if (!i) { + if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,nv->value); } + nv->value=val; + return 1; + } else if (i>0) { + break; + } + } + if (!update) { + t=malloc(sizeof(IntHashNode)); + if (!t) return 0; + t->next=*n; + *n=t; + t->key=key; + t->value=val; + h->count++; + if (resize) _hashtable_resize(h); + return 1; + } else { + return 0; + } +} + +static int _hashtable_lookup_or_insert(IntHashTable *h,void *key,void **retVal,void *newVal,int resize) { + IntHashNode **n,*nv; + IntHashNode *t; + int i; + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { + nv=*n; + i=h->cmpFunc((HashTable)h,nv->key,key); + if (!i) { + *retVal=nv->value; + return 1; + } else if (i>0) { + break; + } + } + t=malloc(sizeof(IntHashNode)); + if (!t) return 0; + t->next=*n; + *n=t; + t->key=key; + t->value=newVal; + *retVal=newVal; + h->count++; + if (resize) _hashtable_resize(h); + return 1; +} + +int hashtable_insert_or_update_computed(HashTable H, + void *key, + ComputeFunc newFunc, + ComputeFunc existsFunc) { + IntHashTable *h=(IntHashTable *)H; + IntHashNode **n,*nv; + IntHashNode *t; + int i; + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + + for (n=&(h->table[hash]);*n;n=&((*n)->next)) { + nv=*n; + i=h->cmpFunc((HashTable)h,nv->key,key); + if (!i) { + void *old=nv->value; + if (existsFunc) { + existsFunc(H,nv->key,&(nv->value)); + if (nv->value!=old) { + if (h->valDestroyFunc) { + h->valDestroyFunc((HashTable)h,old); + } + } + } else { + return 0; + } + return 1; + } else if (i>0) { + break; + } + } + t=malloc(sizeof(IntHashNode)); + if (!t) return 0; + t->key=key; + t->next=*n; + *n=t; + if (newFunc) { + newFunc(H,t->key,&(t->value)); + } else { + free(t); + return 0; + } + h->count++; + _hashtable_resize(h); + return 1; +} + +int hashtable_update(HashTable H,void *key,void *val) { + IntHashTable *h=(IntHashTable *)H; + return _hashtable_insert(h,key,val,1,0); +} + +int hashtable_insert(HashTable H,void *key,void *val) { + IntHashTable *h=(IntHashTable *)H; + return _hashtable_insert(h,key,val,1,0); +} + +void hashtable_foreach_update(HashTable H,IteratorUpdateFunc i,void *u) { + IntHashTable *h=(IntHashTable *)H; + IntHashNode *n; + unsigned long x; + + if (h->table) { + for (x=0;xlength;x++) { + for (n=h->table[x];n;n=n->next) { + i((HashTable)h,n->key,(void **)&(n->value),u); + } + } + } +} + +void hashtable_foreach(HashTable H,IteratorFunc i,void *u) { + IntHashTable *h=(IntHashTable *)H; + IntHashNode *n; + unsigned long x; + + if (h->table) { + for (x=0;xlength;x++) { + for (n=h->table[x];n;n=n->next) { + i((HashTable)h,n->key,n->value,u); + } + } + } +} + +void hashtable_free(HashTable H) { + IntHashTable *h=(IntHashTable *)H; + IntHashNode *n,*nn; + unsigned long i; + + if (h->table) { + if (h->keyDestroyFunc || h->keyDestroyFunc) { + hashtable_foreach(H,_hashtable_destroy,NULL); + } + for (i=0;ilength;i++) { + for (n=h->table[i];n;n=nn) { + nn=n->next; + free(n); + } + } + free(h->table); + } + free(h); +} + +DestroyFunc hashtable_set_value_destroy_func(HashTable H,DestroyFunc d) { + IntHashTable *h=(IntHashTable *)H; + DestroyFunc r=h->valDestroyFunc; + h->valDestroyFunc=d; + return r; +} + +DestroyFunc hashtable_set_key_destroy_func(HashTable H,DestroyFunc d) { + IntHashTable *h=(IntHashTable *)H; + DestroyFunc r=h->keyDestroyFunc; + h->keyDestroyFunc=d; + return r; +} + +static int _hashtable_remove(IntHashTable *h, + const void *key, + void **keyRet, + void **valRet, + int resize) { + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + IntHashNode *n,*p; + int i; + + for (p=NULL,n=h->table[hash];n;p=n,n=n->next) { + i=h->cmpFunc((HashTable)h,n->key,key); + if (!i) { + if (p) p=n->next; else h->table[hash]=n->next; + *keyRet=n->key; + *valRet=n->value; + free(n); + h->count++; + return 1; + } else if (i>0) { + break; + } + } + return 0; +} + +static int _hashtable_delete(IntHashTable *h,const void *key,int resize) { + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + IntHashNode *n,*p; + int i; + + for (p=NULL,n=h->table[hash];n;p=n,n=n->next) { + i=h->cmpFunc((HashTable)h,n->key,key); + if (!i) { + if (p) p=n->next; else h->table[hash]=n->next; + if (h->valDestroyFunc) { h->valDestroyFunc((HashTable)h,n->value); } + if (h->keyDestroyFunc) { h->keyDestroyFunc((HashTable)h,n->key); } + free(n); + h->count++; + return 1; + } else if (i>0) { + break; + } + } + return 0; +} + +int hashtable_remove(HashTable H,const void *key,void **keyRet,void **valRet) { + IntHashTable *h=(IntHashTable *)H; + return _hashtable_remove(h,key,keyRet,valRet,1); +} + +int hashtable_delete(HashTable H,const void *key) { + IntHashTable *h=(IntHashTable *)H; + return _hashtable_delete(h,key,1); +} + +void hashtable_rehash_compute(HashTable H,CollisionFunc cf) { + IntHashTable *h=(IntHashTable *)H; + _hashtable_rehash(h,cf,h->length); +} + +void hashtable_rehash(HashTable H) { + IntHashTable *h=(IntHashTable *)H; + _hashtable_rehash(h,NULL,h->length); +} + +int hashtable_lookup_or_insert(HashTable H,void *key,void **valp,void *val) { + IntHashTable *h=(IntHashTable *)H; + return _hashtable_lookup_or_insert(h,key,valp,val,1); +} + +int hashtable_lookup(const HashTable H,const void *key,void **valp) { + IntHashTable *h=(IntHashTable *)H; + unsigned long hash=h->hashFunc((HashTable)h,key)%h->length; + IntHashNode *n; + int i; + + for (n=h->table[hash];n;n=n->next) { + i=h->cmpFunc((HashTable)h,n->key,key); + if (!i) { + *valp=n->value; + return 1; + } else if (i>0) { + break; + } + } + return 0; +} + +unsigned long hashtable_get_count(const HashTable H) { + IntHashTable *h=(IntHashTable *)H; + return h->count; +} + +void *hashtable_get_user_data(const HashTable H) { + IntHashTable *h=(IntHashTable *)H; + return h->userData; +} + +void *hashtable_set_user_data(HashTable H,void *data) { + IntHashTable *h=(IntHashTable *)H; + void *r=h->userData; + h->userData=data; + return r; +} diff --git a/libImaging/QuantHash.h b/libImaging/QuantHash.h new file mode 100644 index 000000000..b98b56eaa --- /dev/null +++ b/libImaging/QuantHash.h @@ -0,0 +1,36 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * Written by Toby J Sargeant . + * + * See the README file for information on usage and redistribution. + */ + +#ifndef __HASH_H__ +#define __HASH_H__ + +#include "QuantTypes.h" + +HashTable hashtable_new(HashFunc,HashCmpFunc); +void hashtable_free(HashTable); +void hashtable_foreach(HashTable,IteratorFunc,void *); +void hashtable_foreach_update(HashTable,IteratorUpdateFunc,void *); +int hashtable_insert(HashTable,void *,void *); +int hashtable_update(HashTable,void *,void *); +int hashtable_lookup(const HashTable,const void *,void **); +int hashtable_lookup_or_insert(HashTable,void *,void **,void *); +int hashtable_insert_or_update_computed(HashTable,void *,ComputeFunc,ComputeFunc); +int hashtable_delete(HashTable,const void *); +int hashtable_remove(HashTable,const void *,void **,void **); +void *hashtable_set_user_data(HashTable,void *); +void *hashtable_get_user_data(const HashTable); +DestroyFunc hashtable_set_key_destroy_func(HashTable,DestroyFunc); +DestroyFunc hashtable_set_value_destroy_func(HashTable,DestroyFunc); +unsigned long hashtable_get_count(const HashTable); +void hashtable_rehash(HashTable); +void hashtable_rehash_compute(HashTable,CollisionFunc); + +#endif diff --git a/libImaging/QuantHeap.c b/libImaging/QuantHeap.c new file mode 100644 index 000000000..9332a5cd7 --- /dev/null +++ b/libImaging/QuantHeap.c @@ -0,0 +1,150 @@ +/* + * The Python Imaging Library + * $Id$ + * + * heap data type used by the image quantizer + * + * history: + * 98-09-10 tjs Contributed + * 98-12-29 fl Added to PIL 1.0b1 + * + * Written by Toby J Sargeant . + * + * Copyright (c) 1998 by Toby J Sargeant + * Copyright (c) 1998 by Secret Labs AB + * + * See the README file for information on usage and redistribution. + */ + +#include +#include +#include +#include + +#include "QuantHash.h" +#include "QuantDefines.h" + +typedef struct { + void **heap; + int heapsize; + int heapcount; + HeapCmpFunc cf; +} IntHeap; + +#define INITIAL_SIZE 256 + +#define DEBUG + +#ifdef DEBUG +static int _heap_test(Heap); +#endif + +void ImagingQuantHeapFree(Heap H) { + IntHeap *h=(IntHeap *)H; + free(h->heap); + free(h); +} + +static int _heap_grow(IntHeap *h,int newsize) { + void *newheap; + if (!newsize) newsize=h->heapsize<<1; + if (newsizeheapsize) return 0; + newheap=malloc(sizeof(void *)*newsize); + if (!newheap) return 0; + memcpy(newheap,h->heap,sizeof(void *)*h->heapsize); + free(h->heap); + h->heap=newheap; + h->heapsize=newsize; + return 1; +} + +#ifdef DEBUG +static int _heap_test(Heap H) { + IntHeap *h=(IntHeap *)H; + int k; + for (k=1;k*2<=h->heapcount;k++) { + if (h->cf(H,h->heap[k],h->heap[k*2])<0) { + printf ("heap is bad\n"); + return 0; + } + if (k*2+1<=h->heapcount && h->cf(H,h->heap[k],h->heap[k*2+1])<0) { + printf ("heap is bad\n"); + return 0; + } + } + return 1; +} +#endif + +int ImagingQuantHeapRemove(Heap H,void **r) { + IntHeap *h=(IntHeap *)H; + int k,l; + void *v; + + if (!h->heapcount) { + return 0; + } + *r=h->heap[1]; + v=h->heap[h->heapcount--]; + for (k=1;k*2<=h->heapcount;k=l) { + l=k*2; + if (lheapcount) { + if (h->cf(H,h->heap[l],h->heap[l+1])<0) { + l++; + } + } + if (h->cf(H,v,h->heap[l])>0) { + break; + } + h->heap[k]=h->heap[l]; + } + h->heap[k]=v; +#ifdef DEBUG + if (!_heap_test(H)) { printf ("oops - heap_remove messed up the heap\n"); exit(1); } +#endif + return 1; +} + +int ImagingQuantHeapAdd(Heap H,void *val) { + IntHeap *h=(IntHeap *)H; + int k; + if (h->heapcount==h->heapsize-1) { + _heap_grow(h,0); + } + k=++h->heapcount; + while (k!=1) { + if (h->cf(H,val,h->heap[k/2])<=0) { + break; + } + h->heap[k]=h->heap[k/2]; + k>>=1; + } + h->heap[k]=val; +#ifdef DEBUG + if (!_heap_test(H)) { printf ("oops - heap_add messed up the heap\n"); exit(1); } +#endif + return 1; +} + +int ImagingQuantHeapTop(Heap H,void **r) { + IntHeap *h=(IntHeap *)H; + if (!h->heapcount) { + return 0; + } + *r=h->heap[1]; + return 1; +} + +Heap *ImagingQuantHeapNew(HeapCmpFunc cf) { + IntHeap *h; + + h=malloc(sizeof(IntHeap)); + if (!h) return NULL; + h->heapsize=INITIAL_SIZE; + h->heap=malloc(sizeof(void *)*h->heapsize); + if (!h->heap) { free(h); return NULL; } + h->heapcount=0; + h->cf=cf; + return (Heap)h; +} + diff --git a/libImaging/QuantHeap.h b/libImaging/QuantHeap.h new file mode 100644 index 000000000..5a213c42a --- /dev/null +++ b/libImaging/QuantHeap.h @@ -0,0 +1,23 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * Written by Toby J Sargeant . + * + * See the README file for information on usage and redistribution. + */ + +#ifndef __HEAP_H__ +#define __HEAP_H__ + +#include "QuantTypes.h" + +void ImagingQuantHeapFree(Heap); +int ImagingQuantHeapRemove(Heap,void **); +int ImagingQuantHeapAdd(Heap,void *); +int ImagingQuantHeapTop(Heap,void **); +Heap *ImagingQuantHeapNew(HeapCmpFunc); + +#endif diff --git a/libImaging/QuantTypes.h b/libImaging/QuantTypes.h new file mode 100644 index 000000000..308b51c13 --- /dev/null +++ b/libImaging/QuantTypes.h @@ -0,0 +1,28 @@ +/* + * The Python Imaging Library + * $Id$ + * + * image quantizer + * + * Written by Toby J Sargeant . + * + * See the README file for information on usage and redistribution. + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef void *HashTable; +typedef void *Heap; + +typedef unsigned long (*HashFunc)(const HashTable,const void *); +typedef int (*HashCmpFunc)(const HashTable,const void *,const void *); +typedef void (*IteratorFunc)(const HashTable,const void *,const void *,void *); +typedef void (*IteratorUpdateFunc)(const HashTable,const void *,void **,void *); +typedef void (*DestroyFunc)(const HashTable,void *); +typedef void (*ComputeFunc)(const HashTable,const void *,void **); +typedef void (*CollisionFunc)(const HashTable,void **,void **,void *,void *); + +typedef int (*HeapCmpFunc)(const Heap,const void *,const void *); + +#endif diff --git a/libImaging/RankFilter.c b/libImaging/RankFilter.c new file mode 100644 index 000000000..84090f751 --- /dev/null +++ b/libImaging/RankFilter.c @@ -0,0 +1,105 @@ +/* + * The Python Imaging Library + * $Id$ + * + * min, max, median filters + * + * history: + * 2002-06-08 fl Created + * + * Copyright (c) Secret Labs AB 2002. All rights reserved. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + +/* Fast rank algorithm (due to Wirth), based on public domain code + by Nicolas Devillard, available at http://ndevilla.free.fr */ + +#define SWAP(type,a,b) { register type t=(a);(a)=(b);(b)=t; } + +#define MakeRankFunction(type)\ +static type Rank##type(type a[], int n, int k)\ +{\ + register int i, j, l, m;\ + register type x;\ + l = 0; m = n-1;\ + while (l < m) {\ + x = a[k];\ + i = l;\ + j = m;\ + do {\ + while (a[i] < x) i++;\ + while (x < a[j]) j--;\ + if (i <= j) {\ + SWAP(type, a[i], a[j]);\ + i++; j--;\ + }\ + } while (i <= j);\ + if (j < k) l = i;\ + if (k < i) m = j;\ + }\ + return a[k];\ +} + +MakeRankFunction(UINT8) +MakeRankFunction(INT32) +MakeRankFunction(FLOAT32) + +Imaging +ImagingRankFilter(Imaging im, int size, int rank) +{ + Imaging imOut = NULL; + int x, y; + int i, margin, size2; + + if (!im || im->bands != 1 || im->type == IMAGING_TYPE_SPECIAL) + return (Imaging) ImagingError_ModeError(); + + if (!(size & 1)) + return (Imaging) ImagingError_ValueError("bad filter size"); + + size2 = size * size; + margin = (size-1) / 2; + + if (rank < 0 || rank >= size2) + return (Imaging) ImagingError_ValueError("bad rank value"); + + imOut = ImagingNew(im->mode, im->xsize - 2*margin, im->ysize - 2*margin); + if (!imOut) + return NULL; + +#define RANK_BODY(type) do {\ + type* buf = malloc(size2 * sizeof(type));\ + if (!buf)\ + goto nomemory;\ + for (y = 0; y < imOut->ysize; y++)\ + for (x = 0; x < imOut->xsize; x++) {\ + for (i = 0; i < size; i++)\ + memcpy(buf + i*size, &IMAGING_PIXEL_##type(im, x, y+i),\ + size * sizeof(type));\ + IMAGING_PIXEL_##type(imOut, x, y) = Rank##type(buf, size2, rank);\ + }\ +} while (0) + + if (im->image8) + RANK_BODY(UINT8); + else if (im->type == IMAGING_TYPE_INT32) + RANK_BODY(INT32); + else if (im->type == IMAGING_TYPE_FLOAT32) + RANK_BODY(FLOAT32); + else { + /* safety net (we shouldn't end up here) */ + ImagingDelete(imOut); + return (Imaging) ImagingError_ModeError(); + } + + ImagingCopyInfo(imOut, im); + + return imOut; + +nomemory: + ImagingDelete(imOut); + return (Imaging) ImagingError_MemoryError(); +} diff --git a/libImaging/Raw.h b/libImaging/Raw.h new file mode 100644 index 000000000..4d28fa546 --- /dev/null +++ b/libImaging/Raw.h @@ -0,0 +1,15 @@ +/* Raw.h */ + +typedef struct { + + /* CONFIGURATION */ + + /* Distance between lines (0=no padding) */ + int stride; + + /* PRIVATE (initialized by decoder) */ + + /* Padding between lines */ + int skip; + +} RAWSTATE; diff --git a/libImaging/RawDecode.c b/libImaging/RawDecode.c new file mode 100644 index 000000000..5aadb2b44 --- /dev/null +++ b/libImaging/RawDecode.c @@ -0,0 +1,89 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for raw (uncompressed) image data + * + * history: + * 96-03-07 fl rewritten + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#include "Raw.h" + + +int +ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + enum { LINE = 1, SKIP }; + RAWSTATE* rawstate = state->context; + + UINT8* ptr; + + if (state->state == 0) { + + /* Initialize context variables */ + + /* get size of image data and padding */ + state->bytes = (state->xsize * state->bits + 7) / 8; + rawstate->skip = (rawstate->stride) ? + rawstate->stride - state->bytes : 0; + + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; + + state->state = LINE; + + } + + ptr = buf; + + for (;;) { + + if (state->state == SKIP) { + + /* Skip padding between lines */ + + if (bytes < rawstate->skip) + return ptr - buf; + + ptr += rawstate->skip; + bytes -= rawstate->skip; + + state->state = LINE; + + } + + if (bytes < state->bytes) + return ptr - buf; + + /* Unpack data */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, ptr, state->xsize); + + ptr += state->bytes; + bytes -= state->bytes; + + state->y += state->ystep; + + if (state->y < 0 || state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + + state->state = SKIP; + + } + +} diff --git a/libImaging/RawEncode.c b/libImaging/RawEncode.c new file mode 100644 index 000000000..a3b74b8cf --- /dev/null +++ b/libImaging/RawEncode.c @@ -0,0 +1,89 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * coder for raw data + * + * FIXME: This encoder will fail if the buffer is not large enough to + * hold one full line of data. There's a workaround for this problem + * in ImageFile.py, but it should be solved here instead. + * + * history: + * 96-04-30 fl created + * 97-01-03 fl fixed padding + * + * Copyright (c) Fredrik Lundh 1996-97. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. */ + + +#include "Imaging.h" + +int +ImagingRawEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + UINT8* ptr; + + if (!state->state) { + + /* The "count" field holds the stride, if specified. Fix + things up so "bytes" is the full size, and "count" the + packed size */ + + if (state->count > 0) { + int bytes = state->count; + + /* stride must not be less than real size */ + if (state->count < state->bytes) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + state->count = state->bytes; + state->bytes = bytes; + } else + state->count = state->bytes; + + /* The "ystep" field specifies the orientation */ + + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; + + state->state = 1; + + } + + if (bytes < state->bytes) { + state->errcode = IMAGING_CODEC_CONFIG; + return 0; + } + + ptr = buf; + + while (bytes >= state->bytes) { + + state->shuffle(ptr, (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); + + if (state->bytes > state->count) + /* zero-pad the buffer, if necessary */ + memset(ptr + state->count, 0, state->bytes - state->count); + + ptr += state->bytes; + bytes -= state->bytes; + + state->y += state->ystep; + + if (state->y < 0 || state->y >= state->ysize) { + state->errcode = IMAGING_CODEC_END; + break; + } + + } + + return ptr - buf; + +} diff --git a/libImaging/Storage.c b/libImaging/Storage.c new file mode 100644 index 000000000..76a66c2a5 --- /dev/null +++ b/libImaging/Storage.c @@ -0,0 +1,413 @@ +/* + * The Python Imaging Library + * $Id$ + * + * imaging storage object + * + * This baseline implementation is designed to efficiently handle + * large images, provided they fit into the available memory. + * + * history: + * 1995-06-15 fl Created + * 1995-09-12 fl Updated API, compiles silently under ANSI C++ + * 1995-11-26 fl Compiles silently under Borland 4.5 as well + * 1996-05-05 fl Correctly test status from Prologue + * 1997-05-12 fl Increased THRESHOLD (to speed up Tk interface) + * 1997-05-30 fl Added support for floating point images + * 1997-11-17 fl Added support for "RGBX" images + * 1998-01-11 fl Added support for integer images + * 1998-03-05 fl Exported Prologue/Epilogue functions + * 1998-07-01 fl Added basic "YCrCb" support + * 1998-07-03 fl Attach palette in prologue for "P" images + * 1998-07-09 hk Don't report MemoryError on zero-size images + * 1998-07-12 fl Change "YCrCb" to "YCbCr" (!) + * 1998-10-26 fl Added "I;16" and "I;16B" storage modes (experimental) + * 1998-12-29 fl Fixed allocation bug caused by previous fix + * 1999-02-03 fl Added "RGBa" and "BGR" modes (experimental) + * 2001-04-22 fl Fixed potential memory leak in ImagingCopyInfo + * 2003-09-26 fl Added "LA" and "PA" modes (experimental) + * 2005-10-02 fl Added image counter + * + * Copyright (c) 1998-2005 by Secret Labs AB + * Copyright (c) 1995-2005 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int ImagingNewCount = 0; + +/* -------------------------------------------------------------------- + * Standard image object. + */ + +Imaging +ImagingNewPrologueSubtype(const char *mode, unsigned xsize, unsigned ysize, + int size) +{ + Imaging im; + ImagingSectionCookie cookie; + + im = (Imaging) calloc(1, size); + if (!im) + return (Imaging) ImagingError_MemoryError(); + + /* Setup image descriptor */ + im->xsize = xsize; + im->ysize = ysize; + + im->type = IMAGING_TYPE_UINT8; + + if (strcmp(mode, "1") == 0) { + /* 1-bit images */ + im->bands = im->pixelsize = 1; + im->linesize = xsize; + + } else if (strcmp(mode, "P") == 0) { + /* 8-bit palette mapped images */ + im->bands = im->pixelsize = 1; + im->linesize = xsize; + im->palette = ImagingPaletteNew("RGB"); + + } else if (strcmp(mode, "PA") == 0) { + /* 8-bit palette with alpha */ + im->bands = 2; + im->pixelsize = 4; /* store in image32 memory */ + im->linesize = xsize * 4; + im->palette = ImagingPaletteNew("RGB"); + + } else if (strcmp(mode, "L") == 0) { + /* 8-bit greyscale (luminance) images */ + im->bands = im->pixelsize = 1; + im->linesize = xsize; + + } else if (strcmp(mode, "LA") == 0) { + /* 8-bit greyscale (luminance) with alpha */ + im->bands = 2; + im->pixelsize = 4; /* store in image32 memory */ + im->linesize = xsize * 4; + + } else if (strcmp(mode, "F") == 0) { + /* 32-bit floating point images */ + im->bands = 1; + im->pixelsize = 4; + im->linesize = xsize * 4; + im->type = IMAGING_TYPE_FLOAT32; + + } else if (strcmp(mode, "I") == 0) { + /* 32-bit integer images */ + im->bands = 1; + im->pixelsize = 4; + im->linesize = xsize * 4; + im->type = IMAGING_TYPE_INT32; + + } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 || strcmp(mode, "I;16B") == 0) { + /* EXPERIMENTAL */ + /* 16-bit raw integer images */ + im->bands = 1; + im->pixelsize = 2; + im->linesize = xsize * 2; + im->type = IMAGING_TYPE_SPECIAL; + + } else if (strcmp(mode, "RGB") == 0) { + /* 24-bit true colour images */ + im->bands = 3; + im->pixelsize = 4; + im->linesize = xsize * 4; + + } else if (strcmp(mode, "BGR;15") == 0) { + /* EXPERIMENTAL */ + /* 15-bit true colour */ + im->bands = 1; + im->pixelsize = 2; + im->linesize = (xsize*2 + 3) & -4; + im->type = IMAGING_TYPE_SPECIAL; + + } else if (strcmp(mode, "BGR;16") == 0) { + /* EXPERIMENTAL */ + /* 16-bit reversed true colour */ + im->bands = 1; + im->pixelsize = 2; + im->linesize = (xsize*2 + 3) & -4; + im->type = IMAGING_TYPE_SPECIAL; + + } else if (strcmp(mode, "BGR;24") == 0) { + /* EXPERIMENTAL */ + /* 24-bit reversed true colour */ + im->bands = 1; + im->pixelsize = 3; + im->linesize = (xsize*3 + 3) & -4; + im->type = IMAGING_TYPE_SPECIAL; + + } else if (strcmp(mode, "BGR;32") == 0) { + /* EXPERIMENTAL */ + /* 32-bit reversed true colour */ + im->bands = 1; + im->pixelsize = 4; + im->linesize = (xsize*4 + 3) & -4; + im->type = IMAGING_TYPE_SPECIAL; + + } else if (strcmp(mode, "RGBX") == 0) { + /* 32-bit true colour images with padding */ + im->bands = im->pixelsize = 4; + im->linesize = xsize * 4; + + } else if (strcmp(mode, "RGBA") == 0) { + /* 32-bit true colour images with alpha */ + im->bands = im->pixelsize = 4; + im->linesize = xsize * 4; + + } else if (strcmp(mode, "RGBa") == 0) { + /* EXPERIMENTAL */ + /* 32-bit true colour images with premultiplied alpha */ + im->bands = im->pixelsize = 4; + im->linesize = xsize * 4; + + } else if (strcmp(mode, "CMYK") == 0) { + /* 32-bit colour separation */ + im->bands = im->pixelsize = 4; + im->linesize = xsize * 4; + + } else if (strcmp(mode, "YCbCr") == 0) { + /* 24-bit video format */ + im->bands = 3; + im->pixelsize = 4; + im->linesize = xsize * 4; + + } else { + free(im); + return (Imaging) ImagingError_ValueError("unrecognized mode"); + } + + /* Setup image descriptor */ + strcpy(im->mode, mode); + + ImagingSectionEnter(&cookie); + + /* Pointer array (allocate at least one line, to avoid MemoryError + exceptions on platforms where calloc(0, x) returns NULL) */ + im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *)); + + ImagingSectionLeave(&cookie); + + if (!im->image) { + free(im); + return (Imaging) ImagingError_MemoryError(); + } + + ImagingNewCount++; + + return im; +} + +Imaging +ImagingNewPrologue(const char *mode, unsigned xsize, unsigned ysize) +{ + return ImagingNewPrologueSubtype( + mode, xsize, ysize, sizeof(struct ImagingMemoryInstance) + ); +} + +Imaging +ImagingNewEpilogue(Imaging im) +{ + /* If the raster data allocator didn't setup a destructor, + assume that it couldn't allocate the required amount of + memory. */ + if (!im->destroy) + return (Imaging) ImagingError_MemoryError(); + + /* Initialize alias pointers to pixel data. */ + switch (im->pixelsize) { + case 1: case 2: case 3: + im->image8 = (UINT8 **) im->image; + break; + case 4: + im->image32 = (INT32 **) im->image; + break; + } + + return im; +} + +void +ImagingDelete(Imaging im) +{ + if (!im) + return; + + if (im->palette) + ImagingPaletteDelete(im->palette); + + if (im->destroy) + im->destroy(im); + + if (im->image) + free(im->image); + + free(im); +} + + +/* Array Storage Type */ +/* ------------------ */ +/* Allocate image as an array of line buffers. */ + +static void +ImagingDestroyArray(Imaging im) +{ + int y; + + if (im->image) + for (y = 0; y < im->ysize; y++) + if (im->image[y]) + free(im->image[y]); +} + +Imaging +ImagingNewArray(const char *mode, int xsize, int ysize) +{ + Imaging im; + ImagingSectionCookie cookie; + + int y; + char* p; + + im = ImagingNewPrologue(mode, xsize, ysize); + if (!im) + return NULL; + + ImagingSectionEnter(&cookie); + + /* Allocate image as an array of lines */ + for (y = 0; y < im->ysize; y++) { + p = (char *) malloc(im->linesize); + if (!p) { + ImagingDestroyArray(im); + break; + } + im->image[y] = p; + } + + ImagingSectionLeave(&cookie); + + if (y == im->ysize) + im->destroy = ImagingDestroyArray; + + return ImagingNewEpilogue(im); +} + + +/* Block Storage Type */ +/* ------------------ */ +/* Allocate image as a single block. */ + +static void +ImagingDestroyBlock(Imaging im) +{ + if (im->block) + free(im->block); +} + +Imaging +ImagingNewBlock(const char *mode, int xsize, int ysize) +{ + Imaging im; + int y, i; + int bytes; + + im = ImagingNewPrologue(mode, xsize, ysize); + if (!im) + return NULL; + + /* Use a single block */ + bytes = im->ysize * im->linesize; + if (bytes <= 0) + /* some platforms return NULL for malloc(0); this fix + prevents MemoryError on zero-sized images on such + platforms */ + bytes = 1; + im->block = (char *) malloc(bytes); + + if (im->block) { + + for (y = i = 0; y < im->ysize; y++) { + im->image[y] = im->block + i; + i += im->linesize; + } + + im->destroy = ImagingDestroyBlock; + + } + + return ImagingNewEpilogue(im); +} + +/* -------------------------------------------------------------------- + * Create a new, internally allocated, image. + */ +#if defined(IMAGING_SMALL_MODEL) +#define THRESHOLD 16384L +#else +#define THRESHOLD (2048*2048*4L) +#endif + +Imaging +ImagingNew(const char* mode, int xsize, int ysize) +{ + int bytes; + Imaging im; + + if (strlen(mode) == 1) { + if (mode[0] == 'F' || mode[0] == 'I') + bytes = 4; + else + bytes = 1; + } else + bytes = strlen(mode); /* close enough */ + + if ((long) xsize * ysize * bytes <= THRESHOLD) { + im = ImagingNewBlock(mode, xsize, ysize); + if (im) + return im; + /* assume memory error; try allocating in array mode instead */ + ImagingError_Clear(); + } + + return ImagingNewArray(mode, xsize, ysize); +} + +Imaging +ImagingNew2(const char* mode, Imaging imOut, Imaging imIn) +{ + /* allocate or validate output image */ + + if (imOut) { + /* make sure images match */ + if (strcmp(imOut->mode, mode) != 0 + || imOut->xsize != imIn->xsize + || imOut->ysize != imIn->ysize) { + return ImagingError_Mismatch(); + } + } else { + /* create new image */ + imOut = ImagingNew(mode, imIn->xsize, imIn->ysize); + if (!imOut) + return NULL; + } + + return imOut; +} + +void +ImagingCopyInfo(Imaging destination, Imaging source) +{ + if (source->palette) { + if (destination->palette) + ImagingPaletteDelete(destination->palette); + destination->palette = ImagingPaletteDuplicate(source->palette); + } +} diff --git a/libImaging/SunRleDecode.c b/libImaging/SunRleDecode.c new file mode 100644 index 000000000..bf34d9382 --- /dev/null +++ b/libImaging/SunRleDecode.c @@ -0,0 +1,112 @@ +/* + * THIS IS WORK IN PROGRESS + * + * The Python Imaging Library. + * $Id$ + * + * decoder for SUN RLE data. + * + * history: + * 97-01-04 fl Created + * + * Copyright (c) Fredrik Lundh 1997. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + int n; + UINT8* ptr; + + ptr = buf; + + for (;;) { + + if (bytes < 1) + return ptr - buf; + + if (ptr[0] == 0x80) { + + if (bytes < 2) + break; + + n = ptr[1]; + + if (n == 0) { + + /* Literal 0x80 (2 bytes) */ + n = 1; + + state->buffer[state->x] = 0x80; + + ptr += 2; + bytes -= 2; + + } else { + + /* Run (3 bytes) */ + if (bytes < 3) + break; + + if (state->x + n > state->bytes) { + /* FIXME: is this correct? */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + memset(state->buffer + state->x, ptr[2], n); + + ptr += 3; + bytes -= 3; + + } + + } else { + + /* Literal (1+n bytes block) */ + n = ptr[0]; + + if (bytes < 1 + n) + break; + + if (state->x + n > state->bytes) { + /* FIXME: is this correct? */ + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + memcpy(state->buffer + state->x, ptr + 1, n); + + ptr += 1 + n; + bytes -= 1 + n; + + } + + state->x += n; + + if (state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + } + + return ptr - buf; +} diff --git a/libImaging/TgaRleDecode.c b/libImaging/TgaRleDecode.c new file mode 100644 index 000000000..cad6bc3bc --- /dev/null +++ b/libImaging/TgaRleDecode.c @@ -0,0 +1,118 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for Targa RLE data. + * + * history: + * 97-01-04 fl created + * 98-09-11 fl don't one byte per pixel; take orientation into account + * + * Copyright (c) Fredrik Lundh 1997. + * Copyright (c) Secret Labs AB 1997-98. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingTgaRleDecode(Imaging im, ImagingCodecState state, + UINT8* buf, int bytes) +{ + int n, depth; + UINT8* ptr; + + ptr = buf; + + if (state->state == 0) { + + /* check image orientation */ + if (state->ystep < 0) { + state->y = state->ysize-1; + state->ystep = -1; + } else + state->ystep = 1; + + state->state = 1; + + } + + depth = state->count; + + for (;;) { + + if (bytes < 1) + return ptr - buf; + + if (ptr[0] & 0x80) { + + /* Run (1 + pixelsize bytes) */ + + if (bytes < 1 + depth) + break; + + n = depth * ((ptr[0] & 0x7f) + 1); + + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + if (depth == 1) + memset(state->buffer + state->x, ptr[1], n); + else { + int i; + for (i = 0; i < n; i += depth) + memcpy(state->buffer + state->x + i, ptr+1, depth); + } + + ptr += 1 + depth; + bytes -= 1 + depth; + + } else { + + /* Literal (1+n+1 bytes block) */ + n = depth * (ptr[0] + 1); + + if (bytes < 1 + n) + break; + + if (state->x + n > state->bytes) { + state->errcode = IMAGING_CODEC_OVERRUN; + return -1; + } + + memcpy(state->buffer + state->x, ptr + 1, n); + + ptr += 1 + n; + bytes -= 1 + n; + + } + + state->x += n; + + if (state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->buffer, + state->xsize); + + state->x = 0; + + state->y += state->ystep; + + if (state->y < 0 || state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + + } + + } + + return ptr - buf; +} diff --git a/libImaging/Unpack.c b/libImaging/Unpack.c new file mode 100644 index 000000000..90a76feb3 --- /dev/null +++ b/libImaging/Unpack.c @@ -0,0 +1,1028 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * code to unpack raw data from various file formats + * + * history: + * 1996-03-07 fl Created (from various decoders) + * 1996-04-19 fl Added band unpackers + * 1996-05-12 fl Published RGB unpackers + * 1996-05-27 fl Added nibble unpacker + * 1996-12-10 fl Added complete set of PNG unpackers + * 1996-12-29 fl Set alpha byte in RGB unpackers + * 1997-01-05 fl Added remaining TGA unpackers + * 1997-01-18 fl Added inverting band unpackers + * 1997-01-25 fl Added FlashPix unpackers + * 1997-05-31 fl Added floating point unpackers + * 1998-02-08 fl Added I unpacker + * 1998-07-01 fl Added YCbCr unpacker + * 1998-07-02 fl Added full set of integer unpackers + * 1998-12-29 fl Added mode field, I;16 unpackers + * 1998-12-30 fl Added RGBX modes + * 1999-02-04 fl Fixed I;16 unpackers + * 2003-05-13 fl Added L/RGB reversed unpackers + * 2003-09-26 fl Added LA/PA and RGBa->RGB unpackers + * + * Copyright (c) 1997-2003 by Secret Labs AB. + * Copyright (c) 1996-1997 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + +#include "Imaging.h" + + +#define R 0 +#define G 1 +#define B 2 +#define X 3 + +#define A 3 + +#define C 0 +#define M 1 +#define Y 2 +#define K 3 + +#define CLIP(x) ((x) <= 0 ? 0 : (x) < 256 ? (x) : 255) + +/* byte-swapping macros */ + +#define C16N\ + (tmp[0]=in[0], tmp[1]=in[1]); +#define C16S\ + (tmp[1]=in[0], tmp[0]=in[1]); +#define C32N\ + (tmp[0]=in[0], tmp[1]=in[1], tmp[2]=in[2], tmp[3]=in[3]); +#define C32S\ + (tmp[3]=in[0], tmp[2]=in[1], tmp[1]=in[2], tmp[0]=in[3]); +#define C64N\ + (tmp[0]=in[0], tmp[1]=in[1], tmp[2]=in[2], tmp[3]=in[3],\ + tmp[4]=in[4], tmp[5]=in[5], tmp[6]=in[6], tmp[7]=in[7]); +#define C64S\ + (tmp[7]=in[0], tmp[6]=in[1], tmp[5]=in[2], tmp[4]=in[3],\ + tmp[3]=in[4], tmp[2]=in[5], tmp[1]=in[6], tmp[0]=in[7]); + +#ifdef WORDS_BIGENDIAN +#define C16B C16N +#define C16L C16S +#define C32B C32N +#define C32L C32S +#define C64B C64N +#define C64L C64S +#else +#define C16B C16S +#define C16L C16N +#define C32B C32S +#define C32L C32N +#define C64B C64S +#define C64L C64N +#endif + +/* bit-swapping */ + +static UINT8 BITFLIP[] = { + 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, + 240, 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, + 120, 248, 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, + 180, 116, 244, 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, + 60, 188, 124, 252, 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, + 210, 50, 178, 114, 242, 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, + 90, 218, 58, 186, 122, 250, 6, 134, 70, 198, 38, 166, 102, 230, 22, + 150, 86, 214, 54, 182, 118, 246, 14, 142, 78, 206, 46, 174, 110, 238, + 30, 158, 94, 222, 62, 190, 126, 254, 1, 129, 65, 193, 33, 161, 97, + 225, 17, 145, 81, 209, 49, 177, 113, 241, 9, 137, 73, 201, 41, 169, + 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, 5, 133, 69, 197, 37, + 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, 13, 141, 77, 205, + 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, 3, 131, 67, + 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, 11, 139, + 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, 7, + 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, + 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, + 255 +}; + +/* Unpack to "1" image */ + +static void +unpack1(UINT8* out, const UINT8* in, int pixels) +{ + /* bits (msb first, white is non-zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 7: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 6: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 5: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 4: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 3: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 2: *out++ = (byte & 128) ? 255 : 0; byte <<= 1; + case 1: *out++ = (byte & 128) ? 255 : 0; + } + pixels -= 8; + } +} + +static void +unpack1I(UINT8* out, const UINT8* in, int pixels) +{ + /* bits (msb first, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 7: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 6: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 5: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 4: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 3: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 2: *out++ = (byte & 128) ? 0 : 255; byte <<= 1; + case 1: *out++ = (byte & 128) ? 0 : 255; + } + pixels -= 8; + } +} + +static void +unpack1R(UINT8* out, const UINT8* in, int pixels) +{ + /* bits (lsb first, white is non-zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 7: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 6: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 5: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 4: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 3: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 2: *out++ = (byte & 1) ? 255 : 0; byte >>= 1; + case 1: *out++ = (byte & 1) ? 255 : 0; + } + pixels -= 8; + } +} + +static void +unpack1IR(UINT8* out, const UINT8* in, int pixels) +{ + /* bits (lsb first, white is zero) */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 7: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 6: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 5: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 4: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 3: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 2: *out++ = (byte & 1) ? 0 : 255; byte >>= 1; + case 1: *out++ = (byte & 1) ? 0 : 255; + } + pixels -= 8; + } +} + + +/* Unpack to "L" image */ + +static void +unpackL2(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 3: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 2: *out++ = ((byte >> 6) & 3) * 255 / 3; byte <<= 2; + case 1: *out++ = ((byte >> 6) & 3) * 255 / 3; + } + pixels -= 4; + } +} + +static void +unpackL4(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = ((byte >> 4) & 15) * 255 / 15; byte <<= 4; + case 1: *out++ = ((byte >> 4) & 15) * 255 / 15; + } + pixels -= 2; + } +} + +static void +unpackLA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LA, pixel interleaved */ + for (i = 0; i < pixels; i++) { + out[R] = out[G] = out[B] = in[0]; + out[A] = in[1]; + in += 2; out += 4; + } +} + +static void +unpackLAL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* LA, line interleaved */ + for (i = 0; i < pixels; i++) { + out[R] = out[G] = out[B] = in[i]; + out[A] = in[i+pixels]; + out += 4; + } +} + +static void +unpackLI(UINT8* out, const UINT8* in, int pixels) +{ + /* negative */ + int i; + for (i = 0; i < pixels; i++) + out[i] = ~in[i]; +} + +static void +unpackLR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, bit reversed */ + for (i = 0; i < pixels; i++) { + out[i] = BITFLIP[in[i]]; + } +} + +static void +unpackL16(UINT8* out, const UINT8* in, int pixels) +{ + /* int16 (upper byte, little endian) */ + int i; + for (i = 0; i < pixels; i++) { + out[i] = in[1]; + in += 2; + } +} + +static void +unpackL16B(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* int16 (upper byte, big endian) */ + for (i = 0; i < pixels; i++) { + out[i] = in[0]; + in += 2; + } +} + + +/* Unpack to "P" image */ + +static void +unpackP1(UINT8* out, const UINT8* in, int pixels) +{ + /* bits */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 7) & 1; byte <<= 1; + case 7: *out++ = (byte >> 7) & 1; byte <<= 1; + case 6: *out++ = (byte >> 7) & 1; byte <<= 1; + case 5: *out++ = (byte >> 7) & 1; byte <<= 1; + case 4: *out++ = (byte >> 7) & 1; byte <<= 1; + case 3: *out++ = (byte >> 7) & 1; byte <<= 1; + case 2: *out++ = (byte >> 7) & 1; byte <<= 1; + case 1: *out++ = (byte >> 7) & 1; + } + pixels -= 8; + } +} + +static void +unpackP2(UINT8* out, const UINT8* in, int pixels) +{ + /* bit pairs */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 6) & 3; byte <<= 2; + case 3: *out++ = (byte >> 6) & 3; byte <<= 2; + case 2: *out++ = (byte >> 6) & 3; byte <<= 2; + case 1: *out++ = (byte >> 6) & 3; + } + pixels -= 4; + } +} + +static void +unpackP4(UINT8* out, const UINT8* in, int pixels) +{ + /* nibbles */ + while (pixels > 0) { + UINT8 byte = *in++; + switch (pixels) { + default: *out++ = (byte >> 4) & 15; byte <<= 4; + case 1: *out++ = (byte >> 4) & 15; + } + pixels -= 2; + } +} + +static void +unpackP2L(UINT8* out, const UINT8* in, int pixels) +{ + int i, j, m, s; + /* bit layers */ + m = 128; + s = (pixels+7)/8; + for (i = j = 0; i < pixels; i++) { + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } + } +} + +static void +unpackP4L(UINT8* out, const UINT8* in, int pixels) +{ + int i, j, m, s; + /* bit layers (trust the optimizer ;-) */ + m = 128; + s = (pixels+7)/8; + for (i = j = 0; i < pixels; i++) { + out[i] = ((in[j] & m) ? 1 : 0) + ((in[j + s] & m) ? 2 : 0) + + ((in[j + 2*s] & m) ? 4 : 0) + ((in[j + 3*s] & m) ? 8 : 0); + if ((m >>= 1) == 0) { + m = 128; + j++; + } + } +} + +/* Unpack to "RGB" image */ + +void +ImagingUnpackRGB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB triplets */ + for (i = 0; i < pixels; i++) { + out[R] = in[0]; + out[G] = in[1]; + out[B] = in[2]; + out[A] = 255; + out += 4; in += 3; + } +} + +void +unpackRGB16B(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* 16-bit RGB triplets, big-endian order */ + for (i = 0; i < pixels; i++) { + out[R] = in[0]; + out[G] = in[2]; + out[B] = in[4]; + out[A] = 255; + out += 4; in += 6; + } +} + +static void +unpackRGBL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, line interleaved */ + for (i = 0; i < pixels; i++) { + out[R] = in[i]; + out[G] = in[i+pixels]; + out[B] = in[i+pixels+pixels]; + out[A] = 255; + out += 4; + } +} + +static void +unpackRGBR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, bit reversed */ + for (i = 0; i < pixels; i++) { + out[R] = BITFLIP[in[0]]; + out[G] = BITFLIP[in[1]]; + out[B] = BITFLIP[in[2]]; + out[A] = 255; + out += 4; in += 3; + } +} + +void +ImagingUnpackBGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, reversed bytes */ + for (i = 0; i < pixels; i++) { + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = 255; + out += 4; in += 3; + } +} + +void +ImagingUnpackBGR15(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, reversed bytes, 5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 31) * 255 / 31; + out[R] = ((pixel>>10) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +void +ImagingUnpackBGR16(UINT8* out, const UINT8* in, int pixels) +{ + int i, pixel; + /* RGB, reversed bytes, 5/6/5 bits per pixel */ + for (i = 0; i < pixels; i++) { + pixel = in[0] + (in[1] << 8); + out[B] = (pixel & 31) * 255 / 31; + out[G] = ((pixel>>5) & 63) * 255 / 63; + out[R] = ((pixel>>11) & 31) * 255 / 31; + out[A] = 255; + out += 4; in += 2; + } +} + +static void +ImagingUnpackBGRX(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, reversed bytes with padding */ + for (i = 0; i < pixels; i++) { + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = 255; + out += 4; in += 4; + } +} + +static void +ImagingUnpackXRGB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, leading pad */ + for (i = 0; i < pixels; i++) { + out[R] = in[1]; + out[G] = in[2]; + out[B] = in[3]; + out[A] = 255; + out += 4; in += 4; + } +} + +static void +ImagingUnpackXBGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGB, reversed bytes, leading pad */ + for (i = 0; i < pixels; i++) { + out[R] = in[3]; + out[G] = in[2]; + out[B] = in[1]; + out[A] = 255; + out += 4; in += 4; + } +} + +/* Unpack to "RGBA" image */ + +static void +unpackRGBALA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* greyscale with alpha */ + for (i = 0; i < pixels; i++) { + out[R] = out[G] = out[B] = in[0]; + out[A] = in[1]; + out += 4; in += 2; + } +} + +static void +unpackRGBALA16B(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* 16-bit greyscale with alpha, big-endian */ + for (i = 0; i < pixels; i++) { + out[R] = out[G] = out[B] = in[0]; + out[A] = in[2]; + out += 4; in += 4; + } +} + +static void +unpackRGBa(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* premultiplied RGBA */ + for (i = 0; i < pixels; i++) { + int a = in[3]; + if (!a) + out[R] = out[G] = out[B] = out[A] = 0; + else { + out[R] = CLIP(in[0] * 255 / a); + out[G] = CLIP(in[1] * 255 / a); + out[B] = CLIP(in[2] * 255 / a); + out[A] = a; + } + out += 4; in += 4; + } +} + +static void +unpackRGBAI(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGBA, inverted RGB bytes (FlashPix) */ + for (i = 0; i < pixels; i++) { + out[R] = ~in[0]; + out[G] = ~in[1]; + out[B] = ~in[2]; + out[A] = in[3]; + out += 4; in += 4; + } +} + +static void +unpackRGBAL(UINT8* out, const UINT8* in, int pixels) +{ + int i; + + /* RGBA, line interleaved */ + for (i = 0; i < pixels; i++) { + out[R] = in[i]; + out[G] = in[i+pixels]; + out[B] = in[i+pixels+pixels]; + out[A] = in[i+pixels+pixels+pixels]; + out += 4; + } +} + +void +unpackRGBA16B(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* 16-bit RGBA, big-endian order */ + for (i = 0; i < pixels; i++) { + out[R] = in[0]; + out[G] = in[2]; + out[B] = in[4]; + out[A] = in[6]; + out += 4; in += 8; + } +} + +static void +unpackARGB(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGBA, leading pad */ + for (i = 0; i < pixels; i++) { + out[R] = in[1]; + out[G] = in[2]; + out[B] = in[3]; + out[A] = in[0]; + out += 4; in += 4; + } +} + +static void +unpackABGR(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGBA, reversed bytes */ + for (i = 0; i < pixels; i++) { + out[R] = in[3]; + out[G] = in[2]; + out[B] = in[1]; + out[A] = in[0]; + out += 4; in += 4; + } +} + +static void +unpackBGRA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* RGBA, reversed bytes */ + for (i = 0; i < pixels; i++) { + out[R] = in[2]; + out[G] = in[1]; + out[B] = in[0]; + out[A] = in[3]; + out += 4; in += 4; + } +} + + +/* Unpack to "CMYK" image */ + +static void +unpackCMYKI(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* CMYK, inverted bytes (Photoshop 2.5) */ + for (i = 0; i < pixels; i++) { + out[C] = ~in[0]; + out[M] = ~in[1]; + out[Y] = ~in[2]; + out[K] = ~in[3]; + out += 4; in += 4; + } +} + +static void +copy1(UINT8* out, const UINT8* in, int pixels) +{ + /* L, P */ + memcpy(out, in, pixels); +} + +static void +copy2(UINT8* out, const UINT8* in, int pixels) +{ + /* I;16 */ + memcpy(out, in, pixels*2); +} + +static void +copy4(UINT8* out, const UINT8* in, int pixels) +{ + /* RGBA, CMYK quadruples */ + memcpy(out, in, 4 * pixels); +} + + +/* Unpack to "I" and "F" images */ + +#define UNPACK_RAW(NAME, GET, INTYPE, OUTTYPE)\ +static void NAME(UINT8* out_, const UINT8* in, int pixels)\ +{\ + int i;\ + OUTTYPE* out = (OUTTYPE*) out_;\ + for (i = 0; i < pixels; i++, in += sizeof(INTYPE))\ + out[i] = (OUTTYPE) ((INTYPE) GET);\ +} + +#define UNPACK(NAME, COPY, INTYPE, OUTTYPE)\ +static void NAME(UINT8* out_, const UINT8* in, int pixels)\ +{\ + int i;\ + OUTTYPE* out = (OUTTYPE*) out_;\ + INTYPE tmp_;\ + UINT8* tmp = (UINT8*) &tmp_;\ + for (i = 0; i < pixels; i++, in += sizeof(INTYPE)) {\ + COPY;\ + out[i] = (OUTTYPE) tmp_;\ + }\ +} + +UNPACK_RAW(unpackI8, in[0], UINT8, INT32) +UNPACK_RAW(unpackI8S, in[0], INT8, INT32) +UNPACK(unpackI16, C16L, UINT16, INT32) +UNPACK(unpackI16S, C16L, INT16, INT32) +UNPACK(unpackI16B, C16B, UINT16, INT32) +UNPACK(unpackI16BS, C16B, INT16, INT32) +UNPACK(unpackI16N, C16N, UINT16, INT32) +UNPACK(unpackI16NS, C16N, INT16, INT32) +UNPACK(unpackI32, C32L, UINT32, INT32) +UNPACK(unpackI32S, C32L, INT32, INT32) +UNPACK(unpackI32B, C32B, UINT32, INT32) +UNPACK(unpackI32BS, C32B, INT32, INT32) +UNPACK(unpackI32N, C32N, UINT32, INT32) +UNPACK(unpackI32NS, C32N, INT32, INT32) + +UNPACK_RAW(unpackF8, in[0], UINT8, FLOAT32) +UNPACK_RAW(unpackF8S, in[0], INT8, FLOAT32) +UNPACK(unpackF16, C16L, UINT16, FLOAT32) +UNPACK(unpackF16S, C16L, INT16, FLOAT32) +UNPACK(unpackF16B, C16B, UINT16, FLOAT32) +UNPACK(unpackF16BS, C16B, INT16, FLOAT32) +UNPACK(unpackF16N, C16N, UINT16, FLOAT32) +UNPACK(unpackF16NS, C16N, INT16, FLOAT32) +UNPACK(unpackF32, C32L, UINT32, FLOAT32) +UNPACK(unpackF32S, C32L, INT32, FLOAT32) +UNPACK(unpackF32B, C32B, UINT32, FLOAT32) +UNPACK(unpackF32BS, C32B, INT32, FLOAT32) +UNPACK(unpackF32N, C32N, UINT32, FLOAT32) +UNPACK(unpackF32NS, C32N, INT32, FLOAT32) +UNPACK(unpackF32F, C32L, FLOAT32, FLOAT32) +UNPACK(unpackF32BF, C32B, FLOAT32, FLOAT32) +UNPACK(unpackF32NF, C32N, FLOAT32, FLOAT32) +#ifdef FLOAT64 +UNPACK(unpackF64F, C64L, FLOAT64, FLOAT32) +UNPACK(unpackF64BF, C64B, FLOAT64, FLOAT32) +UNPACK(unpackF64NF, C64N, FLOAT64, FLOAT32) +#endif + + +/* Misc. unpackers */ + +static void +band0(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 0 only */ + for (i = 0; i < pixels; i++) { + out[0] = in[i]; + out += 4; + } +} + +static void +band1(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 1 only */ + for (i = 0; i < pixels; i++) { + out[1] = in[i]; + out += 4; + } +} + +static void +band2(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 2 only */ + for (i = 0; i < pixels; i++) { + out[2] = in[i]; + out += 4; + } +} + +static void +band3(UINT8* out, const UINT8* in, int pixels) +{ + /* band 3 only */ + int i; + for (i = 0; i < pixels; i++) { + out[3] = in[i]; + out += 4; + } +} + +static void +band0I(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 0 only */ + for (i = 0; i < pixels; i++) { + out[0] = ~in[i]; + out += 4; + } +} + +static void +band1I(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 1 only */ + for (i = 0; i < pixels; i++) { + out[1] = ~in[i]; + out += 4; + } +} + +static void +band2I(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* band 2 only */ + for (i = 0; i < pixels; i++) { + out[2] = ~in[i]; + out += 4; + } +} + +static void +band3I(UINT8* out, const UINT8* in, int pixels) +{ + /* band 3 only */ + int i; + for (i = 0; i < pixels; i++) { + out[3] = ~in[i]; + out += 4; + } +} + +static struct { + const char* mode; + const char* rawmode; + int bits; + ImagingShuffler unpack; +} unpackers[] = { + + /* raw mode syntax is ";" where "bits" defaults + depending on mode (1 for "1", 8 for "P" and "L", etc), and + "flags" should be given in alphabetical order. if both bits + and flags have their default values, the ; should be left out */ + + /* flags: "I" inverted data; "R" reversed bit order; "B" big + endian byte order (default is little endian); "L" line + interleave, "S" signed, "F" floating point */ + + /* bilevel */ + {"1", "1", 1, unpack1}, + {"1", "1;I", 1, unpack1I}, + {"1", "1;R", 1, unpack1R}, + {"1", "1;IR", 1, unpack1IR}, + + /* greyscale */ + {"L", "L;2", 2, unpackL2}, + {"L", "L;4", 4, unpackL4}, + {"L", "L", 8, copy1}, + {"L", "L;I", 8, unpackLI}, + {"L", "L;R", 8, unpackLR}, + {"L", "L;16", 16, unpackL16}, + {"L", "L;16B", 16, unpackL16B}, + + /* greyscale w. alpha */ + {"LA", "LA", 16, unpackLA}, + {"LA", "LA;L", 16, unpackLAL}, + + /* palette */ + {"P", "P;1", 1, unpackP1}, + {"P", "P;2", 2, unpackP2}, + {"P", "P;2L", 2, unpackP2L}, + {"P", "P;4", 4, unpackP4}, + {"P", "P;4L", 4, unpackP4L}, + {"P", "P", 8, copy1}, + {"P", "P;R", 8, unpackLR}, + + /* palette w. alpha */ + {"PA", "PA", 16, unpackLA}, + {"PA", "PA;L", 16, unpackLAL}, + + /* true colour */ + {"RGB", "RGB", 24, ImagingUnpackRGB}, + {"RGB", "RGB;L", 24, unpackRGBL}, + {"RGB", "RGB;R", 24, unpackRGBR}, + {"RGB", "RGB;16B", 48, unpackRGB16B}, + {"RGB", "BGR", 24, ImagingUnpackBGR}, + {"RGB", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGB", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ + {"RGB", "RGBX", 32, copy4}, + {"RGB", "RGBX;L", 32, unpackRGBAL}, + {"RGB", "BGRX", 32, ImagingUnpackBGRX}, + {"RGB", "XRGB", 24, ImagingUnpackXRGB}, + {"RGB", "XBGR", 32, ImagingUnpackXBGR}, + {"RGB", "YCC;P", 24, ImagingUnpackYCC}, + {"RGB", "R", 8, band0}, + {"RGB", "G", 8, band1}, + {"RGB", "B", 8, band2}, + + /* true colour w. alpha */ + {"RGBA", "LA", 16, unpackRGBALA}, + {"RGBA", "LA;16B", 32, unpackRGBALA16B}, + {"RGBA", "RGBA", 32, copy4}, + {"RGBA", "RGBa", 32, unpackRGBa}, + {"RGBA", "RGBA;I", 32, unpackRGBAI}, + {"RGBA", "RGBA;L", 32, unpackRGBAL}, + {"RGBA", "RGBA;16B", 64, unpackRGBA16B}, + {"RGBA", "BGRA", 32, unpackBGRA}, + {"RGBA", "ARGB", 32, unpackARGB}, + {"RGBA", "ABGR", 32, unpackABGR}, + {"RGBA", "YCCA;P", 32, ImagingUnpackYCCA}, + {"RGBA", "R", 8, band0}, + {"RGBA", "G", 8, band1}, + {"RGBA", "B", 8, band2}, + {"RGBA", "A", 8, band3}, + + /* true colour w. padding */ + {"RGBX", "RGB", 24, ImagingUnpackRGB}, + {"RGBX", "RGB;L", 24, unpackRGBL}, + {"RGBX", "RGB;16B", 48, unpackRGB16B}, + {"RGBX", "BGR", 24, ImagingUnpackBGR}, + {"RGBX", "BGR;15", 16, ImagingUnpackBGR15}, + {"RGB", "BGR;16", 16, ImagingUnpackBGR16}, + {"RGBX", "BGR;5", 16, ImagingUnpackBGR15}, /* compat */ + {"RGBX", "RGBX", 32, copy4}, + {"RGBX", "RGBX;L", 32, unpackRGBAL}, + {"RGBX", "BGRX", 32, ImagingUnpackBGRX}, + {"RGBX", "XRGB", 24, ImagingUnpackXRGB}, + {"RGBX", "XBGR", 32, ImagingUnpackXBGR}, + {"RGBX", "YCC;P", 24, ImagingUnpackYCC}, + {"RGBX", "R", 8, band0}, + {"RGBX", "G", 8, band1}, + {"RGBX", "B", 8, band2}, + {"RGBX", "X", 8, band3}, + + /* colour separation */ + {"CMYK", "CMYK", 32, copy4}, + {"CMYK", "CMYK;I", 32, unpackCMYKI}, + {"CMYK", "CMYK;L", 32, unpackRGBAL}, + {"CMYK", "C", 8, band0}, + {"CMYK", "M", 8, band1}, + {"CMYK", "Y", 8, band2}, + {"CMYK", "K", 8, band3}, + {"CMYK", "C;I", 8, band0I}, + {"CMYK", "M;I", 8, band1I}, + {"CMYK", "Y;I", 8, band2I}, + {"CMYK", "K;I", 8, band3I}, + + /* video (YCbCr) */ + {"YCbCr", "YCbCr", 24, ImagingUnpackRGB}, + {"YCbCr", "YCbCr;L", 24, unpackRGBL}, + {"YCbCr", "YCbCrX", 32, copy4}, + {"YCbCr", "YCbCrK", 32, copy4}, + + /* integer variations */ + {"I", "I", 32, copy4}, + {"I", "I;8", 8, unpackI8}, + {"I", "I;8S", 8, unpackI8S}, + {"I", "I;16", 16, unpackI16}, + {"I", "I;16S", 16, unpackI16S}, + {"I", "I;16B", 16, unpackI16B}, + {"I", "I;16BS", 16, unpackI16BS}, + {"I", "I;16N", 16, unpackI16N}, + {"I", "I;16NS", 16, unpackI16NS}, + {"I", "I;32", 32, unpackI32}, + {"I", "I;32S", 32, unpackI32S}, + {"I", "I;32B", 32, unpackI32B}, + {"I", "I;32BS", 32, unpackI32BS}, + {"I", "I;32N", 32, unpackI32N}, + {"I", "I;32NS", 32, unpackI32NS}, + + /* floating point variations */ + {"F", "F", 32, copy4}, + {"F", "F;8", 8, unpackF8}, + {"F", "F;8S", 8, unpackF8S}, + {"F", "F;16", 16, unpackF16}, + {"F", "F;16S", 16, unpackF16S}, + {"F", "F;16B", 16, unpackF16B}, + {"F", "F;16BS", 16, unpackF16BS}, + {"F", "F;16N", 16, unpackF16N}, + {"F", "F;16NS", 16, unpackF16NS}, + {"F", "F;32", 32, unpackF32}, + {"F", "F;32S", 32, unpackF32S}, + {"F", "F;32B", 32, unpackF32B}, + {"F", "F;32BS", 32, unpackF32BS}, + {"F", "F;32N", 32, unpackF32N}, + {"F", "F;32NS", 32, unpackF32NS}, + {"F", "F;32F", 32, unpackF32F}, + {"F", "F;32BF", 32, unpackF32BF}, + {"F", "F;32NF", 32, unpackF32NF}, +#ifdef FLOAT64 + {"F", "F;64F", 64, unpackF64F}, + {"F", "F;64BF", 64, unpackF64BF}, + {"F", "F;64NF", 64, unpackF64NF}, +#endif + + /* storage modes */ + {"I;16", "I;16", 16, copy2}, + {"I;16B", "I;16B", 16, copy2}, + {"I;16L", "I;16L", 16, copy2}, + + {NULL} /* sentinel */ +}; + + +ImagingShuffler +ImagingFindUnpacker(const char* mode, const char* rawmode, int* bits_out) +{ + int i; + + /* find a suitable pixel unpacker */ + for (i = 0; unpackers[i].rawmode; i++) + if (strcmp(unpackers[i].mode, mode) == 0 && + strcmp(unpackers[i].rawmode, rawmode) == 0) { + if (bits_out) + *bits_out = unpackers[i].bits; + return unpackers[i].unpack; + } + + /* FIXME: configure a general unpacker based on the type codes... */ + + return NULL; +} diff --git a/libImaging/UnpackYCC.c b/libImaging/UnpackYCC.c new file mode 100644 index 000000000..19da1f654 --- /dev/null +++ b/libImaging/UnpackYCC.c @@ -0,0 +1,162 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * code to convert and unpack PhotoYCC data + * + * history: + * 97-01-25 fl Moved from PcdDecode.c + * + * Copyright (c) Fredrik Lundh 1996-97. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +/* Tables generated by pcdtables.py, based on transforms taken from + the "Colour Space Conversions FAQ" by Roberts/Ford. */ + +static INT16 L[] = { 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 16, 18, +19, 20, 22, 23, 24, 26, 27, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, +42, 43, 45, 46, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 62, 64, +65, 67, 68, 69, 71, 72, 73, 75, 76, 77, 79, 80, 82, 83, 84, 86, 87, +88, 90, 91, 92, 94, 95, 96, 98, 99, 101, 102, 103, 105, 106, 107, 109, +110, 111, 113, 114, 115, 117, 118, 120, 121, 122, 124, 125, 126, 128, +129, 130, 132, 133, 134, 136, 137, 139, 140, 141, 143, 144, 145, 147, +148, 149, 151, 152, 153, 155, 156, 158, 159, 160, 162, 163, 164, 166, +167, 168, 170, 171, 173, 174, 175, 177, 178, 179, 181, 182, 183, 185, +186, 187, 189, 190, 192, 193, 194, 196, 197, 198, 200, 201, 202, 204, +205, 206, 208, 209, 211, 212, 213, 215, 216, 217, 219, 220, 221, 223, +224, 225, 227, 228, 230, 231, 232, 234, 235, 236, 238, 239, 240, 242, +243, 245, 246, 247, 249, 250, 251, 253, 254, 255, 257, 258, 259, 261, +262, 264, 265, 266, 268, 269, 270, 272, 273, 274, 276, 277, 278, 280, +281, 283, 284, 285, 287, 288, 289, 291, 292, 293, 295, 296, 297, 299, +300, 302, 303, 304, 306, 307, 308, 310, 311, 312, 314, 315, 317, 318, +319, 321, 322, 323, 325, 326, 327, 329, 330, 331, 333, 334, 336, 337, +338, 340, 341, 342, 344, 345, 346 }; + +static INT16 CB[] = { -345, -343, -341, -338, -336, -334, -332, -329, +-327, -325, -323, -321, -318, -316, -314, -312, -310, -307, -305, +-303, -301, -298, -296, -294, -292, -290, -287, -285, -283, -281, +-278, -276, -274, -272, -270, -267, -265, -263, -261, -258, -256, +-254, -252, -250, -247, -245, -243, -241, -239, -236, -234, -232, +-230, -227, -225, -223, -221, -219, -216, -214, -212, -210, -207, +-205, -203, -201, -199, -196, -194, -192, -190, -188, -185, -183, +-181, -179, -176, -174, -172, -170, -168, -165, -163, -161, -159, +-156, -154, -152, -150, -148, -145, -143, -141, -139, -137, -134, +-132, -130, -128, -125, -123, -121, -119, -117, -114, -112, -110, +-108, -105, -103, -101, -99, -97, -94, -92, -90, -88, -85, -83, -81, +-79, -77, -74, -72, -70, -68, -66, -63, -61, -59, -57, -54, -52, -50, +-48, -46, -43, -41, -39, -37, -34, -32, -30, -28, -26, -23, -21, -19, +-17, -15, -12, -10, -8, -6, -3, -1, 0, 2, 4, 7, 9, 11, 13, 16, 18, 20, +22, 24, 27, 29, 31, 33, 35, 38, 40, 42, 44, 47, 49, 51, 53, 55, 58, +60, 62, 64, 67, 69, 71, 73, 75, 78, 80, 82, 84, 86, 89, 91, 93, 95, +98, 100, 102, 104, 106, 109, 111, 113, 115, 118, 120, 122, 124, 126, +129, 131, 133, 135, 138, 140, 142, 144, 146, 149, 151, 153, 155, 157, +160, 162, 164, 166, 169, 171, 173, 175, 177, 180, 182, 184, 186, 189, +191, 193, 195, 197, 200, 202, 204, 206, 208, 211, 213, 215, 217, 220 }; + +static INT16 GB[] = { 67, 67, 66, 66, 65, 65, 65, 64, 64, 63, 63, 62, +62, 62, 61, 61, 60, 60, 59, 59, 59, 58, 58, 57, 57, 56, 56, 56, 55, +55, 54, 54, 53, 53, 52, 52, 52, 51, 51, 50, 50, 49, 49, 49, 48, 48, +47, 47, 46, 46, 46, 45, 45, 44, 44, 43, 43, 43, 42, 42, 41, 41, 40, +40, 40, 39, 39, 38, 38, 37, 37, 37, 36, 36, 35, 35, 34, 34, 34, 33, +33, 32, 32, 31, 31, 31, 30, 30, 29, 29, 28, 28, 28, 27, 27, 26, 26, +25, 25, 25, 24, 24, 23, 23, 22, 22, 22, 21, 21, 20, 20, 19, 19, 19, +18, 18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, +11, 10, 10, 9, 9, 9, 8, 8, 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 3, 3, 2, 2, +1, 1, 0, 0, 0, 0, 0, -1, -1, -2, -2, -2, -3, -3, -4, -4, -5, -5, -5, +-6, -6, -7, -7, -8, -8, -8, -9, -9, -10, -10, -11, -11, -11, -12, -12, +-13, -13, -14, -14, -14, -15, -15, -16, -16, -17, -17, -18, -18, -18, +-19, -19, -20, -20, -21, -21, -21, -22, -22, -23, -23, -24, -24, -24, +-25, -25, -26, -26, -27, -27, -27, -28, -28, -29, -29, -30, -30, -30, +-31, -31, -32, -32, -33, -33, -33, -34, -34, -35, -35, -36, -36, -36, +-37, -37, -38, -38, -39, -39, -39, -40, -40, -41, -41, -42 }; + +static INT16 CR[] = { -249, -247, -245, -243, -241, -239, -238, -236, +-234, -232, -230, -229, -227, -225, -223, -221, -219, -218, -216, +-214, -212, -210, -208, -207, -205, -203, -201, -199, -198, -196, +-194, -192, -190, -188, -187, -185, -183, -181, -179, -178, -176, +-174, -172, -170, -168, -167, -165, -163, -161, -159, -157, -156, +-154, -152, -150, -148, -147, -145, -143, -141, -139, -137, -136, +-134, -132, -130, -128, -127, -125, -123, -121, -119, -117, -116, +-114, -112, -110, -108, -106, -105, -103, -101, -99, -97, -96, -94, +-92, -90, -88, -86, -85, -83, -81, -79, -77, -76, -74, -72, -70, -68, +-66, -65, -63, -61, -59, -57, -55, -54, -52, -50, -48, -46, -45, -43, +-41, -39, -37, -35, -34, -32, -30, -28, -26, -25, -23, -21, -19, -17, +-15, -14, -12, -10, -8, -6, -4, -3, -1, 0, 2, 4, 5, 7, 9, 11, 13, 15, +16, 18, 20, 22, 24, 26, 27, 29, 31, 33, 35, 36, 38, 40, 42, 44, 46, +47, 49, 51, 53, 55, 56, 58, 60, 62, 64, 66, 67, 69, 71, 73, 75, 77, +78, 80, 82, 84, 86, 87, 89, 91, 93, 95, 97, 98, 100, 102, 104, 106, +107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 129, 131, +133, 135, 137, 138, 140, 142, 144, 146, 148, 149, 151, 153, 155, 157, +158, 160, 162, 164, 166, 168, 169, 171, 173, 175, 177, 179, 180, 182, +184, 186, 188, 189, 191, 193, 195, 197, 199, 200, 202, 204, 206, 208, +209, 211, 213, 215 }; + +static INT16 GR[] = { 127, 126, 125, 124, 123, 122, 121, 121, 120, 119, +118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 108, 107, 106, +105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 95, 94, 93, 92, 91, +90, 89, 88, 87, 86, 85, 84, 83, 83, 82, 81, 80, 79, 78, 77, 76, 75, +74, 73, 72, 71, 70, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, +58, 57, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 45, 44, +43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 32, 31, 30, 29, 28, +27, 26, 25, 24, 23, 22, 21, 20, 19, 19, 18, 17, 16, 15, 14, 13, 12, +11, 10, 9, 8, 7, 6, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2, -3, -4, -5, -5, +-6, -7, -8, -9, -10, -11, -12, -13, -14, -15, -16, -17, -18, -18, -19, +-20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -31, -32, +-33, -34, -35, -36, -37, -38, -39, -40, -41, -42, -43, -44, -44, -45, +-46, -47, -48, -49, -50, -51, -52, -53, -54, -55, -56, -56, -57, -58, +-59, -60, -61, -62, -63, -64, -65, -66, -67, -68, -69, -69, -70, -71, +-72, -73, -74, -75, -76, -77, -78, -79, -80, -81, -82, -82, -83, -84, +-85, -86, -87, -88, -89, -90, -91, -92, -93, -94, -94, -95, -96, -97, +-98, -99, -100, -101, -102, -103, -104, -105, -106, -107, -107, -108 }; + +#define R 0 +#define G 1 +#define B 2 +#define A 3 + +#define YCC2RGB(rgb, y, cb, cr) {\ + int l = L[y];\ + int r = l + CR[cr];\ + int g = l + GR[cr] + GB[cb];\ + int b = l + CB[cb];\ + rgb[0] = (r <= 0) ? 0 : (r >= 255) ? 255 : r;\ + rgb[1] = (g <= 0) ? 0 : (g >= 255) ? 255 : g;\ + rgb[2] = (b <= 0) ? 0 : (b >= 255) ? 255 : b;\ +} + +void +ImagingUnpackYCC(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* PhotoYCC triplets */ + for (i = 0; i < pixels; i++) { + YCC2RGB(out, in[0], in[1], in[2]); + out[A] = 255; + out += 4; in += 3; + } +} + +void +ImagingUnpackYCCA(UINT8* out, const UINT8* in, int pixels) +{ + int i; + /* PhotoYCC triplets plus premultiplied alpha */ + for (i = 0; i < pixels; i++) { + /* Divide by alpha */ + UINT8 rgb[3]; + rgb[0] = (in[3] == 0) ? 0 : (((int) in[0] * 255) / in[3]); + rgb[1] = (in[3] == 0) ? 0 : (((int) in[1] * 255) / in[3]); + rgb[2] = (in[3] == 0) ? 0 : (((int) in[2] * 255) / in[3]); + /* Convert non-multiplied data to RGB */ + YCC2RGB(out, rgb[0], rgb[1], rgb[2]); + out[A] = in[3]; + out += 4; in += 4; + } +} diff --git a/libImaging/UnsharpMask.c b/libImaging/UnsharpMask.c new file mode 100644 index 000000000..231826245 --- /dev/null +++ b/libImaging/UnsharpMask.c @@ -0,0 +1,398 @@ +/* PILusm, a gaussian blur and unsharp masking library for PIL + By Kevin Cazabon, copyright 2003 + kevin_cazabon@hotmail.com + kevin@cazabon.com */ + +/* Originally released under LGPL. Graciously donated to PIL + for distribution under the standard PIL license in 2009." */ + +#include "Python.h" +#include "Imaging.h" + +#define PILUSMVERSION "0.6.1" + +/* version history + +0.6.1 converted to C and added to PIL 1.1.7 + +0.6.0 fixed/improved float radius support (oops!) + now that radius can be a float (properly), changed radius value to + be an actual radius (instead of diameter). So, you should get + similar results from PIL_usm as from other paint programs when + using the SAME values (no doubling of radius required any more). + Be careful, this may "break" software if you had it set for 2x + or 5x the radius as was recommended with earlier versions. + made PILusm thread-friendly (release GIL before lengthly operations, + and re-acquire it before returning to Python). This makes a huge + difference with multi-threaded applications on dual-processor + or "Hyperthreading"-enabled systems (Pentium4, Xeon, etc.) + +0.5.0 added support for float radius values! + +0.4.0 tweaked gaussian curve calculation to be closer to consistent shape + across a wide range of radius values + +0.3.0 changed deviation calculation in gausian algorithm to be dynamic + _gblur now adds 1 to the user-supplied radius before using it so + that a value of "0" returns the original image instead of a + black one. + fixed handling of alpha channel in RGBX, RGBA images + improved speed of gblur by reducing unnecessary checks and assignments + +0.2.0 fixed L-mode image support + +0.1.0 initial release + +*/ + +static inline UINT8 clip(double in) +{ + if (in >= 255.0) + return (UINT8) 255; + if (in <= 0.0) + return (UINT8) 0; + return (UINT8) in; +} + +static Imaging +gblur(Imaging im, Imaging imOut, float floatRadius, int channels, int padding) +{ + ImagingSectionCookie cookie; + + float *maskData = NULL; + int y = 0; + int x = 0; + float z = 0; + float sum = 0.0; + float dev = 0.0; + + float *buffer = NULL; + + int *line = NULL; + UINT8 *line8 = NULL; + + int pix = 0; + float newPixel[4]; + int channel = 0; + int offset = 0; + INT32 newPixelFinals; + + int radius = 0; + float remainder = 0.0; + + int i; + + /* Do the gaussian blur */ + + /* For a symmetrical gaussian blur, instead of doing a radius*radius + matrix lookup, you get the EXACT same results by doing a radius*1 + transform, followed by a 1*radius transform. This reduces the + number of lookups exponentially (10 lookups per pixel for a + radius of 5 instead of 25 lookups). So, we blur the lines first, + then we blur the resulting columns. */ + + /* first, round radius off to the next higher integer and hold the + remainder this is used so we can support float radius values + properly. */ + + remainder = floatRadius - ((int) floatRadius); + floatRadius = ceil(floatRadius); + + /* Next, double the radius and offset by 2.0... that way "0" returns + the original image instead of a black one. We multiply it by 2.0 + so that it is a true "radius", not a diameter (the results match + other paint programs closer that way too). */ + radius = (int) ((floatRadius * 2.0) + 2.0); + + /* create the maskData for the gaussian curve */ + maskData = malloc(radius * sizeof(float)); + /* FIXME: error checking */ + for (x = 0; x < radius; x++) { + z = ((float) (x + 2) / ((float) radius)); + dev = 0.5 + (((float) (radius * radius)) * 0.001); + /* you can adjust this factor to change the shape/center-weighting + of the gaussian */ + maskData[x] = (float) pow((1.0 / sqrt(2.0 * 3.14159265359 * dev)), + ((-(z - 1.0) * -(x - 1.0)) / + (2.0 * dev))); + } + + /* if there's any remainder, multiply the first/last values in + MaskData it. this allows us to support float radius values. */ + if (remainder > 0.0) { + maskData[0] *= remainder; + maskData[radius - 1] *= remainder; + } + + for (x = 0; x < radius; x++) { + /* this is done separately now due to the correction for float + radius values above */ + sum += maskData[x]; + } + + for (i = 0; i < radius; i++) { + maskData[i] *= (1.0 / sum); + /* printf("%f\n", maskData[i]); */ + } + + /* create a temporary memory buffer for the data for the first pass + memset the buffer to 0 so we can use it directly with += */ + + /* don't bother about alpha/padding */ + buffer = calloc((size_t) (im->xsize * im->ysize * channels), + sizeof(float)); + if (buffer == NULL) + return ImagingError_MemoryError(); + + /* be nice to other threads while you go off to lala land */ + ImagingSectionEnter(&cookie); + + /* memset(buffer, 0, sizeof(buffer)); */ + + newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; + + /* perform a blur on each line, and place in the temporary storage buffer */ + for (y = 0; y < im->ysize; y++) { + if (channels == 1 && im->image8 != NULL) { + line8 = (UINT8 *) im->image8[y]; + } else { + line = im->image32[y]; + } + for (x = 0; x < im->xsize; x++) { + newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; + /* for each neighbor pixel, factor in its value/weighting to the + current pixel */ + for (pix = 0; pix < radius; pix++) { + /* figure the offset of this neighbor pixel */ + offset = + (int) ((-((float) radius / 2.0) + (float) pix) + 0.5); + if (x + offset < 0) + offset = -x; + else if (x + offset >= im->xsize) + offset = im->xsize - x - 1; + + /* add (neighbor pixel value * maskData[pix]) to the current + pixel value */ + if (channels == 1) { + buffer[(y * im->xsize) + x] += + ((float) ((UINT8 *) & line8[x + offset])[0]) * + (maskData[pix]); + } else { + for (channel = 0; channel < channels; channel++) { + buffer[(y * im->xsize * channels) + + (x * channels) + channel] += + ((float) ((UINT8 *) & line[x + offset]) + [channel]) * (maskData[pix]); + } + } + } + } + } + + /* perform a blur on each column in the buffer, and place in the + output image */ + for (x = 0; x < im->xsize; x++) { + for (y = 0; y < im->ysize; y++) { + newPixel[0] = newPixel[1] = newPixel[2] = newPixel[3] = 0; + /* for each neighbor pixel, factor in its value/weighting to the + current pixel */ + for (pix = 0; pix < radius; pix++) { + /* figure the offset of this neighbor pixel */ + offset = + (int) (-((float) radius / 2.0) + (float) pix + 0.5); + if (y + offset < 0) + offset = -y; + else if (y + offset >= im->ysize) + offset = im->ysize - y - 1; + /* add (neighbor pixel value * maskData[pix]) to the current + pixel value */ + for (channel = 0; channel < channels; channel++) { + newPixel[channel] += + (buffer + [((y + offset) * im->xsize * channels) + + (x * channels) + channel]) * (maskData[pix]); + } + } + /* if the image is RGBX or RGBA, copy the 4th channel data to + newPixel, so it gets put in imOut */ + if (strcmp(im->mode, "RGBX") == 0 + || strcmp(im->mode, "RGBA") == 0) { + newPixel[3] = (float) ((UINT8 *) & line[x + offset])[3]; + } + + /* pack the channels into an INT32 so we can put them back in + the PIL image */ + newPixelFinals = 0; + if (channels == 1) { + newPixelFinals = clip(newPixel[0]); + } else { + /* for RGB, the fourth channel isn't used anyways, so just + pack a 0 in there, this saves checking the mode for each + pixel. */ + /* this doesn't work on little-endian machines... fix it! */ + newPixelFinals = + clip(newPixel[0]) | clip(newPixel[1]) << 8 | + clip(newPixel[2]) << 16 | clip(newPixel[3]) << 24; + } + /* set the resulting pixel in imOut */ + if (channels == 1) { + imOut->image8[y][x] = (UINT8) newPixelFinals; + } else { + imOut->image32[y][x] = newPixelFinals; + } + } + } + + /* free the buffer */ + free(buffer); + + /* get the GIL back so Python knows who you are */ + ImagingSectionLeave(&cookie); + + return imOut; +} + +Imaging ImagingGaussianBlur(Imaging im, Imaging imOut, float radius) +{ + int channels = 0; + int padding = 0; + + if (strcmp(im->mode, "RGB") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "RGBA") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "RGBX") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "CMYK") == 0) { + channels = 4; + padding = 0; + } else if (strcmp(im->mode, "L") == 0) { + channels = 1; + padding = 0; + } else + return ImagingError_ModeError(); + + return gblur(im, imOut, radius, channels, padding); +} + +Imaging +ImagingUnsharpMask(Imaging im, Imaging imOut, float radius, int percent, + int threshold) +{ + ImagingSectionCookie cookie; + + Imaging result; + int channel = 0; + int channels = 0; + int padding = 0; + + int x = 0; + int y = 0; + + int *lineIn = NULL; + int *lineOut = NULL; + UINT8 *lineIn8 = NULL; + UINT8 *lineOut8 = NULL; + + int diff = 0; + + INT32 newPixel = 0; + + if (strcmp(im->mode, "RGB") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "RGBA") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "RGBX") == 0) { + channels = 3; + padding = 1; + } else if (strcmp(im->mode, "CMYK") == 0) { + channels = 4; + padding = 0; + } else if (strcmp(im->mode, "L") == 0) { + channels = 1; + padding = 0; + } else + return ImagingError_ModeError(); + + /* first, do a gaussian blur on the image, putting results in imOut + temporarily */ + result = gblur(im, imOut, radius, channels, padding); + if (!result) + return NULL; + + /* now, go through each pixel, compare "normal" pixel to blurred + pixel. if the difference is more than threshold values, apply + the OPPOSITE correction to the amount of blur, multiplied by + percent. */ + + ImagingSectionEnter(&cookie); + + for (y = 0; y < im->ysize; y++) { + if (channels == 1) { + lineIn8 = im->image8[y]; + lineOut8 = imOut->image8[y]; + } else { + lineIn = im->image32[y]; + lineOut = imOut->image32[y]; + } + for (x = 0; x < im->xsize; x++) { + newPixel = 0; + /* compare in/out pixels, apply sharpening */ + if (channels == 1) { + diff = + ((UINT8 *) & lineIn8[x])[0] - + ((UINT8 *) & lineOut8[x])[0]; + if (abs(diff) > threshold) { + /* add the diff*percent to the original pixel */ + imOut->image8[y][x] = + clip((((UINT8 *) & lineIn8[x])[0]) + + (diff * ((float) percent) / 100.0)); + } else { + /* newPixel is the same as imIn */ + imOut->image8[y][x] = ((UINT8 *) & lineIn8[x])[0]; + } + } + + else { + for (channel = 0; channel < channels; channel++) { + diff = (int) ((((UINT8 *) & lineIn[x])[channel]) - + (((UINT8 *) & lineOut[x])[channel])); + if (abs(diff) > threshold) { + /* add the diff*percent to the original pixel + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | + clip((float) (((UINT8 *) & lineIn[x])[channel]) + + + (diff * + (((float) percent / + 100.0)))) << (channel * 8); + } else { + /* newPixel is the same as imIn + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | ((UINT8 *) & lineIn[x])[channel] << + (channel * 8); + } + } + if (strcmp(im->mode, "RGBX") == 0 + || strcmp(im->mode, "RGBA") == 0) { + /* preserve the alpha channel + this may not work for little-endian systems, fix it! */ + newPixel = + newPixel | ((UINT8 *) & lineIn[x])[channel] << 24; + } + imOut->image32[y][x] = newPixel; + } + } + } + + ImagingSectionLeave(&cookie); + + return imOut; +} diff --git a/libImaging/XbmDecode.c b/libImaging/XbmDecode.c new file mode 100644 index 000000000..8a203841b --- /dev/null +++ b/libImaging/XbmDecode.c @@ -0,0 +1,81 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for XBM hex image data + * + * history: + * 96-04-13 fl Created + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#define HEX(v) ((v >= '0' && v <= '9') ? v - '0' :\ + (v >= 'a' && v <= 'f') ? v - 'a' + 10 :\ + (v >= 'A' && v <= 'F') ? v - 'A' + 10 : 0) + +int +ImagingXbmDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + enum { BYTE = 1, SKIP }; + + UINT8* ptr; + + if (!state->state) + state->state = SKIP; + + ptr = buf; + + for (;;) { + + if (state->state == SKIP) { + + /* Skip forward until next 'x' */ + + while (bytes > 0) { + if (*ptr == 'x') + break; + ptr++; + bytes--; + } + + if (bytes == 0) + return ptr - buf; + + state->state = BYTE; + + } + + if (bytes < 3) + return ptr - buf; + + state->buffer[state->x] = (HEX(ptr[1])<<4) + HEX(ptr[2]); + + if (++state->x >= state->bytes) { + + /* Got a full line, unpack it */ + state->shuffle((UINT8*) im->image[state->y], state->buffer, + state->xsize); + + state->x = 0; + + if (++state->y >= state->ysize) { + /* End of file (errcode = 0) */ + return -1; + } + } + + ptr += 3; + bytes -= 3; + + state->state = SKIP; + + } + +} diff --git a/libImaging/XbmEncode.c b/libImaging/XbmEncode.c new file mode 100644 index 000000000..e066fd6b5 --- /dev/null +++ b/libImaging/XbmEncode.c @@ -0,0 +1,106 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * encoder for Xbm data + * + * history: + * 96-11-01 fl created + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + + +int +ImagingXbmEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + const char *hex = "0123456789abcdef"; + + UINT8* ptr = buf; + int i, n; + + if (!state->state) { + + /* 8 pixels are stored in no more than 6 bytes */ + state->bytes = 6*(state->xsize+7)/8; + + state->state = 1; + + } + + if (bytes < state->bytes) { + state->errcode = IMAGING_CODEC_MEMORY; + return 0; + } + + ptr = buf; + + while (bytes >= state->bytes) { + + state->shuffle(state->buffer, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, state->xsize); + + if (state->y < state->ysize-1) { + + /* any line but the last */ + for (n = 0; n < state->xsize; n += 8) { + + i = state->buffer[n/8]; + + *ptr++ = '0'; + *ptr++ = 'x'; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; + *ptr++ = ','; + bytes -= 5; + + if (++state->count >= 79/5) { + *ptr++ = '\n'; + bytes--; + state->count = 0; + } + + } + + state->y++; + + } else { + + /* last line */ + for (n = 0; n < state->xsize; n += 8) { + + i = state->buffer[n/8]; + + *ptr++ = '0'; + *ptr++ = 'x'; + *ptr++ = hex[(i>>4)&15]; + *ptr++ = hex[i&15]; + + if (n < state->xsize-8) { + *ptr++ = ','; + if (++state->count >= 79/5) { + *ptr++ = '\n'; + bytes--; + state->count = 0; + } + } else + *ptr++ = '\n'; + + bytes -= 5; + + } + + state->errcode = IMAGING_CODEC_END; + break; + } + } + + return ptr - buf; +} diff --git a/libImaging/Zip.h b/libImaging/Zip.h new file mode 100644 index 000000000..d961407e3 --- /dev/null +++ b/libImaging/Zip.h @@ -0,0 +1,57 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * declarations for the ZIP codecs + * + * Copyright (c) Fredrik Lundh 1996. + */ + + +#include "zlib.h" + + +/* modes */ +#define ZIP_PNG 0 /* continuous, filtered image data */ +#define ZIP_PNG_PALETTE 1 /* non-continuous data, disable filtering */ +#define ZIP_TIFF_PREDICTOR 2 /* TIFF, with predictor */ +#define ZIP_TIFF 3 /* TIFF, without predictor */ + + +typedef struct { + + /* CONFIGURATION */ + + /* Codec mode */ + int mode; + + /* Optimize (max compression) SLOW!!! */ + int optimize; + + /* Predefined dictionary (experimental) */ + char* dictionary; + int dictionary_size; + + /* PRIVATE CONTEXT (set by decoder/encoder) */ + + z_stream z_stream; /* (de)compression stream */ + + UINT8* previous; /* previous line (allocated) */ + + int last_output; /* # bytes last output by inflate */ + + /* Compressor specific stuff */ + UINT8* prior; /* filter storage (allocated) */ + UINT8* up; + UINT8* average; + UINT8* paeth; + + UINT8* output; /* output data */ + + int prefix; /* size of filter prefix (0 for TIFF data) */ + + int interlaced; /* is the image interlaced? (PNG) */ + + int pass; /* current pass of the interlaced image (PNG) */ + +} ZIPSTATE; diff --git a/libImaging/ZipDecode.c b/libImaging/ZipDecode.c new file mode 100644 index 000000000..08cdc7189 --- /dev/null +++ b/libImaging/ZipDecode.c @@ -0,0 +1,271 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * decoder for ZIP (deflated) image data. + * + * history: + * 1996-12-14 fl Created (for PNG) + * 1997-01-15 fl Prepared to read TIFF/ZIP + * 2001-11-19 fl PNG incomplete read patch (from Bernhard Herzog) + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997-2001. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#ifdef HAVE_LIBZ + +#include "Zip.h" + +static const int OFFSET[] = { 7, 3, 3, 1, 1, 0, 0 }; +static const int STARTING_COL[] = { 0, 4, 0, 2, 0, 1, 0 }; +static const int STARTING_ROW[] = { 0, 0, 4, 0, 2, 0, 1 }; +static const int COL_INCREMENT[] = { 8, 8, 4, 4, 2, 2, 1 }; +static const int ROW_INCREMENT[] = { 8, 8, 8, 4, 4, 2, 2 }; + +/* Get the length in bytes of a scanline in the pass specified, + * for interlaced images */ +static int get_row_len(ImagingCodecState state, int pass) +{ + int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass]; + return ((row_len * state->bits) + 7) / 8; +} + +/* -------------------------------------------------------------------- */ +/* Decoder */ +/* -------------------------------------------------------------------- */ + +int +ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + ZIPSTATE* context = (ZIPSTATE*) state->context; + int err; + int n; + UINT8* ptr; + int i, bpp; + int row_len; + + if (!state->state) { + + /* Initialization */ + if (context->mode == ZIP_PNG || context->mode == ZIP_PNG_PALETTE) + context->prefix = 1; /* PNG */ + + /* Expand standard buffer to make room for the (optional) filter + prefix, and allocate a buffer to hold the previous line */ + free(state->buffer); + state->buffer = (UINT8*) malloc(state->bytes+1); + context->previous = (UINT8*) malloc(state->bytes+1); + if (!state->buffer || !context->previous) { + state->errcode = IMAGING_CODEC_MEMORY; + return -1; + } + + context->last_output = 0; + + /* Initialize to black */ + memset(context->previous, 0, state->bytes+1); + + /* Setup decompression context */ + context->z_stream.zalloc = (alloc_func) NULL; + context->z_stream.zfree = (free_func) NULL; + context->z_stream.opaque = (voidpf) NULL; + + err = inflateInit(&context->z_stream); + if (err < 0) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + if (context->interlaced) { + context->pass = 0; + state->y = STARTING_ROW[context->pass]; + } + + /* Ready to decode */ + state->state = 1; + + } + + if (context->interlaced) { + row_len = get_row_len(state, context->pass); + } else { + row_len = state->bytes; + } + + /* Setup the source buffer */ + context->z_stream.next_in = buf; + context->z_stream.avail_in = bytes; + + /* Decompress what we've got this far */ + while (context->z_stream.avail_in > 0) { + + context->z_stream.next_out = state->buffer + context->last_output; + context->z_stream.avail_out = + row_len + context->prefix - context->last_output; + + err = inflate(&context->z_stream, Z_NO_FLUSH); + + if (err < 0) { + /* Something went wrong inside the compression library */ + if (err == Z_DATA_ERROR) + state->errcode = IMAGING_CODEC_BROKEN; + else if (err == Z_MEM_ERROR) + state->errcode = IMAGING_CODEC_MEMORY; + else + state->errcode = IMAGING_CODEC_CONFIG; + free(context->previous); + inflateEnd(&context->z_stream); + return -1; + } + + n = row_len + context->prefix - context->z_stream.avail_out; + + if (n < row_len + context->prefix) { + context->last_output = n; + break; /* need more input data */ + } + + /* Apply predictor */ + switch (context->mode) { + case ZIP_PNG: + switch (state->buffer[0]) { + case 0: + break; + case 1: + /* prior */ + bpp = (state->bits + 7) / 8; + for (i = bpp+1; i <= row_len; i++) + state->buffer[i] += state->buffer[i-bpp]; + break; + case 2: + /* up */ + for (i = 1; i <= row_len; i++) + state->buffer[i] += context->previous[i]; + break; + case 3: + /* average */ + bpp = (state->bits + 7) / 8; + for (i = 1; i <= bpp; i++) + state->buffer[i] += context->previous[i]/2; + for (; i <= row_len; i++) + state->buffer[i] += + (state->buffer[i-bpp] + context->previous[i])/2; + break; + case 4: + /* paeth filtering */ + bpp = (state->bits + 7) / 8; + for (i = 1; i <= bpp; i++) + state->buffer[i] += context->previous[i]; + for (; i <= row_len; i++) { + int a, b, c; + int pa, pb, pc; + + /* fetch pixels */ + a = state->buffer[i-bpp]; + b = context->previous[i]; + c = context->previous[i-bpp]; + + /* distances to surrounding pixels */ + pa = abs(b - c); + pb = abs(a - c); + pc = abs(a + b - 2*c); + + /* pick predictor with the shortest distance */ + state->buffer[i] += + (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; + + } + break; + default: + state->errcode = IMAGING_CODEC_UNKNOWN; + free(context->previous); + inflateEnd(&context->z_stream); + return -1; + } + break; + case ZIP_TIFF_PREDICTOR: + bpp = (state->bits + 7) / 8; + for (i = bpp+1; i <= row_len; i++) + state->buffer[i] += state->buffer[i-bpp]; + break; + } + + /* Stuff data into the image */ + if (context->interlaced) { + int col = STARTING_COL[context->pass]; + if (state->bits >= 8) { + /* Stuff pixels in their correct location, one by one */ + for (i = 0; i < row_len; i += ((state->bits + 7) / 8)) { + state->shuffle((UINT8*) im->image[state->y] + + col * im->pixelsize, + state->buffer + context->prefix + i, 1); + col += COL_INCREMENT[context->pass]; + } + } else { + /* Handle case with more than a pixel in each byte */ + int row_bits = ((state->xsize + OFFSET[context->pass]) + / COL_INCREMENT[context->pass]) * state->bits; + for (i = 0; i < row_bits; i += state->bits) { + UINT8 byte = *(state->buffer + context->prefix + (i / 8)); + byte <<= (i % 8); + state->shuffle((UINT8*) im->image[state->y] + + col * im->pixelsize, &byte, 1); + col += COL_INCREMENT[context->pass]; + } + } + /* Find next valid scanline */ + state->y += ROW_INCREMENT[context->pass]; + while (state->y >= state->ysize || row_len <= 0) { + context->pass++; + if (context->pass == 7) { + /* Force exit below */ + state->y = state->ysize; + break; + } + state->y = STARTING_ROW[context->pass]; + row_len = get_row_len(state, context->pass); + /* Since we're moving to the "first" line, the previous line + * should be black to make filters work corectly */ + memset(state->buffer, 0, state->bytes+1); + } + } else { + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->buffer + context->prefix, + state->xsize); + state->y++; + } + + /* all inflate output has been consumed */ + context->last_output = 0; + + if (state->y >= state->ysize || err == Z_STREAM_END) { + + /* The image and the data should end simultaneously */ + /* if (state->y < state->ysize || err != Z_STREAM_END) + state->errcode = IMAGING_CODEC_BROKEN; */ + + free(context->previous); + inflateEnd(&context->z_stream); + return -1; /* end of file (errcode=0) */ + + } + + /* Swap buffer pointers */ + ptr = state->buffer; + state->buffer = context->previous; + context->previous = ptr; + + } + + return bytes; /* consumed all of it */ + +} + +#endif diff --git a/libImaging/ZipEncode.c b/libImaging/ZipEncode.c new file mode 100644 index 000000000..19b2b7787 --- /dev/null +++ b/libImaging/ZipEncode.c @@ -0,0 +1,341 @@ +/* + * The Python Imaging Library. + * $Id$ + * + * coder for ZIP (deflated) image data + * + * History: + * 96-12-29 fl created + * 96-12-30 fl adaptive filter selection, encoder tuning + * + * Copyright (c) Fredrik Lundh 1996. + * Copyright (c) Secret Labs AB 1997. + * + * See the README file for information on usage and redistribution. + */ + + +#include "Imaging.h" + +#ifdef HAVE_LIBZ + +#include "Zip.h" + +int +ImagingZipEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) +{ + ZIPSTATE* context = (ZIPSTATE*) state->context; + int err; + UINT8* ptr; + int i, bpp, s, sum; + ImagingSectionCookie cookie; + + if (!state->state) { + + /* Initialization */ + + /* Valid modes are ZIP_PNG, ZIP_PNG_PALETTE, and ZIP_TIFF */ + + /* Expand standard buffer to make room for the filter selector, + and allocate filter buffers */ + free(state->buffer); + state->buffer = (UINT8*) malloc(state->bytes+1); + context->previous = (UINT8*) malloc(state->bytes+1); + context->prior = (UINT8*) malloc(state->bytes+1); + context->up = (UINT8*) malloc(state->bytes+1); + context->average = (UINT8*) malloc(state->bytes+1); + context->paeth = (UINT8*) malloc(state->bytes+1); + if (!state->buffer || !context->previous || !context->prior || + !context->up || !context->average || !context->paeth) { + free(context->paeth); + free(context->average); + free(context->up); + free(context->prior); + free(context->previous); + state->errcode = IMAGING_CODEC_MEMORY; + return -1; + } + + /* Initalise filter buffers */ + state->buffer[0] = 0; + context->prior[0] = 1; + context->up[0] = 2; + context->average[0] = 3; + context->paeth[0] = 4; + + /* Initialise previous buffer to black */ + memset(context->previous, 0, state->bytes+1); + + /* Setup compression context */ + context->z_stream.zalloc = (alloc_func)0; + context->z_stream.zfree = (free_func)0; + context->z_stream.opaque = (voidpf)0; + context->z_stream.next_in = 0; + context->z_stream.avail_in = 0; + + err = deflateInit2(&context->z_stream, + /* compression level */ + (context->optimize) ? Z_BEST_COMPRESSION + : Z_DEFAULT_COMPRESSION, + /* compression method */ + Z_DEFLATED, + /* compression memory resources */ + 15, 9, + /* compression strategy (image data are filtered)*/ + (context->mode == ZIP_PNG) ? Z_FILTERED + : Z_DEFAULT_STRATEGY); + if (err < 0) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + + if (context->dictionary && context->dictionary_size > 0) { + err = deflateSetDictionary(&context->z_stream, (unsigned char *)context->dictionary, + context->dictionary_size); + if (err < 0) { + state->errcode = IMAGING_CODEC_CONFIG; + return -1; + } + } + + /* Ready to decode */ + state->state = 1; + + } + + /* Setup the destination buffer */ + context->z_stream.next_out = buf; + context->z_stream.avail_out = bytes; + if (context->z_stream.next_in && context->z_stream.avail_in > 0) { + /* We have some data from previous round, deflate it first */ + err = deflate(&context->z_stream, Z_NO_FLUSH); + + if (err < 0) { + /* Something went wrong inside the compression library */ + if (err == Z_DATA_ERROR) + state->errcode = IMAGING_CODEC_BROKEN; + else if (err == Z_MEM_ERROR) + state->errcode = IMAGING_CODEC_MEMORY; + else + state->errcode = IMAGING_CODEC_CONFIG; + free(context->paeth); + free(context->average); + free(context->up); + free(context->prior); + free(context->previous); + deflateEnd(&context->z_stream); + return -1; + } + } + + ImagingSectionEnter(&cookie); + for (;;) { + + switch (state->state) { + + case 1: + + /* Compress image data */ + while (context->z_stream.avail_out > 0) { + + if (state->y >= state->ysize) { + /* End of image; now flush compressor buffers */ + state->state = 2; + break; + + } + + /* Stuff image data into the compressor */ + state->shuffle(state->buffer+1, + (UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->xsize); + + state->y++; + + context->output = state->buffer; + + if (context->mode == ZIP_PNG) { + + /* Filter the image data. For each line, select + the filter that gives the least total distance + from zero for the filtered data (taken from + LIBPNG) */ + + bpp = (state->bits + 7) / 8; + + /* 0. No filter */ + for (i = 1, sum = 0; i <= state->bytes; i++) { + UINT8 v = state->buffer[i]; + sum += (v < 128) ? v : 256 - v; + } + + /* 2. Up. We'll test this first to save time when + an image line is identical to the one above. */ + if (sum > 0) { + for (i = 1, s = 0; i <= state->bytes; i++) { + UINT8 v = state->buffer[i] - context->previous[i]; + context->up[i] = v; + s += (v < 128) ? v : 256 - v; + } + if (s < sum) { + context->output = context->up; + sum = s; /* 0 if line was duplicated */ + } + } + + /* 1. Prior */ + if (sum > 0) { + for (i = 1, s = 0; i <= bpp; i++) { + UINT8 v = state->buffer[i]; + context->prior[i] = v; + s += (v < 128) ? v : 256 - v; + } + for (; i <= state->bytes; i++) { + UINT8 v = state->buffer[i] - state->buffer[i-bpp]; + context->prior[i] = v; + s += (v < 128) ? v : 256 - v; + } + if (s < sum) { + context->output = context->prior; + sum = s; /* 0 if line is solid */ + } + } + + /* 3. Average (not very common in real-life images, + so its only used with the optimize option) */ + if (context->optimize && sum > 0) { + for (i = 1, s = 0; i <= bpp; i++) { + UINT8 v = state->buffer[i] - context->previous[i]/2; + context->average[i] = v; + s += (v < 128) ? v : 256 - v; + } + for (; i <= state->bytes; i++) { + UINT8 v = state->buffer[i] - + (state->buffer[i-bpp] + context->previous[i])/2; + context->average[i] = v; + s += (v < 128) ? v : 256 - v; + } + if (s < sum) { + context->output = context->average; + sum = s; + } + } + + /* 4. Paeth */ + if (sum > 0) { + for (i = 1, s = 0; i <= bpp; i++) { + UINT8 v = state->buffer[i] - context->previous[i]; + context->paeth[i] = v; + s += (v < 128) ? v : 256 - v; + } + for (; i <= state->bytes; i++) { + UINT8 v; + int a, b, c; + int pa, pb, pc; + + /* fetch pixels */ + a = state->buffer[i-bpp]; + b = context->previous[i]; + c = context->previous[i-bpp]; + + /* distances to surrounding pixels */ + pa = abs(b - c); + pb = abs(a - c); + pc = abs(a + b - 2*c); + + /* pick predictor with the shortest distance */ + v = state->buffer[i] - + ((pa <= pb && pa <= pc) ? a : + (pb <= pc) ? b : c); + context->paeth[i] = v; + s += (v < 128) ? v : 256 - v; + } + if (s < sum) { + context->output = context->paeth; + sum = s; + } + } + } + + /* Compress this line */ + context->z_stream.next_in = context->output; + context->z_stream.avail_in = state->bytes+1; + + err = deflate(&context->z_stream, Z_NO_FLUSH); + + if (err < 0) { + /* Something went wrong inside the compression library */ + if (err == Z_DATA_ERROR) + state->errcode = IMAGING_CODEC_BROKEN; + else if (err == Z_MEM_ERROR) + state->errcode = IMAGING_CODEC_MEMORY; + else + state->errcode = IMAGING_CODEC_CONFIG; + free(context->paeth); + free(context->average); + free(context->up); + free(context->prior); + free(context->previous); + deflateEnd(&context->z_stream); + ImagingSectionLeave(&cookie); + return -1; + } + + /* Swap buffer pointers */ + ptr = state->buffer; + state->buffer = context->previous; + context->previous = ptr; + + } + + if (context->z_stream.avail_out == 0) + break; /* Buffer full */ + + case 2: + + /* End of image data; flush compressor buffers */ + + while (context->z_stream.avail_out > 0) { + + err = deflate(&context->z_stream, Z_FINISH); + + if (err == Z_STREAM_END) { + + free(context->paeth); + free(context->average); + free(context->up); + free(context->prior); + free(context->previous); + + deflateEnd(&context->z_stream); + + state->errcode = IMAGING_CODEC_END; + + break; + } + + if (context->z_stream.avail_out == 0) + break; /* Buffer full */ + + } + + } + ImagingSectionLeave(&cookie); + return bytes - context->z_stream.avail_out; + + } + + /* Should never ever arrive here... */ + state->errcode = IMAGING_CODEC_CONFIG; + ImagingSectionLeave(&cookie); + return -1; +} + +const char* +ImagingZipVersion(void) +{ + return ZLIB_VERSION; +} + +#endif diff --git a/map.c b/map.c new file mode 100644 index 000000000..f15b8200f --- /dev/null +++ b/map.c @@ -0,0 +1,379 @@ +/* + * The Python Imaging Library. + * + * standard memory mapping interface for the Imaging library + * + * history: + * 1998-03-05 fl added Win32 read mapping + * 1999-02-06 fl added "I;16" support + * 2003-04-21 fl added PyImaging_MapBuffer primitive + * + * Copyright (c) 1998-2003 by Secret Labs AB. + * Copyright (c) 2003 by Fredrik Lundh. + * + * See the README file for information on usage and redistribution. + */ + +/* + * FIXME: should move the memory mapping primitives into libImaging! + */ + +#include "Python.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#include "Imaging.h" + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#undef INT32 +#undef INT64 +#undef UINT32 +#include "windows.h" +#endif + +/* compatibility wrappers (defined in _imaging.c) */ +extern int PyImaging_CheckBuffer(PyObject* buffer); +extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); + +/* -------------------------------------------------------------------- */ +/* Standard mapper */ + +typedef struct { + PyObject_HEAD + char* base; + int size; + int offset; +#ifdef WIN32 + HANDLE hFile; + HANDLE hMap; +#endif +} ImagingMapperObject; + +staticforward PyTypeObject ImagingMapperType; + +ImagingMapperObject* +PyImaging_MapperNew(const char* filename, int readonly) +{ + ImagingMapperObject *mapper; + + ImagingMapperType.ob_type = &PyType_Type; + + mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType); + if (mapper == NULL) + return NULL; + + mapper->base = NULL; + mapper->size = mapper->offset = 0; + +#ifdef WIN32 + mapper->hFile = (HANDLE)-1; + mapper->hMap = (HANDLE)-1; + + /* FIXME: currently supports readonly mappings only */ + mapper->hFile = CreateFile( + filename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (mapper->hFile == (HANDLE)-1) { + PyErr_SetString(PyExc_IOError, "cannot open file"); + PyObject_Del(mapper); + return NULL; + } + + mapper->hMap = CreateFileMapping( + mapper->hFile, NULL, + PAGE_READONLY, + 0, 0, NULL); + if (mapper->hMap == (HANDLE)-1) { + CloseHandle(mapper->hFile); + PyErr_SetString(PyExc_IOError, "cannot map file"); + PyObject_Del(mapper); + return NULL; + } + + mapper->base = (char*) MapViewOfFile( + mapper->hMap, + FILE_MAP_READ, + 0, 0, 0); + + mapper->size = GetFileSize(mapper->hFile, 0); +#endif + + return mapper; +} + +static void +mapping_dealloc(ImagingMapperObject* mapper) +{ +#ifdef WIN32 + if (mapper->base != 0) + UnmapViewOfFile(mapper->base); + if (mapper->hMap != (HANDLE)-1) + CloseHandle(mapper->hMap); + if (mapper->hFile != (HANDLE)-1) + CloseHandle(mapper->hFile); + mapper->base = 0; + mapper->hMap = mapper->hFile = (HANDLE)-1; +#endif + PyObject_Del(mapper); +} + +/* -------------------------------------------------------------------- */ +/* standard file operations */ + +static PyObject* +mapping_read(ImagingMapperObject* mapper, PyObject* args) +{ + PyObject* buf; + + int size = -1; + if (!PyArg_ParseTuple(args, "|i", &size)) + return NULL; + + /* check size */ + if (size < 0 || mapper->offset + size > mapper->size) + size = mapper->size - mapper->offset; + if (size < 0) + size = 0; + + buf = PyString_FromStringAndSize(NULL, size); + if (!buf) + return NULL; + + if (size > 0) { + memcpy(PyString_AsString(buf), mapper->base + mapper->offset, size); + mapper->offset += size; + } + + return buf; +} + +static PyObject* +mapping_seek(ImagingMapperObject* mapper, PyObject* args) +{ + int offset; + int whence = 0; + if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) + return NULL; + + switch (whence) { + case 0: /* SEEK_SET */ + mapper->offset = offset; + break; + case 1: /* SEEK_CUR */ + mapper->offset += offset; + break; + case 2: /* SEEK_END */ + mapper->offset = mapper->size + offset; + break; + default: + /* FIXME: raise ValueError? */ + break; + } + + Py_INCREF(Py_None); + return Py_None; +} + +/* -------------------------------------------------------------------- */ +/* map entire image */ + +extern PyObject*PyImagingNew(Imaging im); + +static void +ImagingDestroyMap(Imaging im) +{ + return; /* nothing to do! */ +} + +static PyObject* +mapping_readimage(ImagingMapperObject* mapper, PyObject* args) +{ + int y, size; + Imaging im; + + char* mode; + int xsize; + int ysize; + int stride; + int orientation; + if (!PyArg_ParseTuple(args, "s(ii)ii", &mode, &xsize, &ysize, + &stride, &orientation)) + return NULL; + + if (stride <= 0) { + /* FIXME: maybe we should call ImagingNewPrologue instead */ + if (!strcmp(mode, "L") || !strcmp(mode, "P")) + stride = xsize; + else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B")) + stride = xsize * 2; + else + stride = xsize * 4; + } + + size = ysize * stride; + + if (mapper->offset + size > mapper->size) { + PyErr_SetString(PyExc_IOError, "image file truncated"); + return NULL; + } + + im = ImagingNewPrologue(mode, xsize, ysize); + if (!im) + return NULL; + + /* setup file pointers */ + if (orientation > 0) + for (y = 0; y < ysize; y++) + im->image[y] = mapper->base + mapper->offset + y * stride; + else + for (y = 0; y < ysize; y++) + im->image[ysize-y-1] = mapper->base + mapper->offset + y * stride; + + im->destroy = ImagingDestroyMap; + + if (!ImagingNewEpilogue(im)) + return NULL; + + mapper->offset += size; + + return PyImagingNew(im); +} + +static struct PyMethodDef methods[] = { + /* standard file interface */ + {"read", (PyCFunction)mapping_read, 1}, + {"seek", (PyCFunction)mapping_seek, 1}, + /* extensions */ + {"readimage", (PyCFunction)mapping_readimage, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +mapping_getattr(ImagingMapperObject* self, char* name) +{ + return Py_FindMethod(methods, (PyObject*) self, name); +} + +statichere PyTypeObject ImagingMapperType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "ImagingMapper", /*tp_name*/ + sizeof(ImagingMapperObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)mapping_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)mapping_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_hash*/ +}; + +PyObject* +PyImaging_Mapper(PyObject* self, PyObject* args) +{ + char* filename; + if (!PyArg_ParseTuple(args, "s", &filename)) + return NULL; + + return (PyObject*) PyImaging_MapperNew(filename, 1); +} + +/* -------------------------------------------------------------------- */ +/* Buffer mapper */ + +typedef struct ImagingBufferInstance { + struct ImagingMemoryInstance im; + PyObject* target; +} ImagingBufferInstance; + +static void +mapping_destroy_buffer(Imaging im) +{ + ImagingBufferInstance* buffer = (ImagingBufferInstance*) im; + + Py_XDECREF(buffer->target); +} + +PyObject* +PyImaging_MapBuffer(PyObject* self, PyObject* args) +{ + int y, size; + Imaging im; + char* ptr; + int bytes; + + PyObject* target; + char* mode; + char* codec; + PyObject* bbox; + int offset; + int xsize, ysize; + int stride; + int ystep; + + if (!PyArg_ParseTuple(args, "O(ii)sOi(sii)", &target, &xsize, &ysize, + &codec, &bbox, &offset, &mode, &stride, &ystep)) + return NULL; + + if (!PyImaging_CheckBuffer(target)) { + PyErr_SetString(PyExc_TypeError, "expected string or buffer"); + return NULL; + } + + if (stride <= 0) { + if (!strcmp(mode, "L") || !strcmp(mode, "P")) + stride = xsize; + else if (!strncmp(mode, "I;16", 4)) + stride = xsize * 2; + else + stride = xsize * 4; + } + + size = ysize * stride; + + /* check buffer size */ + bytes = PyImaging_ReadBuffer(target, (const void**) &ptr); + if (bytes < 0) { + PyErr_SetString(PyExc_ValueError, "buffer has negative size"); + return NULL; + } + if (offset + size > bytes) { + PyErr_SetString(PyExc_ValueError, "buffer is not large enough"); + return NULL; + } + + im = ImagingNewPrologueSubtype( + mode, xsize, ysize, sizeof(ImagingBufferInstance) + ); + if (!im) + return NULL; + + /* setup file pointers */ + if (ystep > 0) + for (y = 0; y < ysize; y++) + im->image[y] = ptr + offset + y * stride; + else + for (y = 0; y < ysize; y++) + im->image[ysize-y-1] = ptr + offset + y * stride; + + im->destroy = mapping_destroy_buffer; + + Py_INCREF(target); + ((ImagingBufferInstance*) im)->target = target; + + if (!ImagingNewEpilogue(im)) + return NULL; + + return PyImagingNew(im); +} + diff --git a/outline.c b/outline.c new file mode 100644 index 000000000..6bb2ebb54 --- /dev/null +++ b/outline.c @@ -0,0 +1,179 @@ +/* + * THIS IS WORK IN PROGRESS. + * + * The Python Imaging Library. + * + * "arrow" outline stuff. the contents of this module + * will be merged with the path module and the rest of + * the arrow graphics package, but not before PIL 1.1. + * use at your own risk. + * + * history: + * 99-01-10 fl Added to PIL (experimental) + * + * Copyright (c) Secret Labs AB 1999. + * Copyright (c) Fredrik Lundh 1999. + * + * See the README file for information on usage and redistribution. + */ + +#include "Python.h" + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#include "Imaging.h" + + +/* -------------------------------------------------------------------- */ +/* Class */ + +typedef struct { + PyObject_HEAD + ImagingOutline outline; +} OutlineObject; + +staticforward PyTypeObject OutlineType; + +#define PyOutline_Check(op) ((op)->ob_type == &OutlineType) + +static OutlineObject* +_outline_new(void) +{ + OutlineObject *self; + + self = PyObject_New(OutlineObject, &OutlineType); + if (self == NULL) + return NULL; + + self->outline = ImagingOutlineNew(); + + return self; +} + +static void +_outline_dealloc(OutlineObject* self) +{ + ImagingOutlineDelete(self->outline); + PyObject_Del(self); +} + +ImagingOutline +PyOutline_AsOutline(PyObject* outline) +{ + if (PyOutline_Check(outline)) + return ((OutlineObject*) outline)->outline; + + return NULL; +} + + +/* -------------------------------------------------------------------- */ +/* Factories */ + +PyObject* +PyOutline_Create(PyObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, ":outline")) + return NULL; + + return (PyObject*) _outline_new(); +} + + +/* -------------------------------------------------------------------- */ +/* Methods */ + +static PyObject* +_outline_move(OutlineObject* self, PyObject* args) +{ + float x0, y0; + if (!PyArg_ParseTuple(args, "ff", &x0, &y0)) + return NULL; + + ImagingOutlineMove(self->outline, x0, y0); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_outline_line(OutlineObject* self, PyObject* args) +{ + float x1, y1; + if (!PyArg_ParseTuple(args, "ff", &x1, &y1)) + return NULL; + + ImagingOutlineLine(self->outline, x1, y1); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_outline_curve(OutlineObject* self, PyObject* args) +{ + float x1, y1, x2, y2, x3, y3; + if (!PyArg_ParseTuple(args, "ffffff", &x1, &y1, &x2, &y2, &x3, &y3)) + return NULL; + + ImagingOutlineCurve(self->outline, x1, y1, x2, y2, x3, y3); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_outline_close(OutlineObject* self, PyObject* args) +{ + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + ImagingOutlineClose(self->outline); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* +_outline_transform(OutlineObject* self, PyObject* args) +{ + double a[6]; + if (!PyArg_ParseTuple(args, "(dddddd)", a+0, a+1, a+2, a+3, a+4, a+5)) + return NULL; + + ImagingOutlineTransform(self->outline, a); + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef _outline_methods[] = { + {"line", (PyCFunction)_outline_line, 1}, + {"curve", (PyCFunction)_outline_curve, 1}, + {"move", (PyCFunction)_outline_move, 1}, + {"close", (PyCFunction)_outline_close, 1}, + {"transform", (PyCFunction)_outline_transform, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +_outline_getattr(OutlineObject* self, char* name) +{ + return Py_FindMethod(_outline_methods, (PyObject*) self, name); +} + +statichere PyTypeObject OutlineType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Outline", /*tp_name*/ + sizeof(OutlineObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)_outline_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)_outline_getattr, /*tp_getattr*/ + 0 /*tp_setattr*/ +}; diff --git a/path.c b/path.c new file mode 100644 index 000000000..f083b971d --- /dev/null +++ b/path.c @@ -0,0 +1,587 @@ +/* + * The Python Imaging Library. + * + * 2D path utilities + * + * history: + * 1996-11-04 fl Added to PIL (incomplete) + * 1996-11-05 fl Added sequence semantics + * 1997-02-28 fl Fixed getbbox + * 1997-06-12 fl Added id attribute + * 1997-06-14 fl Added slicing and setitem + * 1998-12-29 fl Improved sequence handling (from Richard Jones) + * 1999-01-10 fl Fixed IndexError test for 1.5 (from Fred Drake) + * 2000-10-12 fl Added special cases for tuples and lists + * 2002-10-27 fl Added clipping boilerplate + * 2004-09-19 fl Added tolist(flat) variant + * 2005-05-06 fl Added buffer interface support to path constructor + * + * notes: + * FIXME: fill in remaining slots in the sequence api + * + * Copyright (c) 1997-2005 by Secret Labs AB + * Copyright (c) 1997-2005 by Fredrik Lundh + * + * See the README file for information on usage and redistribution. + */ + + +#include "Python.h" + +#include + +#if PY_VERSION_HEX < 0x01060000 +#define PyObject_New PyObject_NEW +#define PyObject_Del PyMem_DEL +#endif + +#if PY_VERSION_HEX < 0x02050000 +#define Py_ssize_t int +#define lenfunc inquiry +#define ssizeargfunc intargfunc +#define ssizessizeargfunc intintargfunc +#define ssizeobjargproc intobjargproc +#define ssizessizeobjargproc intintobjargproc +#endif + +/* compatibility wrappers (defined in _imaging.c) */ +extern int PyImaging_CheckBuffer(PyObject* buffer); +extern int PyImaging_ReadBuffer(PyObject* buffer, const void** ptr); + +/* -------------------------------------------------------------------- */ +/* Class */ +/* -------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + Py_ssize_t count; + double *xy; + int index; /* temporary use, e.g. in decimate */ +} PyPathObject; + +staticforward PyTypeObject PyPathType; + +static double* +alloc_array(int count) +{ + double* xy; + if (count < 0) { + PyErr_NoMemory(); + return NULL; + } + xy = malloc(2 * count * sizeof(double) + 1); + if (!xy) + PyErr_NoMemory(); + return xy; +} + +static PyPathObject* +path_new(Py_ssize_t count, double* xy, int duplicate) +{ + PyPathObject *path; + + if (duplicate) { + /* duplicate path */ + double* p = alloc_array(count); + if (!p) + return NULL; + memcpy(p, xy, count * 2 * sizeof(double)); + xy = p; + } + + path = PyObject_New(PyPathObject, &PyPathType); + if (path == NULL) + return NULL; + + path->count = count; + path->xy = xy; + + return path; +} + +static void +path_dealloc(PyPathObject* path) +{ + free(path->xy); + PyObject_Del(path); +} + +/* -------------------------------------------------------------------- */ +/* Helpers */ +/* -------------------------------------------------------------------- */ + +#define PyPath_Check(op) ((op)->ob_type == &PyPathType) + +int +PyPath_Flatten(PyObject* data, double **pxy) +{ + int i, j, n; + double *xy; + + if (PyPath_Check(data)) { + /* This was another path object. */ + PyPathObject *path = (PyPathObject*) data; + xy = alloc_array(path->count); + if (!xy) + return -1; + memcpy(xy, path->xy, 2 * path->count * sizeof(double)); + *pxy = xy; + return path->count; + } + + if (PyImaging_CheckBuffer(data)) { + /* Assume the buffer contains floats */ + float* ptr; + int n = PyImaging_ReadBuffer(data, (const void**) &ptr); + n /= 2 * sizeof(float); + xy = alloc_array(n); + if (!xy) + return -1; + for (i = 0; i < n+n; i++) + xy[i] = ptr[i]; + *pxy = xy; + return n; + } + + if (!PySequence_Check(data)) { + PyErr_SetString(PyExc_TypeError, "argument must be sequence"); + return -1; + } + + j = 0; + n = PyObject_Length(data); + /* Just in case __len__ breaks (or doesn't exist) */ + if (PyErr_Occurred()) + return -1; + + /* Allocate for worst case */ + xy = alloc_array(n); + if (!xy) + return -1; + + /* Copy table to path array */ + if (PyList_Check(data)) { + for (i = 0; i < n; i++) { + double x, y; + PyObject *op = PyList_GET_ITEM(data, i); + if (PyFloat_Check(op)) + xy[j++] = PyFloat_AS_DOUBLE(op); + else if (PyInt_Check(op)) + xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyNumber_Check(op)) + xy[j++] = PyFloat_AsDouble(op); + else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + xy[j++] = x; + xy[j++] = y; + } else { + free(xy); + return -1; + } + } + } else if (PyTuple_Check(data)) { + for (i = 0; i < n; i++) { + double x, y; + PyObject *op = PyTuple_GET_ITEM(data, i); + if (PyFloat_Check(op)) + xy[j++] = PyFloat_AS_DOUBLE(op); + else if (PyInt_Check(op)) + xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyNumber_Check(op)) + xy[j++] = PyFloat_AsDouble(op); + else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + xy[j++] = x; + xy[j++] = y; + } else { + free(xy); + return -1; + } + } + } else { + for (i = 0; i < n; i++) { + double x, y; + PyObject *op = PySequence_GetItem(data, i); + if (!op) { + /* treat IndexError as end of sequence */ + if (PyErr_Occurred() && + PyErr_ExceptionMatches(PyExc_IndexError)) { + PyErr_Clear(); + break; + } else { + free(xy); + return -1; + } + } + if (PyFloat_Check(op)) + xy[j++] = PyFloat_AS_DOUBLE(op); + else if (PyInt_Check(op)) + xy[j++] = (float) PyInt_AS_LONG(op); + else if (PyNumber_Check(op)) + xy[j++] = PyFloat_AsDouble(op); + else if (PyArg_ParseTuple(op, "dd", &x, &y)) { + xy[j++] = x; + xy[j++] = y; + } else { + Py_DECREF(op); + free(xy); + return -1; + } + Py_DECREF(op); + } + } + + if (j & 1) { + PyErr_SetString(PyExc_ValueError, "wrong number of coordinates"); + free(xy); + return -1; + } + + *pxy = xy; + return j/2; +} + + +/* -------------------------------------------------------------------- */ +/* Factories */ +/* -------------------------------------------------------------------- */ + +PyObject* +PyPath_Create(PyObject* self, PyObject* args) +{ + PyObject* data; + Py_ssize_t count; + double *xy; + + if (PyArg_ParseTuple(args, "i:Path", &count)) { + + /* number of vertices */ + xy = alloc_array(count); + if (!xy) + return NULL; + + } else { + + /* sequence or other path */ + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "O", &data)) + return NULL; + + count = PyPath_Flatten(data, &xy); + if (count < 0) + return NULL; + } + + return (PyObject*) path_new(count, xy, 0); +} + + +/* -------------------------------------------------------------------- */ +/* Methods */ +/* -------------------------------------------------------------------- */ + +static PyObject* +path_compact(PyPathObject* self, PyObject* args) +{ + /* Simple-minded method to shorten path. A point is removed if + the city block distance to the previous point is less than the + given distance */ + int i, j; + double *xy; + + double cityblock = 2.0; + + if (!PyArg_ParseTuple(args, "|d:compact", &cityblock)) + return NULL; + + xy = self->xy; + + /* remove bogus vertices */ + for (i = j = 1; i < self->count; i++) { + if (fabs(xy[j+j-2]-xy[i+i]) + fabs(xy[j+j-1]-xy[i+i+1]) >= cityblock) { + xy[j+j] = xy[i+i]; + xy[j+j+1] = xy[i+i+1]; + j++; + } + } + + i = self->count - j; + self->count = j; + + /* shrink coordinate array */ + self->xy = realloc(self->xy, 2 * self->count * sizeof(double)); + + return Py_BuildValue("i", i); /* number of removed vertices */ +} + +static PyObject* +path_clip_polygon(PyPathObject* self, PyObject* args) +{ + /* Clip path representing a single polygon */ + PyErr_SetString(PyExc_RuntimeError, "not yet implemented"); + return NULL; +} + +static PyObject* +path_clip_polyline(PyPathObject* self, PyObject* args) +{ + /* Clip path representing a single polyline (outline) */ + PyErr_SetString(PyExc_RuntimeError, "not yet implemented"); + return NULL; +} + +static PyObject* +path_getbbox(PyPathObject* self, PyObject* args) +{ + /* Find bounding box */ + int i; + double *xy; + double x0, y0, x1, y1; + + if (!PyArg_ParseTuple(args, ":getbbox")) + return NULL; + + xy = self->xy; + + x0 = x1 = xy[0]; + y0 = y1 = xy[1]; + + for (i = 1; i < self->count; i++) { + if (xy[i+i] < x0) + x0 = xy[i+i]; + if (xy[i+i] > x1) + x1 = xy[i+i]; + if (xy[i+i+1] < y0) + y0 = xy[i+i+1]; + if (xy[i+i+1] > y1) + y1 = xy[i+i+1]; + } + + return Py_BuildValue("dddd", x0, y0, x1, y1); +} + +static PyObject* +path_getitem(PyPathObject* self, int i) +{ + if (i < 0 || i >= self->count) { + PyErr_SetString(PyExc_IndexError, "path index out of range"); + return NULL; + } + + return Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]); +} + +static PyObject* +path_getslice(PyPathObject* self, Py_ssize_t ilow, Py_ssize_t ihigh) +{ + /* adjust arguments */ + if (ilow < 0) + ilow = 0; + else if (ilow >= self->count) + ilow = self->count; + if (ihigh < 0) + ihigh = 0; + if (ihigh < ilow) + ihigh = ilow; + else if (ihigh > self->count) + ihigh = self->count; + + return (PyObject*) path_new(ihigh - ilow, self->xy + ilow * 2, 1); +} + +static Py_ssize_t +path_len(PyPathObject* self) +{ + return self->count; +} + +static PyObject* +path_map(PyPathObject* self, PyObject* args) +{ + /* Map coordinate set through function */ + int i; + double *xy; + PyObject* function; + + if (!PyArg_ParseTuple(args, "O:map", &function)) + return NULL; + + xy = self->xy; + + /* apply function to coordinate set */ + for (i = 0; i < self->count; i++) { + double x = xy[i+i]; + double y = xy[i+i+1]; + PyObject* item = PyObject_CallFunction(function, "dd", x, y); + if (!item || !PyArg_ParseTuple(item, "dd", &x, &y)) { + Py_XDECREF(item); + return NULL; + } + xy[i+i] = x; + xy[i+i+1] = y; + Py_DECREF(item); + } + + Py_INCREF(Py_None); + return Py_None; +} + +static int +path_setitem(PyPathObject* self, int i, PyObject* op) +{ + double* xy; + + if (i < 0 || i >= self->count) { + PyErr_SetString(PyExc_IndexError, + "path assignment index out of range"); + return -1; + } + + if (op == NULL) { + PyErr_SetString(PyExc_TypeError, + "cannot delete from path"); + return -1; + } + + xy = &self->xy[i+i]; + + if (!PyArg_ParseTuple(op, "dd", &xy[0], &xy[1])) + return -1; + + return 0; +} + +static PyObject* +path_tolist(PyPathObject* self, PyObject* args) +{ + PyObject *list; + int i; + + int flat = 0; + if (!PyArg_ParseTuple(args, "|i:tolist", &flat)) + return NULL; + + if (flat) { + list = PyList_New(self->count*2); + for (i = 0; i < self->count*2; i++) { + PyObject* item; + item = PyFloat_FromDouble(self->xy[i]); + if (!item) + goto error; + PyList_SetItem(list, i, item); + } + } else { + list = PyList_New(self->count); + for (i = 0; i < self->count; i++) { + PyObject* item; + item = Py_BuildValue("dd", self->xy[i+i], self->xy[i+i+1]); + if (!item) + goto error; + PyList_SetItem(list, i, item); + } + } + + return list; + +error: + Py_DECREF(list); + return NULL; +} + +static PyObject* +path_transform(PyPathObject* self, PyObject* args) +{ + /* Apply affine transform to coordinate set */ + int i; + double *xy; + double a, b, c, d, e, f; + + double wrap = 0.0; + + if (!PyArg_ParseTuple(args, "(dddddd)|d:transform", + &a, &b, &c, &d, &e, &f, + &wrap)) + return NULL; + + xy = self->xy; + + /* transform the coordinate set */ + if (b == 0.0 && d == 0.0) + /* scaling */ + for (i = 0; i < self->count; i++) { + xy[i+i] = a*xy[i+i]+c; + xy[i+i+1] = e*xy[i+i+1]+f; + } + else + /* affine transform */ + for (i = 0; i < self->count; i++) { + double x = xy[i+i]; + double y = xy[i+i+1]; + xy[i+i] = a*x+b*y+c; + xy[i+i+1] = d*x+e*y+f; + } + + /* special treatment of geographical map data */ + if (wrap != 0.0) + for (i = 0; i < self->count; i++) + xy[i+i] = fmod(xy[i+i], wrap); + + Py_INCREF(Py_None); + return Py_None; +} + +static struct PyMethodDef methods[] = { + {"getbbox", (PyCFunction)path_getbbox, 1}, + {"tolist", (PyCFunction)path_tolist, 1}, + {"clip_polygon", (PyCFunction)path_clip_polygon, 1}, + {"clip_polyline", (PyCFunction)path_clip_polyline, 1}, + {"compact", (PyCFunction)path_compact, 1}, + {"map", (PyCFunction)path_map, 1}, + {"transform", (PyCFunction)path_transform, 1}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject* +path_getattr(PyPathObject* self, char* name) +{ + PyObject* res; + + res = Py_FindMethod(methods, (PyObject*) self, name); + if (res) + return res; + + PyErr_Clear(); + + if (strcmp(name, "id") == 0) + return Py_BuildValue("l", (long) self->xy); + + PyErr_SetString(PyExc_AttributeError, name); + return NULL; +} + +static PySequenceMethods path_as_sequence = { + (lenfunc)path_len, /*sq_length*/ + (binaryfunc)0, /*sq_concat*/ + (ssizeargfunc)0, /*sq_repeat*/ + (ssizeargfunc)path_getitem, /*sq_item*/ + (ssizessizeargfunc)path_getslice, /*sq_slice*/ + (ssizeobjargproc)path_setitem, /*sq_ass_item*/ + (ssizessizeobjargproc)0, /*sq_ass_slice*/ +}; + +statichere PyTypeObject PyPathType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "Path", /*tp_name*/ + sizeof(PyPathObject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)path_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)path_getattr, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number */ + &path_as_sequence, /*tp_as_sequence */ + 0, /*tp_as_mapping */ + 0, /*tp_hash*/ +}; diff --git a/selftest.py b/selftest.py new file mode 100644 index 000000000..22bbe433b --- /dev/null +++ b/selftest.py @@ -0,0 +1,202 @@ +# minimal sanity check + +ROOT = "." + +import os, sys +sys.path.insert(0, ROOT) + +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFilter +from PIL import ImageMath + +try: + Image.core.ping +except ImportError, v: + print "***", v + sys.exit() +except AttributeError: + pass + +def _info(im): + im.load() + return im.format, im.mode, im.size + +def testimage(): + """ + PIL lets you create in-memory images with various pixel types: + + >>> im = Image.new("1", (128, 128)) # monochrome + >>> _info(im) + (None, '1', (128, 128)) + >>> _info(Image.new("L", (128, 128))) # grayscale (luminance) + (None, 'L', (128, 128)) + >>> _info(Image.new("P", (128, 128))) # palette + (None, 'P', (128, 128)) + >>> _info(Image.new("RGB", (128, 128))) # truecolor + (None, 'RGB', (128, 128)) + >>> _info(Image.new("I", (128, 128))) # 32-bit integer + (None, 'I', (128, 128)) + >>> _info(Image.new("F", (128, 128))) # 32-bit floating point + (None, 'F', (128, 128)) + + Or open existing files: + + >>> im = Image.open(os.path.join(ROOT, "Images/lena.gif")) + >>> _info(im) + ('GIF', 'P', (128, 128)) + >>> _info(Image.open(os.path.join(ROOT, "Images/lena.ppm"))) + ('PPM', 'RGB', (128, 128)) + >>> try: + ... _info(Image.open(os.path.join(ROOT, "Images/lena.jpg"))) + ... except IOError, v: + ... print v + ('JPEG', 'RGB', (128, 128)) + + PIL doesn't actually load the image data until it's needed, + or you call the "load" method: + + >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) + >>> print im.im # internal image attribute + None + >>> a = im.load() + >>> type(im.im) + + + You can apply many different operations on images. Most + operations return a new image: + + >>> im = Image.open(os.path.join(ROOT, "Images/lena.ppm")) + >>> _info(im.convert("L")) + (None, 'L', (128, 128)) + >>> _info(im.copy()) + (None, 'RGB', (128, 128)) + >>> _info(im.crop((32, 32, 96, 96))) + (None, 'RGB', (64, 64)) + >>> _info(im.filter(ImageFilter.BLUR)) + (None, 'RGB', (128, 128)) + >>> im.getbands() + ('R', 'G', 'B') + >>> im.getbbox() + (0, 0, 128, 128) + >>> len(im.getdata()) + 16384 + >>> im.getextrema() + ((61, 255), (26, 234), (44, 223)) + >>> im.getpixel((0, 0)) + (223, 162, 133) + >>> len(im.getprojection()) + 2 + >>> len(im.histogram()) + 768 + >>> _info(im.point(range(256)*3)) + (None, 'RGB', (128, 128)) + >>> _info(im.resize((64, 64))) + (None, 'RGB', (64, 64)) + >>> _info(im.rotate(45)) + (None, 'RGB', (128, 128)) + >>> map(_info, im.split()) + [(None, 'L', (128, 128)), (None, 'L', (128, 128)), (None, 'L', (128, 128))] + >>> len(im.convert("1").tobitmap()) + 10456 + >>> len(im.tostring()) + 49152 + >>> _info(im.transform((512, 512), Image.AFFINE, (1,0,0,0,1,0))) + (None, 'RGB', (512, 512)) + >>> _info(im.transform((512, 512), Image.EXTENT, (32,32,96,96))) + (None, 'RGB', (512, 512)) + + The ImageDraw module lets you draw stuff in raster images: + + >>> im = Image.new("L", (128, 128), 64) + >>> d = ImageDraw.ImageDraw(im) + >>> d.line((0, 0, 128, 128), fill=128) + >>> d.line((0, 128, 128, 0), fill=128) + >>> im.getextrema() + (64, 128) + + In 1.1.4, you can specify colors in a number of ways: + + >>> xy = 0, 0, 128, 128 + >>> im = Image.new("RGB", (128, 128), 0) + >>> d = ImageDraw.ImageDraw(im) + >>> d.rectangle(xy, "#f00") + >>> im.getpixel((0, 0)) + (255, 0, 0) + >>> d.rectangle(xy, "#ff0000") + >>> im.getpixel((0, 0)) + (255, 0, 0) + >>> d.rectangle(xy, "rgb(255,0,0)") + >>> im.getpixel((0, 0)) + (255, 0, 0) + >>> d.rectangle(xy, "rgb(100%,0%,0%)") + >>> im.getpixel((0, 0)) + (255, 0, 0) + >>> d.rectangle(xy, "hsl(0, 100%, 50%)") + >>> im.getpixel((0, 0)) + (255, 0, 0) + >>> d.rectangle(xy, "red") + >>> im.getpixel((0, 0)) + (255, 0, 0) + + In 1.1.6, you can use the ImageMath module to do image + calculations. + + >>> im = ImageMath.eval("float(im + 20)", im=im.convert("L")) + >>> im.mode, im.size + ('F', (128, 128)) + + PIL can do many other things, but I'll leave that for another + day. If you're curious, check the handbook, available from: + + http://www.pythonware.com + + Cheers /F + """ + + +def check_module(feature, module): + try: + __import__("PIL." + module) + except ImportError: + print "***", feature, "support not installed" + else: + print "---", feature, "support ok" + +def check_codec(feature, codec): + if codec + "_encoder" not in dir(Image.core): + print "***", feature, "support not installed" + else: + print "---", feature, "support ok" + + +if __name__ == "__main__": + # check build sanity + + exit_status = 0 + + print "-"*68 + print "PIL", Image.VERSION, "TEST SUMMARY " + print "-"*68 + print "Python modules loaded from", os.path.dirname(Image.__file__) + print "Binary modules loaded from", os.path.dirname(Image.core.__file__) + print "-"*68 + check_module("PIL CORE", "_imaging") + check_module("TKINTER", "_imagingtk") + check_codec("JPEG", "jpeg") + check_codec("ZLIB (PNG/ZIP)", "zip") + check_module("FREETYPE2", "_imagingft") + check_module("LITTLECMS", "_imagingcms") + print "-"*68 + + # use doctest to make sure the test program behaves as documented! + import doctest, selftest + print "Running selftest:" + status = doctest.testmod(selftest) + if status[0]: + print "*** %s tests of %d failed." % status + exit_status = 1 + else: + print "--- %s tests passed." % status[1] + + sys.exit(exit_status) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..861a9f554 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..5e1a0a7c1 --- /dev/null +++ b/setup.py @@ -0,0 +1,477 @@ +#!/usr/bin/env python +# +# Setup script for PIL 1.1.5 and later (repackaged by Chris McDonough and +# Hanno Schlichting for setuptools compatibility) +# +# Usage: python setup.py install +# + +import glob, os, re, struct, string, sys + +def libinclude(root): + # map root to (root/lib, root/include) + return os.path.join(root, "lib"), os.path.join(root, "include") + +# -------------------------------------------------------------------- +# Library pointers. +# +# Use None to look for the libraries in well-known library locations. +# Use a string to specify a single directory, for both the library and +# the include files. Use a tuple to specify separate directories: +# (libpath, includepath). Examples: +# +# JPEG_ROOT = "/home/libraries/jpeg-6b" +# TIFF_ROOT = "/opt/tiff/lib", "/opt/tiff/include" +# +# If you have "lib" and "include" directories under a common parent, +# you can use the "libinclude" helper: +# +# TIFF_ROOT = libinclude("/opt/tiff") + +TCL_ROOT = None +JPEG_ROOT = None +ZLIB_ROOT = None +TIFF_ROOT = None +FREETYPE_ROOT = None +LCMS_ROOT = None + +# FIXME: add mechanism to explicitly *disable* the use of a library + +# -------------------------------------------------------------------- +# Identification + +NAME = "Pillow" +DESCRIPTION = "Python Imaging Library (fork)" +#AUTHOR = "Secret Labs AB (PythonWare)", "info@pythonware.com" +AUTHOR = "Alex Clark (fork author)", "aclark@aclark.net" +#HOMEPAGE = "http://www.pythonware.com/products/pil" +HOMEPAGE = "http://github.com/Pillow" +#DOWNLOAD_URL = "http://effbot.org/downloads/%s-%s.tar.gz" # name, version + +# -------------------------------------------------------------------- +# Core library + +IMAGING = [ + "decode", "encode", "map", "display", "outline", "path", + ] + +LIBIMAGING = [ + "Access", "Antialias", "Bands", "BitDecode", "Blend", "Chops", + "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw", + "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode", + "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", + "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix", + "ModeFilter", "MspDecode", "Negative", "Offset", "Pack", + "PackDecode", "Palette", "Paste", "Quant", "QuantHash", + "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point", + "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode", + "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode", + "XbmEncode", "ZipDecode", "ZipEncode" + ] + +# -------------------------------------------------------------------- + +from distutils import sysconfig +from distutils.command.build_ext import build_ext +from setuptools import setup, find_packages, Extension + +try: + import _tkinter +except ImportError: + _tkinter = None + +def add_directory(path, dir, where=None): + if dir and os.path.isdir(dir) and dir not in path: + if where is None: + path.append(dir) + else: + path.insert(where, dir) + +def find_include_file(self, include): + for directory in self.compiler.include_dirs: + if os.path.isfile(os.path.join(directory, include)): + return 1 + return 0 + +def find_library_file(self, library): + return self.compiler.find_library_file(self.compiler.library_dirs, library) + +def find_version(filename): + for line in open(filename).readlines(): + m = re.search("VERSION\s*=\s*\"([^\"]+)\"", line) + if m: + return m.group(1) + return None + +#VERSION = find_version("PIL/Image.py") +VERSION = "1.0" + +class pil_build_ext(build_ext): + + def build_extensions(self): + + global TCL_ROOT + + library_dirs = [] + include_dirs = [] + + add_directory(include_dirs, "libImaging") + + # + # add platform directories + + if sys.platform == "cygwin": + # pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory + add_directory(library_dirs, os.path.join( + "/usr/lib", "python%s" % sys.version[:3], "config" + )) + + elif sys.platform == "darwin": + # attempt to make sure we pick freetype2 over other versions + add_directory(include_dirs, "/sw/include/freetype2") + add_directory(include_dirs, "/sw/lib/freetype2/include") + # fink installation directories + add_directory(library_dirs, "/sw/lib") + add_directory(include_dirs, "/sw/include") + # darwin ports installation directories + add_directory(library_dirs, "/opt/local/lib") + add_directory(include_dirs, "/opt/local/include") + + add_directory(library_dirs, "/usr/local/lib") + # FIXME: check /opt/stuff directories here? + + prefix = sysconfig.get_config_var("prefix") + if prefix: + add_directory(library_dirs, os.path.join(prefix, "lib")) + add_directory(include_dirs, os.path.join(prefix, "include")) + + # + # locate tkinter libraries + + if _tkinter: + TCL_VERSION = _tkinter.TCL_VERSION[:3] + + if _tkinter and not TCL_ROOT: + # we have Tkinter but the TCL_ROOT variable was not set; + # try to locate appropriate Tcl/Tk libraries + PYVERSION = sys.version[0] + sys.version[2] + TCLVERSION = TCL_VERSION[0] + TCL_VERSION[2] + roots = [ + # common installation directories, mostly for Windows + # (for Unix-style platforms, we'll check in well-known + # locations later) + os.path.join("/py" + PYVERSION, "Tcl"), + os.path.join("/python" + PYVERSION, "Tcl"), + "/Tcl", "/Tcl" + TCLVERSION, "/Tcl" + TCL_VERSION, + os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"), + ] + for TCL_ROOT in roots: + TCL_ROOT = os.path.abspath(TCL_ROOT) + if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")): + # FIXME: use distutils logging (?) + print "--- using Tcl/Tk libraries at", TCL_ROOT + print "--- using Tcl/Tk version", TCL_VERSION + TCL_ROOT = libinclude(TCL_ROOT) + break + else: + TCL_ROOT = None + + # + # add configured kits + + for root in (TCL_ROOT, JPEG_ROOT, TCL_ROOT, TIFF_ROOT, ZLIB_ROOT, + FREETYPE_ROOT, LCMS_ROOT): + if isinstance(root, type(())): + lib_root, include_root = root + else: + lib_root = include_root = root + add_directory(library_dirs, lib_root) + add_directory(include_dirs, include_root) + + # + # add standard directories + + # look for tcl specific subdirectory (e.g debian) + if _tkinter: + tcl_dir = "/usr/include/tcl" + TCL_VERSION + if os.path.isfile(os.path.join(tcl_dir, "tk.h")): + add_directory(include_dirs, tcl_dir) + + # standard locations + add_directory(library_dirs, "/usr/local/lib") + add_directory(include_dirs, "/usr/local/include") + + add_directory(library_dirs, "/usr/lib") + add_directory(include_dirs, "/usr/include") + + # + # insert new dirs *before* default libs, to avoid conflicts + # between Python PYD stub libs and real libraries + + self.compiler.library_dirs = library_dirs + self.compiler.library_dirs + self.compiler.include_dirs = include_dirs + self.compiler.include_dirs + + # + # look for available libraries + + class feature: + zlib = jpeg = tiff = freetype = tcl = tk = lcms = None + feature = feature() + + if find_include_file(self, "zlib.h"): + if find_library_file(self, "z"): + feature.zlib = "z" + elif sys.platform == "win32" and find_library_file(self, "zlib"): + feature.zlib = "zlib" # alternative name + + if find_include_file(self, "jpeglib.h"): + if find_library_file(self, "jpeg"): + feature.jpeg = "jpeg" + elif sys.platform == "win32" and find_library_file(self, "libjpeg"): + feature.jpeg = "libjpeg" # alternative name + + if find_library_file(self, "tiff"): + feature.tiff = "tiff" + + if find_library_file(self, "freetype"): + # look for freetype2 include files + freetype_version = 0 + for dir in self.compiler.include_dirs: + if os.path.isfile(os.path.join(dir, "ft2build.h")): + freetype_version = 21 + dir = os.path.join(dir, "freetype2") + break + dir = os.path.join(dir, "freetype2") + if os.path.isfile(os.path.join(dir, "ft2build.h")): + freetype_version = 21 + break + if os.path.isdir(os.path.join(dir, "freetype")): + freetype_version = 20 + break + if freetype_version: + feature.freetype = "freetype" + feature.freetype_version = freetype_version + if dir: + add_directory(self.compiler.include_dirs, dir, 0) + + if find_include_file(self, "lcms.h"): + if find_library_file(self, "lcms"): + feature.lcms = "lcms" + + if _tkinter and find_include_file(self, "tk.h"): + # the library names may vary somewhat (e.g. tcl84 or tcl8.4) + version = TCL_VERSION[0] + TCL_VERSION[2] + if find_library_file(self, "tcl" + version): + feature.tcl = "tcl" + version + elif find_library_file(self, "tcl" + TCL_VERSION): + feature.tcl = "tcl" + TCL_VERSION + if find_library_file(self, "tk" + version): + feature.tk = "tk" + version + elif find_library_file(self, "tk" + TCL_VERSION): + feature.tk = "tk" + TCL_VERSION + + # + # core library + + files = ["_imaging.c"] + for file in IMAGING: + files.append(file + ".c") + for file in LIBIMAGING: + files.append(os.path.join("libImaging", file + ".c")) + + libs = [] + defs = [] + if feature.jpeg: + libs.append(feature.jpeg) + defs.append(("HAVE_LIBJPEG", None)) + if feature.zlib: + libs.append(feature.zlib) + defs.append(("HAVE_LIBZ", None)) + if sys.platform == "win32": + libs.extend(["kernel32", "user32", "gdi32"]) + if struct.unpack("h", "\0\1")[0] == 1: + defs.append(("WORDS_BIGENDIAN", None)) + + exts = [(Extension( + "_imaging", files, libraries=libs, define_macros=defs + ))] + + # + # additional libraries + + if feature.freetype: + defs = [] + if feature.freetype_version == 20: + defs.append(("USE_FREETYPE_2_0", None)) + exts.append(Extension( + "_imagingft", ["_imagingft.c"], libraries=["freetype"], + define_macros=defs + )) + + if os.path.isfile("_imagingtiff.c") and feature.tiff: + exts.append(Extension( + "_imagingtiff", ["_imagingtiff.c"], libraries=["tiff"] + )) + + if os.path.isfile("_imagingcms.c") and feature.lcms: + extra = [] + if sys.platform == "win32": + extra.extend(["user32", "gdi32"]) + exts.append(Extension( + "_imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra + )) + + if sys.platform == "darwin": + # locate Tcl/Tk frameworks + frameworks = [] + framework_roots = [ + "/Library/Frameworks", + "/System/Library/Frameworks" + ] + for root in framework_roots: + if (os.path.exists(os.path.join(root, "Tcl.framework")) and + os.path.exists(os.path.join(root, "Tk.framework"))): + print "--- using frameworks at", root + frameworks = ["-framework", "Tcl", "-framework", "Tk"] + dir = os.path.join(root, "Tcl.framework", "Headers") + add_directory(self.compiler.include_dirs, dir, 0) + dir = os.path.join(root, "Tk.framework", "Headers") + add_directory(self.compiler.include_dirs, dir, 1) + break + if frameworks: + exts.append(Extension( + "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], + extra_compile_args=frameworks, extra_link_args=frameworks + )) + feature.tcl = feature.tk = 1 # mark as present + elif feature.tcl and feature.tk: + exts.append(Extension( + "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"], + libraries=[feature.tcl, feature.tk] + )) + + if os.path.isfile("_imagingmath.c"): + exts.append(Extension("_imagingmath", ["_imagingmath.c"])) + + self.extensions[:] = exts + + build_ext.build_extensions(self) + + # + # sanity and security checks + + unsafe_zlib = None + + if feature.zlib: + unsafe_zlib = self.check_zlib_version(self.compiler.include_dirs) + + self.summary_report(feature, unsafe_zlib) + + def summary_report(self, feature, unsafe_zlib): + + print "-" * 68 + print "PIL", VERSION, "SETUP SUMMARY" + print "-" * 68 + print "version ", VERSION + v = string.split(sys.version, "[") + print "platform ", sys.platform, string.strip(v[0]) + for v in v[1:]: + print " ", string.strip("[" + v) + print "-" * 68 + + options = [ + (feature.tcl and feature.tk, "TKINTER"), + (feature.jpeg, "JPEG"), + (feature.zlib, "ZLIB (PNG/ZIP)"), + # (feature.tiff, "experimental TIFF G3/G4 read"), + (feature.freetype, "FREETYPE2"), + (feature.lcms, "LITTLECMS"), + ] + + all = 1 + for option in options: + if option[0]: + print "---", option[1], "support available" + else: + print "***", option[1], "support not available", + if option[1] == "TKINTER" and _tkinter: + version = _tkinter.TCL_VERSION + print "(Tcl/Tk %s libraries needed)" % version, + print + all = 0 + + if feature.zlib and unsafe_zlib: + print + print "*** Warning: zlib", unsafe_zlib, + print "may contain a security vulnerability." + print "*** Consider upgrading to zlib 1.2.3 or newer." + print "*** See: http://www.kb.cert.org/vuls/id/238678" + print " http://www.kb.cert.org/vuls/id/680620" + print " http://www.gzip.org/zlib/advisory-2002-03-11.txt" + print + + print "-" * 68 + + if not all: + print "To add a missing option, make sure you have the required" + print "library, and set the corresponding ROOT variable in the" + print "setup.py script." + print + + print "To check the build, run the selftest.py script." + + def check_zlib_version(self, include_dirs): + # look for unsafe versions of zlib + for dir in include_dirs: + zlibfile = os.path.join(dir, "zlib.h") + if os.path.isfile(zlibfile): + break + else: + return + for line in open(zlibfile).readlines(): + m = re.match('#define\s+ZLIB_VERSION\s+"([^"]*)"', line) + if not m: + continue + if m.group(1) < "1.2.3": + return m.group(1) + +# +# build! + +if __name__ == "__main__": + + try: + # add necessary to distutils (for backwards compatibility) + from distutils.dist import DistributionMetadata + DistributionMetadata.classifiers = None + DistributionMetadata.download_url = None + DistributionMetadata.platforms = None + except: + pass + + setup( + author=AUTHOR[0], author_email=AUTHOR[1], + classifiers=[ + "Development Status :: 6 - Mature", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", + "Topic :: Multimedia :: Graphics :: Capture :: Scanners", + "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", + "Topic :: Multimedia :: Graphics :: Graphics Conversion", + "Topic :: Multimedia :: Graphics :: Viewers", + ], + cmdclass = {"build_ext": pil_build_ext}, + description=DESCRIPTION, +# download_url=DOWNLOAD_URL % (NAME, VERSION), + ext_modules = [Extension("_imaging", ["_imaging.c"])], # dummy + extra_path = "PIL", + license="Python (MIT style)", + long_description=DESCRIPTION, + name=NAME, + packages=find_packages(), + setup_requires=["setuptools_hg"], + platforms="Python 1.5.2 and later.", + scripts = glob.glob("Scripts/pil*.py"), + url=HOMEPAGE, + version=VERSION, + )