1#!/usr/bin/env python
2
3# $URL$
4# $Rev$
5
6# png.py - PNG encoder/decoder in pure Python
7#
8# Copyright (C) 2006 Johann C. Rocholl <johann@browsershots.org>
9# Portions Copyright (C) 2009 David Jones <drj@pobox.com>
10# And probably portions Copyright (C) 2006 Nicko van Someren <nicko@nicko.org>
11#
12# Original concept by Johann C. Rocholl.
13#
14# LICENSE (The MIT License)
15#
16# Permission is hereby granted, free of charge, to any person
17# obtaining a copy of this software and associated documentation files
18# (the "Software"), to deal in the Software without restriction,
19# including without limitation the rights to use, copy, modify, merge,
20# publish, distribute, sublicense, and/or sell copies of the Software,
21# and to permit persons to whom the Software is furnished to do so,
22# subject to the following conditions:
23#
24# The above copyright notice and this permission notice shall be
25# included in all copies or substantial portions of the Software.
26#
27# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34# SOFTWARE.
35#
36# Changelog (recent first):
37# 2009-03-11 David: interlaced bit depth < 8 (writing).
38# 2009-03-10 David: interlaced bit depth < 8 (reading).
39# 2009-03-04 David: Flat and Boxed pixel formats.
40# 2009-02-26 David: Palette support (writing).
41# 2009-02-23 David: Bit-depths < 8; better PNM support.
42# 2006-06-17 Nicko: Reworked into a class, faster interlacing.
43# 2006-06-17 Johann: Very simple prototype PNG decoder.
44# 2006-06-17 Nicko: Test suite with various image generators.
45# 2006-06-17 Nicko: Alpha-channel, grey-scale, 16-bit/plane support.
46# 2006-06-15 Johann: Scanline iterator interface for large input files.
47# 2006-06-09 Johann: Very simple prototype PNG encoder.
48
49# Incorporated into Bangai-O Development Tools by drj on 2009-02-11 from
50# http://trac.browsershots.org/browser/trunk/pypng/lib/png.py?rev=2885
51
52# Incorporated into pypng by drj on 2009-03-12 from
53# //depot/prj/bangaio/master/code/png.py#67
54
55
56"""
57Pure Python PNG Reader/Writer
58
59This Python module implements support for PNG images (see PNG
60specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads
61and writes PNG files with all allowable bit depths (1/2/4/8/16/24/32/48/64
62bits per pixel) and colour combinations: greyscale (1/2/4/8/16 bit); RGB,
63RGBA, LA (greyscale with alpha) with 8/16 bits per channel; colour mapped
64images (1/2/4/8 bit).  Adam7 interlacing is supported for reading and
65writing.  A number of optional chunks can be specified (when writing)
66and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``.
67
68For help, type ``import png; help(png)`` in your python interpreter.
69
70A good place to start is the :class:`Reader` and :class:`Writer` classes.
71
72Requires Python 2.3.  Limited support is available for Python 2.2, but
73not everything works.  Best with Python 2.4 and higher.  Installation is
74trivial, but see the ``README.txt`` file (with the source distribution)
75for details.
76
77This file can also be used as a command-line utility to convert
78`Netpbm <http://netpbm.sourceforge.net/>`_ PNM files to PNG, and the reverse conversion from PNG to
79PNM. The interface is similar to that of the ``pnmtopng`` program from
80Netpbm.  Type ``python png.py --help`` at the shell prompt
81for usage and a list of options.
82
83A note on spelling and terminology
84----------------------------------
85
86Generally British English spelling is used in the documentation.  So
87that's "greyscale" and "colour".  This not only matches the author's
88native language, it's also used by the PNG specification.
89
90The major colour models supported by PNG (and hence by PyPNG) are:
91greyscale, RGB, greyscale--alpha, RGB--alpha.  These are sometimes
92referred to using the abbreviations: L, RGB, LA, RGBA.  In this case
93each letter abbreviates a single channel: *L* is for Luminance or Luma or
94Lightness which is the channel used in greyscale images; *R*, *G*, *B* stand
95for Red, Green, Blue, the components of a colour image; *A* stands for
96Alpha, the opacity channel (used for transparency effects, but higher
97values are more opaque, so it makes sense to call it opacity).
98
99A note on formats
100-----------------
101
102When getting pixel data out of this module (reading) and presenting
103data to this module (writing) there are a number of ways the data could
104be represented as a Python value.  Generally this module uses one of
105three formats called "flat row flat pixel", "boxed row flat pixel", and
106"boxed row boxed pixel".  Basically the concern is whether each pixel
107and each row comes in its own little tuple (box), or not.
108
109Consider an image that is 3 pixels wide by 2 pixels high, and each pixel
110has RGB components:
111
112Boxed row flat pixel::
113
114  list([R,G,B, R,G,B, R,G,B],
115       [R,G,B, R,G,B, R,G,B])
116
117Each row appears as its own list, but the pixels are flattened so that
118three values for one pixel simply follow the three values for the previous
119pixel.  This is the most common format used, because it provides a good
120compromise between space and convenience.  PyPNG regards itself as
121at liberty to replace any sequence type with any sufficiently compatible
122other sequence type; in practice each row is an array (from the array
123module), and the outer list is sometimes an iterator rather than an
124explicit list (so that streaming is possible).
125
126Flat row flat pixel::
127
128  [R,G,B, R,G,B, R,G,B,
129   R,G,B, R,G,B, R,G,B]
130
131The entire image is one single giant sequence of colour values.
132Generally an array will be used (to save space), not a list.
133
134Boxed row boxed pixel::
135
136  list([ (R,G,B), (R,G,B), (R,G,B) ],
137       [ (R,G,B), (R,G,B), (R,G,B) ])
138
139Each row appears in its own list, but each pixel also appears in its own
140tuple.  A serious memory burn in Python.
141
142In all cases the top row comes first, and for each row the pixels are
143ordered from left-to-right.  Within a pixel the values appear in the
144order, R-G-B-A (or L-A for greyscale--alpha).
145
146There is a fourth format, mentioned because it is used internally,
147is close to what lies inside a PNG file itself, and has some support
148from the public API.  This format is called packed.  When packed,
149each row is a sequence of bytes (integers from 0 to 255), just as
150it is before PNG scanline filtering is applied.  When the bit depth
151is 8 this is essentially the same as boxed row flat pixel; when the
152bit depth is less than 8, several pixels are packed into each byte;
153when the bit depth is 16 (the only value more than 8 that is supported
154by the PNG image format) each pixel value is decomposed into 2 bytes
155(and `packed` is a misnomer).  This format is used by the
156:meth:`Writer.write_packed` method.  It isn't usually a convenient
157format, but may be just right if the source data for the PNG image
158comes from something that uses a similar format (for example, 1-bit
159BMPs, or another PNG file).
160
161And now, my famous members
162--------------------------
163"""
164
165# http://www.python.org/doc/2.2.3/whatsnew/node5.html
166from __future__ import generators
167
168__version__ = "$URL$ $Rev$"
169
170from array import array
171try: # See :pyver:old
172    import itertools
173except:
174    pass
175import math
176# http://www.python.org/doc/2.4.4/lib/module-operator.html
177import operator
178import struct
179import sys
180import zlib
181# http://www.python.org/doc/2.4.4/lib/module-warnings.html
182import warnings
183try:
184    import pyximport
185    pyximport.install()
186    import cpngfilters as pngfilters
187except ImportError:
188    pass
189
190
191__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array']
192
193
194# The PNG signature.
195# http://www.w3.org/TR/PNG/#5PNG-file-signature
196_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10)
197
198_adam7 = ((0, 0, 8, 8),
199          (4, 0, 8, 8),
200          (0, 4, 4, 8),
201          (2, 0, 4, 4),
202          (0, 2, 2, 4),
203          (1, 0, 2, 2),
204          (0, 1, 1, 2))
205
206def group(s, n):
207    # See
208    # http://www.python.org/doc/2.6/library/functions.html#zip
209    return zip(*[iter(s)]*n)
210
211def isarray(x):
212    """Same as ``isinstance(x, array)`` except on Python 2.2, where it
213    always returns ``False``.  This helps PyPNG work on Python 2.2.
214    """
215
216    try:
217        return isinstance(x, array)
218    except:
219        return False
220
221try:  # see :pyver:old
222    array.tostring
223except:
224    def tostring(row):
225        l = len(row)
226        return struct.pack('%dB' % l, *row)
227else:
228    def tostring(row):
229        """Convert row of bytes to string.  Expects `row` to be an
230        ``array``.
231        """
232        return row.tostring()
233
234# Conditionally convert to bytes.  Works on Python 2 and Python 3.
235try:
236    bytes('', 'ascii')
237    def strtobytes(x): return bytes(x, 'iso8859-1')
238    def bytestostr(x): return str(x, 'iso8859-1')
239except:
240    strtobytes = str
241    bytestostr = str
242
243def interleave_planes(ipixels, apixels, ipsize, apsize):
244    """
245    Interleave (colour) planes, e.g. RGB + A = RGBA.
246
247    Return an array of pixels consisting of the `ipsize` elements of data
248    from each pixel in `ipixels` followed by the `apsize` elements of data
249    from each pixel in `apixels`.  Conventionally `ipixels` and
250    `apixels` are byte arrays so the sizes are bytes, but it actually
251    works with any arrays of the same type.  The returned array is the
252    same type as the input arrays which should be the same type as each other.
253    """
254
255    itotal = len(ipixels)
256    atotal = len(apixels)
257    newtotal = itotal + atotal
258    newpsize = ipsize + apsize
259    # Set up the output buffer
260    # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356
261    out = array(ipixels.typecode)
262    # It's annoying that there is no cheap way to set the array size :-(
263    out.extend(ipixels)
264    out.extend(apixels)
265    # Interleave in the pixel data
266    for i in range(ipsize):
267        out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize]
268    for i in range(apsize):
269        out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize]
270    return out
271
272def check_palette(palette):
273    """Check a palette argument (to the :class:`Writer` class) for validity.
274    Returns the palette as a list if okay; raises an exception otherwise.
275    """
276
277    # None is the default and is allowed.
278    if palette is None:
279        return None
280
281    p = list(palette)
282    if not (0 < len(p) <= 256):
283        raise ValueError("a palette must have between 1 and 256 entries")
284    seen_triple = False
285    for i,t in enumerate(p):
286        if len(t) not in (3,4):
287            raise ValueError(
288              "palette entry %d: entries must be 3- or 4-tuples." % i)
289        if len(t) == 3:
290            seen_triple = True
291        if seen_triple and len(t) == 4:
292            raise ValueError(
293              "palette entry %d: all 4-tuples must precede all 3-tuples" % i)
294        for x in t:
295            if int(x) != x or not(0 <= x <= 255):
296                raise ValueError(
297                  "palette entry %d: values must be integer: 0 <= x <= 255" % i)
298    return p
299
300class Error(Exception):
301    prefix = 'Error'
302    def __str__(self):
303        return self.prefix + ': ' + ' '.join(self.args)
304
305class FormatError(Error):
306    """Problem with input file format.  In other words, PNG file does
307    not conform to the specification in some way and is invalid.
308    """
309
310    prefix = 'FormatError'
311
312class ChunkError(FormatError):
313    prefix = 'ChunkError'
314
315
316class Writer:
317    """
318    PNG encoder in pure Python.
319    """
320
321    def __init__(self, width=None, height=None,
322                 size=None,
323                 greyscale=False,
324                 alpha=False,
325                 bitdepth=8,
326                 palette=None,
327                 transparent=None,
328                 background=None,
329                 gamma=None,
330                 compression=None,
331                 interlace=False,
332                 bytes_per_sample=None, # deprecated
333                 planes=None,
334                 colormap=None,
335                 maxval=None,
336                 chunk_limit=2**20):
337        """
338        Create a PNG encoder object.
339
340        Arguments:
341
342        width, height
343          Image size in pixels, as two separate arguments.
344        size
345          Image size (w,h) in pixels, as single argument.
346        greyscale
347          Input data is greyscale, not RGB.
348        alpha
349          Input data has alpha channel (RGBA or LA).
350        bitdepth
351          Bit depth: from 1 to 16.
352        palette
353          Create a palette for a colour mapped image (colour type 3).
354        transparent
355          Specify a transparent colour (create a ``tRNS`` chunk).
356        background
357          Specify a default background colour (create a ``bKGD`` chunk).
358        gamma
359          Specify a gamma value (create a ``gAMA`` chunk).
360        compression
361          zlib compression level: 0 (none) to 9 (more compressed); default: -1 or None.
362        interlace
363          Create an interlaced image.
364        chunk_limit
365          Write multiple ``IDAT`` chunks to save memory.
366
367        The image size (in pixels) can be specified either by using the
368        `width` and `height` arguments, or with the single `size`
369        argument.  If `size` is used it should be a pair (*width*,
370        *height*).
371
372        `greyscale` and `alpha` are booleans that specify whether
373        an image is greyscale (or colour), and whether it has an
374        alpha channel (or not).
375
376        `bitdepth` specifies the bit depth of the source pixel values.
377        Each source pixel value must be an integer between 0 and
378        ``2**bitdepth-1``.  For example, 8-bit images have values
379        between 0 and 255.  PNG only stores images with bit depths of
380        1,2,4,8, or 16.  When `bitdepth` is not one of these values,
381        the next highest valid bit depth is selected, and an ``sBIT``
382        (significant bits) chunk is generated that specifies the original
383        precision of the source image.  In this case the supplied pixel
384        values will be rescaled to fit the range of the selected bit depth.
385
386        The details of which bit depth / colour model combinations the
387        PNG file format supports directly, are somewhat arcane
388        (refer to the PNG specification for full details).  Briefly:
389        "small" bit depths (1,2,4) are only allowed with greyscale and
390        colour mapped images; colour mapped images cannot have bit depth
391        16.
392
393        For colour mapped images (in other words, when the `palette`
394        argument is specified) the `bitdepth` argument must match one of
395        the valid PNG bit depths: 1, 2, 4, or 8.  (It is valid to have a
396        PNG image with a palette and an ``sBIT`` chunk, but the meaning
397        is slightly different; it would be awkward to press the
398        `bitdepth` argument into service for this.)
399
400        The `palette` option, when specified, causes a colour mapped image
401        to be created: the PNG colour type is set to 3; greyscale
402        must not be set; alpha must not be set; transparent must
403        not be set; the bit depth must be 1,2,4, or 8.  When a colour
404        mapped image is created, the pixel values are palette indexes
405        and the `bitdepth` argument specifies the size of these indexes
406        (not the size of the colour values in the palette).
407
408        The palette argument value should be a sequence of 3- or
409        4-tuples.  3-tuples specify RGB palette entries; 4-tuples
410        specify RGBA palette entries.  If both 4-tuples and 3-tuples
411        appear in the sequence then all the 4-tuples must come
412        before all the 3-tuples.  A ``PLTE`` chunk is created; if there
413        are 4-tuples then a ``tRNS`` chunk is created as well.  The
414        ``PLTE`` chunk will contain all the RGB triples in the same
415        sequence; the ``tRNS`` chunk will contain the alpha channel for
416        all the 4-tuples, in the same sequence.  Palette entries
417        are always 8-bit.
418
419        If specified, the `transparent` and `background` parameters must
420        be a tuple with three integer values for red, green, blue, or
421        a simple integer (or singleton tuple) for a greyscale image.
422
423        If specified, the `gamma` parameter must be a positive number
424        (generally, a float).  A ``gAMA`` chunk will be created.  Note that
425        this will not change the values of the pixels as they appear in
426        the PNG file, they are assumed to have already been converted
427        appropriately for the gamma specified.
428
429        The `compression` argument specifies the compression level to
430        be used by the ``zlib`` module.  Values from 1 to 9 specify
431        compression, with 9 being "more compressed" (usually smaller
432        and slower, but it doesn't always work out that way).  0 means
433        no compression.  -1 and ``None`` both mean that the default
434        level of compession will be picked by the ``zlib`` module
435        (which is generally acceptable).
436
437        If `interlace` is true then an interlaced image is created
438        (using PNG's so far only interace method, *Adam7*).  This does not
439        affect how the pixels should be presented to the encoder, rather
440        it changes how they are arranged into the PNG file.  On slow
441        connexions interlaced images can be partially decoded by the
442        browser to give a rough view of the image that is successively
443        refined as more image data appears.
444
445        .. note ::
446
447          Enabling the `interlace` option requires the entire image
448          to be processed in working memory.
449
450        `chunk_limit` is used to limit the amount of memory used whilst
451        compressing the image.  In order to avoid using large amounts of
452        memory, multiple ``IDAT`` chunks may be created.
453        """
454
455        # At the moment the `planes` argument is ignored;
456        # its purpose is to act as a dummy so that
457        # ``Writer(x, y, **info)`` works, where `info` is a dictionary
458        # returned by Reader.read and friends.
459        # Ditto for `colormap`.
460
461        # A couple of helper functions come first.  Best skipped if you
462        # are reading through.
463
464        def isinteger(x):
465            try:
466                return int(x) == x
467            except:
468                return False
469
470        def check_color(c, which):
471            """Checks that a colour argument for transparent or
472            background options is the right form.  Also "corrects" bare
473            integers to 1-tuples.
474            """
475
476            if c is None:
477                return c
478            if greyscale:
479                try:
480                    l = len(c)
481                except TypeError:
482                    c = (c,)
483                if len(c) != 1:
484                    raise ValueError("%s for greyscale must be 1-tuple" %
485                        which)
486                if not isinteger(c[0]):
487                    raise ValueError(
488                        "%s colour for greyscale must be integer" %
489                        which)
490            else:
491                if not (len(c) == 3 and
492                        isinteger(c[0]) and
493                        isinteger(c[1]) and
494                        isinteger(c[2])):
495                    raise ValueError(
496                        "%s colour must be a triple of integers" %
497                        which)
498            return c
499
500        if size:
501            if len(size) != 2:
502                raise ValueError(
503                  "size argument should be a pair (width, height)")
504            if width is not None and width != size[0]:
505                raise ValueError(
506                  "size[0] (%r) and width (%r) should match when both are used."
507                    % (size[0], width))
508            if height is not None and height != size[1]:
509                raise ValueError(
510                  "size[1] (%r) and height (%r) should match when both are used."
511                    % (size[1], height))
512            width,height = size
513        del size
514
515        if width <= 0 or height <= 0:
516            raise ValueError("width and height must be greater than zero")
517        if not isinteger(width) or not isinteger(height):
518            raise ValueError("width and height must be integers")
519        # http://www.w3.org/TR/PNG/#7Integers-and-byte-order
520        if width > 2**32-1 or height > 2**32-1:
521            raise ValueError("width and height cannot exceed 2**32-1")
522
523        if alpha and transparent is not None:
524            raise ValueError(
525                "transparent colour not allowed with alpha channel")
526
527        if bytes_per_sample is not None:
528            warnings.warn('please use bitdepth instead of bytes_per_sample',
529                          DeprecationWarning)
530            if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2):
531                raise ValueError(
532                    "bytes per sample must be .125, .25, .5, 1, or 2")
533            bitdepth = int(8*bytes_per_sample)
534        del bytes_per_sample
535        if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth:
536            raise ValueError("bitdepth (%r) must be a postive integer <= 16" %
537              bitdepth)
538
539        self.rescale = None
540        if palette:
541            if bitdepth not in (1,2,4,8):
542                raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8")
543            if transparent is not None:
544                raise ValueError("transparent and palette not compatible")
545            if alpha:
546                raise ValueError("alpha and palette not compatible")
547            if greyscale:
548                raise ValueError("greyscale and palette not compatible")
549        else:
550            # No palette, check for sBIT chunk generation.
551            if alpha or not greyscale:
552                if bitdepth not in (8,16):
553                    targetbitdepth = (8,16)[bitdepth > 8]
554                    self.rescale = (bitdepth, targetbitdepth)
555                    bitdepth = targetbitdepth
556                    del targetbitdepth
557            else:
558                assert greyscale
559                assert not alpha
560                if bitdepth not in (1,2,4,8,16):
561                    if bitdepth > 8:
562                        targetbitdepth = 16
563                    elif bitdepth == 3:
564                        targetbitdepth = 4
565                    else:
566                        assert bitdepth in (5,6,7)
567                        targetbitdepth = 8
568                    self.rescale = (bitdepth, targetbitdepth)
569                    bitdepth = targetbitdepth
570                    del targetbitdepth
571
572        if bitdepth < 8 and (alpha or not greyscale and not palette):
573            raise ValueError(
574              "bitdepth < 8 only permitted with greyscale or palette")
575        if bitdepth > 8 and palette:
576            raise ValueError(
577                "bit depth must be 8 or less for images with palette")
578
579        transparent = check_color(transparent, 'transparent')
580        background = check_color(background, 'background')
581
582        # It's important that the true boolean values (greyscale, alpha,
583        # colormap, interlace) are converted to bool because Iverson's
584        # convention is relied upon later on.
585        self.width = width
586        self.height = height
587        self.transparent = transparent
588        self.background = background
589        self.gamma = gamma
590        self.greyscale = bool(greyscale)
591        self.alpha = bool(alpha)
592        self.colormap = bool(palette)
593        self.bitdepth = int(bitdepth)
594        self.compression = compression
595        self.chunk_limit = chunk_limit
596        self.interlace = bool(interlace)
597        self.palette = check_palette(palette)
598
599        self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap
600        assert self.color_type in (0,2,3,4,6)
601
602        self.color_planes = (3,1)[self.greyscale or self.colormap]
603        self.planes = self.color_planes + self.alpha
604        # :todo: fix for bitdepth < 8
605        self.psize = (self.bitdepth/8) * self.planes
606
607    def make_palette(self):
608        """Create the byte sequences for a ``PLTE`` and if necessary a
609        ``tRNS`` chunk.  Returned as a pair (*p*, *t*).  *t* will be
610        ``None`` if no ``tRNS`` chunk is necessary.
611        """
612
613        p = array('B')
614        t = array('B')
615
616        for x in self.palette:
617            p.extend(x[0:3])
618            if len(x) > 3:
619                t.append(x[3])
620        p = tostring(p)
621        t = tostring(t)
622        if t:
623            return p,t
624        return p,None
625
626    def write(self, outfile, rows):
627        """Write a PNG image to the output file.  `rows` should be
628        an iterable that yields each row in boxed row flat pixel format.
629        The rows should be the rows of the original image, so there
630        should be ``self.height`` rows of ``self.width * self.planes`` values.
631        If `interlace` is specified (when creating the instance), then
632        an interlaced PNG file will be written.  Supply the rows in the
633        normal image order; the interlacing is carried out internally.
634
635        .. note ::
636
637          Interlacing will require the entire image to be in working memory.
638        """
639
640        if self.interlace:
641            fmt = 'BH'[self.bitdepth > 8]
642            a = array(fmt, itertools.chain(*rows))
643            return self.write_array(outfile, a)
644        else:
645            nrows = self.write_passes(outfile, rows)
646            if nrows != self.height:
647                raise ValueError(
648                  "rows supplied (%d) does not match height (%d)" %
649                  (nrows, self.height))
650
651    def write_passes(self, outfile, rows, packed=False):
652        """
653        Write a PNG image to the output file.
654
655        Most users are expected to find the :meth:`write` or
656        :meth:`write_array` method more convenient.
657
658        The rows should be given to this method in the order that
659        they appear in the output file.  For straightlaced images,
660        this is the usual top to bottom ordering, but for interlaced
661        images the rows should have already been interlaced before
662        passing them to this function.
663
664        `rows` should be an iterable that yields each row.  When
665        `packed` is ``False`` the rows should be in boxed row flat pixel
666        format; when `packed` is ``True`` each row should be a packed
667        sequence of bytes.
668
669        """
670
671        # http://www.w3.org/TR/PNG/#5PNG-file-signature
672        outfile.write(_signature)
673
674        # http://www.w3.org/TR/PNG/#11IHDR
675        write_chunk(outfile, 'IHDR',
676                    struct.pack("!2I5B", self.width, self.height,
677                                self.bitdepth, self.color_type,
678                                0, 0, self.interlace))
679
680        # See :chunk:order
681        # http://www.w3.org/TR/PNG/#11gAMA
682        if self.gamma is not None:
683            write_chunk(outfile, 'gAMA',
684                        struct.pack("!L", int(round(self.gamma*1e5))))
685
686        # See :chunk:order
687        # http://www.w3.org/TR/PNG/#11sBIT
688        if self.rescale:
689            write_chunk(outfile, 'sBIT',
690                struct.pack('%dB' % self.planes,
691                            *[self.rescale[0]]*self.planes))
692
693        # :chunk:order: Without a palette (PLTE chunk), ordering is
694        # relatively relaxed.  With one, gAMA chunk must precede PLTE
695        # chunk which must precede tRNS and bKGD.
696        # See http://www.w3.org/TR/PNG/#5ChunkOrdering
697        if self.palette:
698            p,t = self.make_palette()
699            write_chunk(outfile, 'PLTE', p)
700            if t:
701                # tRNS chunk is optional.  Only needed if palette entries
702                # have alpha.
703                write_chunk(outfile, 'tRNS', t)
704
705        # http://www.w3.org/TR/PNG/#11tRNS
706        if self.transparent is not None:
707            if self.greyscale:
708                write_chunk(outfile, 'tRNS',
709                            struct.pack("!1H", *self.transparent))
710            else:
711                write_chunk(outfile, 'tRNS',
712                            struct.pack("!3H", *self.transparent))
713
714        # http://www.w3.org/TR/PNG/#11bKGD
715        if self.background is not None:
716            if self.greyscale:
717                write_chunk(outfile, 'bKGD',
718                            struct.pack("!1H", *self.background))
719            else:
720                write_chunk(outfile, 'bKGD',
721                            struct.pack("!3H", *self.background))
722
723        # http://www.w3.org/TR/PNG/#11IDAT
724        if self.compression is not None:
725            compressor = zlib.compressobj(self.compression)
726        else:
727            compressor = zlib.compressobj()
728
729        # Choose an extend function based on the bitdepth.  The extend
730        # function packs/decomposes the pixel values into bytes and
731        # stuffs them onto the data array.
732        data = array('B')
733        if self.bitdepth == 8 or packed:
734            extend = data.extend
735        elif self.bitdepth == 16:
736            # Decompose into bytes
737            def extend(sl):
738                fmt = '!%dH' % len(sl)
739                data.extend(array('B', struct.pack(fmt, *sl)))
740        else:
741            # Pack into bytes
742            assert self.bitdepth < 8
743            # samples per byte
744            spb = int(8/self.bitdepth)
745            def extend(sl):
746                a = array('B', sl)
747                # Adding padding bytes so we can group into a whole
748                # number of spb-tuples.
749                l = float(len(a))
750                extra = math.ceil(l / float(spb))*spb - l
751                a.extend([0]*int(extra))
752                # Pack into bytes
753                l = group(a, spb)
754                l = map(lambda e: reduce(lambda x,y:
755                                           (x << self.bitdepth) + y, e), l)
756                data.extend(l)
757        if self.rescale:
758            oldextend = extend
759            factor = \
760              float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1)
761            def extend(sl):
762                oldextend(map(lambda x: int(round(factor*x)), sl))
763
764        # Build the first row, testing mostly to see if we need to
765        # changed the extend function to cope with NumPy integer types
766        # (they cause our ordinary definition of extend to fail, so we
767        # wrap it).  See
768        # http://code.google.com/p/pypng/issues/detail?id=44
769        enumrows = enumerate(rows)
770        del rows
771
772        # First row's filter type.
773        data.append(0)
774        # :todo: Certain exceptions in the call to ``.next()`` or the
775        # following try would indicate no row data supplied.
776        # Should catch.
777        i,row = enumrows.next()
778        try:
779            # If this fails...
780            extend(row)
781        except:
782            # ... try a version that converts the values to int first.
783            # Not only does this work for the (slightly broken) NumPy
784            # types, there are probably lots of other, unknown, "nearly"
785            # int types it works for.
786            def wrapmapint(f):
787                return lambda sl: f(map(int, sl))
788            extend = wrapmapint(extend)
789            del wrapmapint
790            extend(row)
791
792        for i,row in enumrows:
793            # Add "None" filter type.  Currently, it's essential that
794            # this filter type be used for every scanline as we do not
795            # mark the first row of a reduced pass image; that means we
796            # could accidentally compute the wrong filtered scanline if
797            # we used "up", "average", or "paeth" on such a line.
798            data.append(0)
799            extend(row)
800            if len(data) > self.chunk_limit:
801                compressed = compressor.compress(tostring(data))
802                if len(compressed):
803                    # print >> sys.stderr, len(data), len(compressed)
804                    write_chunk(outfile, 'IDAT', compressed)
805                # Because of our very witty definition of ``extend``,
806                # above, we must re-use the same ``data`` object.  Hence
807                # we use ``del`` to empty this one, rather than create a
808                # fresh one (which would be my natural FP instinct).
809                del data[:]
810        if len(data):
811            compressed = compressor.compress(tostring(data))
812        else:
813            compressed = ''
814        flushed = compressor.flush()
815        if len(compressed) or len(flushed):
816            # print >> sys.stderr, len(data), len(compressed), len(flushed)
817            write_chunk(outfile, 'IDAT', compressed + flushed)
818        # http://www.w3.org/TR/PNG/#11IEND
819        write_chunk(outfile, 'IEND')
820        return i+1
821
822    def write_array(self, outfile, pixels):
823        """
824        Write an array in flat row flat pixel format as a PNG file on
825        the output file.  See also :meth:`write` method.
826        """
827
828        if self.interlace:
829            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
830        else:
831            self.write_passes(outfile, self.array_scanlines(pixels))
832
833    def write_packed(self, outfile, rows):
834        """
835        Write PNG file to `outfile`.  The pixel data comes from `rows`
836        which should be in boxed row packed format.  Each row should be
837        a sequence of packed bytes.
838
839        Technically, this method does work for interlaced images but it
840        is best avoided.  For interlaced images, the rows should be
841        presented in the order that they appear in the file.
842
843        This method should not be used when the source image bit depth
844        is not one naturally supported by PNG; the bit depth should be
845        1, 2, 4, 8, or 16.
846        """
847
848        if self.rescale:
849            raise Error("write_packed method not suitable for bit depth %d" %
850              self.rescale[0])
851        return self.write_passes(outfile, rows, packed=True)
852
853    def convert_pnm(self, infile, outfile):
854        """
855        Convert a PNM file containing raw pixel data into a PNG file
856        with the parameters set in the writer object.  Works for
857        (binary) PGM, PPM, and PAM formats.
858        """
859
860        if self.interlace:
861            pixels = array('B')
862            pixels.fromfile(infile,
863                            (self.bitdepth/8) * self.color_planes *
864                            self.width * self.height)
865            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
866        else:
867            self.write_passes(outfile, self.file_scanlines(infile))
868
869    def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile):
870        """
871        Convert a PPM and PGM file containing raw pixel data into a
872        PNG outfile with the parameters set in the writer object.
873        """
874        pixels = array('B')
875        pixels.fromfile(ppmfile,
876                        (self.bitdepth/8) * self.color_planes *
877                        self.width * self.height)
878        apixels = array('B')
879        apixels.fromfile(pgmfile,
880                         (self.bitdepth/8) *
881                         self.width * self.height)
882        pixels = interleave_planes(pixels, apixels,
883                                   (self.bitdepth/8) * self.color_planes,
884                                   (self.bitdepth/8))
885        if self.interlace:
886            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
887        else:
888            self.write_passes(outfile, self.array_scanlines(pixels))
889
890    def file_scanlines(self, infile):
891        """
892        Generates boxed rows in flat pixel format, from the input file
893        `infile`.  It assumes that the input file is in a "Netpbm-like"
894        binary format, and is positioned at the beginning of the first
895        pixel.  The number of pixels to read is taken from the image
896        dimensions (`width`, `height`, `planes`) and the number of bytes
897        per value is implied by the image `bitdepth`.
898        """
899
900        # Values per row
901        vpr = self.width * self.planes
902        row_bytes = vpr
903        if self.bitdepth > 8:
904            assert self.bitdepth == 16
905            row_bytes *= 2
906            fmt = '>%dH' % vpr
907            def line():
908                return array('H', struct.unpack(fmt, infile.read(row_bytes)))
909        else:
910            def line():
911                scanline = array('B', infile.read(row_bytes))
912                return scanline
913        for y in range(self.height):
914            yield line()
915
916    def array_scanlines(self, pixels):
917        """
918        Generates boxed rows (flat pixels) from flat rows (flat pixels)
919        in an array.
920        """
921
922        # Values per row
923        vpr = self.width * self.planes
924        stop = 0
925        for y in range(self.height):
926            start = stop
927            stop = start + vpr
928            yield pixels[start:stop]
929
930    def array_scanlines_interlace(self, pixels):
931        """
932        Generator for interlaced scanlines from an array.  `pixels` is
933        the full source image in flat row flat pixel format.  The
934        generator yields each scanline of the reduced passes in turn, in
935        boxed row flat pixel format.
936        """
937
938        # http://www.w3.org/TR/PNG/#8InterlaceMethods
939        # Array type.
940        fmt = 'BH'[self.bitdepth > 8]
941        # Value per row
942        vpr = self.width * self.planes
943        for xstart, ystart, xstep, ystep in _adam7:
944            if xstart >= self.width:
945                continue
946            # Pixels per row (of reduced image)
947            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
948            # number of values in reduced image row.
949            row_len = ppr*self.planes
950            for y in range(ystart, self.height, ystep):
951                if xstep == 1:
952                    offset = y * vpr
953                    yield pixels[offset:offset+vpr]
954                else:
955                    row = array(fmt)
956                    # There's no easier way to set the length of an array
957                    row.extend(pixels[0:row_len])
958                    offset = y * vpr + xstart * self.planes
959                    end_offset = (y+1) * vpr
960                    skip = self.planes * xstep
961                    for i in range(self.planes):
962                        row[i::self.planes] = \
963                            pixels[offset+i:end_offset:skip]
964                    yield row
965
966def write_chunk(outfile, tag, data=strtobytes('')):
967    """
968    Write a PNG chunk to the output file, including length and
969    checksum.
970    """
971
972    # http://www.w3.org/TR/PNG/#5Chunk-layout
973    outfile.write(struct.pack("!I", len(data)))
974    tag = strtobytes(tag)
975    outfile.write(tag)
976    outfile.write(data)
977    checksum = zlib.crc32(tag)
978    checksum = zlib.crc32(data, checksum)
979    checksum &= 2**32-1
980    outfile.write(struct.pack("!I", checksum))
981
982def write_chunks(out, chunks):
983    """Create a PNG file by writing out the chunks."""
984
985    out.write(_signature)
986    for chunk in chunks:
987        write_chunk(out, *chunk)
988
989def filter_scanline(type, line, fo, prev=None):
990    """Apply a scanline filter to a scanline.  `type` specifies the
991    filter type (0 to 4); `line` specifies the current (unfiltered)
992    scanline as a sequence of bytes; `prev` specifies the previous
993    (unfiltered) scanline as a sequence of bytes. `fo` specifies the
994    filter offset; normally this is size of a pixel in bytes (the number
995    of bytes per sample times the number of channels), but when this is
996    < 1 (for bit depths < 8) then the filter offset is 1.
997    """
998
999    assert 0 <= type < 5
1000
1001    # The output array.  Which, pathetically, we extend one-byte at a
1002    # time (fortunately this is linear).
1003    out = array('B', [type])
1004
1005    def sub():
1006        ai = -fo
1007        for x in line:
1008            if ai >= 0:
1009                x = (x - line[ai]) & 0xff
1010            out.append(x)
1011            ai += 1
1012    def up():
1013        for i,x in enumerate(line):
1014            x = (x - prev[i]) & 0xff
1015            out.append(x)
1016    def average():
1017        ai = -fo
1018        for i,x in enumerate(line):
1019            if ai >= 0:
1020                x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff
1021            else:
1022                x = (x - (prev[i] >> 1)) & 0xff
1023            out.append(x)
1024            ai += 1
1025    def paeth():
1026        # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
1027        ai = -fo # also used for ci
1028        for i,x in enumerate(line):
1029            a = 0
1030            b = prev[i]
1031            c = 0
1032
1033            if ai >= 0:
1034                a = line[ai]
1035                c = prev[ai]
1036            p = a + b - c
1037            pa = abs(p - a)
1038            pb = abs(p - b)
1039            pc = abs(p - c)
1040            if pa <= pb and pa <= pc: Pr = a
1041            elif pb <= pc: Pr = b
1042            else: Pr = c
1043
1044            x = (x - Pr) & 0xff
1045            out.append(x)
1046            ai += 1
1047
1048    if not prev:
1049        # We're on the first line.  Some of the filters can be reduced
1050        # to simpler cases which makes handling the line "off the top"
1051        # of the image simpler.  "up" becomes "none"; "paeth" becomes
1052        # "left" (non-trivial, but true). "average" needs to be handled
1053        # specially.
1054        if type == 2: # "up"
1055            return line # type = 0
1056        elif type == 3:
1057            prev = [0]*len(line)
1058        elif type == 4: # "paeth"
1059            type = 1
1060    if type == 0:
1061        out.extend(line)
1062    elif type == 1:
1063        sub()
1064    elif type == 2:
1065        up()
1066    elif type == 3:
1067        average()
1068    else: # type == 4
1069        paeth()
1070    return out
1071
1072
1073def from_array(a, mode=None, info={}):
1074    """Create a PNG :class:`Image` object from a 2- or 3-dimensional array.
1075    One application of this function is easy PIL-style saving:
1076    ``png.from_array(pixels, 'L').save('foo.png')``.
1077
1078    .. note :
1079
1080      The use of the term *3-dimensional* is for marketing purposes
1081      only.  It doesn't actually work.  Please bear with us.  Meanwhile
1082      enjoy the complimentary snacks (on request) and please use a
1083      2-dimensional array.
1084
1085    Unless they are specified using the *info* parameter, the PNG's
1086    height and width are taken from the array size.  For a 3 dimensional
1087    array the first axis is the height; the second axis is the width;
1088    and the third axis is the channel number.  Thus an RGB image that is
1089    16 pixels high and 8 wide will use an array that is 16x8x3.  For 2
1090    dimensional arrays the first axis is the height, but the second axis
1091    is ``width*channels``, so an RGB image that is 16 pixels high and 8
1092    wide will use a 2-dimensional array that is 16x24 (each row will be
1093    8*3==24 sample values).
1094
1095    *mode* is a string that specifies the image colour format in a
1096    PIL-style mode.  It can be:
1097
1098    ``'L'``
1099      greyscale (1 channel)
1100    ``'LA'``
1101      greyscale with alpha (2 channel)
1102    ``'RGB'``
1103      colour image (3 channel)
1104    ``'RGBA'``
1105      colour image with alpha (4 channel)
1106
1107    The mode string can also specify the bit depth (overriding how this
1108    function normally derives the bit depth, see below).  Appending
1109    ``';16'`` to the mode will cause the PNG to be 16 bits per channel;
1110    any decimal from 1 to 16 can be used to specify the bit depth.
1111
1112    When a 2-dimensional array is used *mode* determines how many
1113    channels the image has, and so allows the width to be derived from
1114    the second array dimension.
1115
1116    The array is expected to be a ``numpy`` array, but it can be any
1117    suitable Python sequence.  For example, a list of lists can be used:
1118    ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``.  The exact
1119    rules are: ``len(a)`` gives the first dimension, height;
1120    ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the
1121    third dimension, unless an exception is raised in which case a
1122    2-dimensional array is assumed.  It's slightly more complicated than
1123    that because an iterator of rows can be used, and it all still
1124    works.  Using an iterator allows data to be streamed efficiently.
1125
1126    The bit depth of the PNG is normally taken from the array element's
1127    datatype (but if *mode* specifies a bitdepth then that is used
1128    instead).  The array element's datatype is determined in a way which
1129    is supposed to work both for ``numpy`` arrays and for Python
1130    ``array.array`` objects.  A 1 byte datatype will give a bit depth of
1131    8, a 2 byte datatype will give a bit depth of 16.  If the datatype
1132    does not have an implicit size, for example it is a plain Python
1133    list of lists, as above, then a default of 8 is used.
1134
1135    The *info* parameter is a dictionary that can be used to specify
1136    metadata (in the same style as the arguments to the
1137    :class:``png.Writer`` class).  For this function the keys that are
1138    useful are:
1139
1140    height
1141      overrides the height derived from the array dimensions and allows
1142      *a* to be an iterable.
1143    width
1144      overrides the width derived from the array dimensions.
1145    bitdepth
1146      overrides the bit depth derived from the element datatype (but
1147      must match *mode* if that also specifies a bit depth).
1148
1149    Generally anything specified in the
1150    *info* dictionary will override any implicit choices that this
1151    function would otherwise make, but must match any explicit ones.
1152    For example, if the *info* dictionary has a ``greyscale`` key then
1153    this must be true when mode is ``'L'`` or ``'LA'`` and false when
1154    mode is ``'RGB'`` or ``'RGBA'``.
1155    """
1156
1157    # We abuse the *info* parameter by modifying it.  Take a copy here.
1158    # (Also typechecks *info* to some extent).
1159    info = dict(info)
1160
1161    # Syntax check mode string.
1162    bitdepth = None
1163    try:
1164        mode = mode.split(';')
1165        if len(mode) not in (1,2):
1166            raise Error()
1167        if mode[0] not in ('L', 'LA', 'RGB', 'RGBA'):
1168            raise Error()
1169        if len(mode) == 2:
1170            try:
1171                bitdepth = int(mode[1])
1172            except:
1173                raise Error()
1174    except Error:
1175        raise Error("mode string should be 'RGB' or 'L;16' or similar.")
1176    mode = mode[0]
1177
1178    # Get bitdepth from *mode* if possible.
1179    if bitdepth:
1180        if info.get('bitdepth') and bitdepth != info['bitdepth']:
1181            raise Error("mode bitdepth (%d) should match info bitdepth (%d)." %
1182              (bitdepth, info['bitdepth']))
1183        info['bitdepth'] = bitdepth
1184
1185    # Fill in and/or check entries in *info*.
1186    # Dimensions.
1187    if 'size' in info:
1188        # Check width, height, size all match where used.
1189        for dimension,axis in [('width', 0), ('height', 1)]:
1190            if dimension in info:
1191                if info[dimension] != info['size'][axis]:
1192                    raise Error(
1193                      "info[%r] shhould match info['size'][%r]." %
1194                      (dimension, axis))
1195        info['width'],info['height'] = info['size']
1196    if 'height' not in info:
1197        try:
1198            l = len(a)
1199        except:
1200            raise Error(
1201              "len(a) does not work, supply info['height'] instead.")
1202        info['height'] = l
1203    # Colour format.
1204    if 'greyscale' in info:
1205        if bool(info['greyscale']) != ('L' in mode):
1206            raise Error("info['greyscale'] should match mode.")
1207    info['greyscale'] = 'L' in mode
1208    if 'alpha' in info:
1209        if bool(info['alpha']) != ('A' in mode):
1210            raise Error("info['alpha'] should match mode.")
1211    info['alpha'] = 'A' in mode
1212
1213    planes = len(mode)
1214    if 'planes' in info:
1215        if info['planes'] != planes:
1216            raise Error("info['planes'] should match mode.")
1217
1218    # In order to work out whether we the array is 2D or 3D we need its
1219    # first row, which requires that we take a copy of its iterator.
1220    # We may also need the first row to derive width and bitdepth.
1221    a,t = itertools.tee(a)
1222    row = t.next()
1223    del t
1224    try:
1225        row[0][0]
1226        threed = True
1227        testelement = row[0]
1228    except:
1229        threed = False
1230        testelement = row
1231    if 'width' not in info:
1232        if threed:
1233            width = len(row)
1234        else:
1235            width = len(row) // planes
1236        info['width'] = width
1237
1238    # Not implemented yet
1239    assert not threed
1240
1241    if 'bitdepth' not in info:
1242        try:
1243            dtype = testelement.dtype
1244            # goto the "else:" clause.  Sorry.
1245        except:
1246            try:
1247                # Try a Python array.array.
1248                bitdepth = 8 * testelement.itemsize
1249            except:
1250                # We can't determine it from the array element's
1251                # datatype, use a default of 8.
1252                bitdepth = 8
1253        else:
1254            # If we got here without exception, we now assume that
1255            # the array is a numpy array.
1256            if dtype.kind == 'b':
1257                bitdepth = 1
1258            else:
1259                bitdepth = 8 * dtype.itemsize
1260        info['bitdepth'] = bitdepth
1261
1262    for thing in 'width height bitdepth greyscale alpha'.split():
1263        assert thing in info
1264    return Image(a, info)
1265
1266# So that refugee's from PIL feel more at home.  Not documented.
1267fromarray = from_array
1268
1269class Image:
1270    """A PNG image.
1271    You can create an :class:`Image` object from an array of pixels by calling
1272    :meth:`png.from_array`.  It can be saved to disk with the
1273    :meth:`save` method."""
1274    def __init__(self, rows, info):
1275        """
1276        .. note ::
1277
1278          The constructor is not public.  Please do not call it.
1279        """
1280
1281        self.rows = rows
1282        self.info = info
1283
1284    def save(self, file):
1285        """Save the image to *file*.  If *file* looks like an open file
1286        descriptor then it is used, otherwise it is treated as a
1287        filename and a fresh file is opened.
1288
1289        In general, you can only call this method once; after it has
1290        been called the first time and the PNG image has been saved, the
1291        source data will have been streamed, and cannot be streamed
1292        again.
1293        """
1294
1295        w = Writer(**self.info)
1296
1297        try:
1298            file.write
1299            def close(): pass
1300        except:
1301            file = open(file, 'wb')
1302            def close(): file.close()
1303
1304        try:
1305            w.write(file, self.rows)
1306        finally:
1307            close()
1308
1309class _readable:
1310    """
1311    A simple file-like interface for strings and arrays.
1312    """
1313
1314    def __init__(self, buf):
1315        self.buf = buf
1316        self.offset = 0
1317
1318    def read(self, n):
1319        r = self.buf[self.offset:self.offset+n]
1320        if isarray(r):
1321            r = r.tostring()
1322        self.offset += n
1323        return r
1324
1325
1326class Reader:
1327    """
1328    PNG decoder in pure Python.
1329    """
1330
1331    def __init__(self, _guess=None, **kw):
1332        """
1333        Create a PNG decoder object.
1334
1335        The constructor expects exactly one keyword argument. If you
1336        supply a positional argument instead, it will guess the input
1337        type. You can choose among the following keyword arguments:
1338
1339        filename
1340          Name of input file (a PNG file).
1341        file
1342          A file-like object (object with a read() method).
1343        bytes
1344          ``array`` or ``string`` with PNG data.
1345
1346        """
1347        if ((_guess is not None and len(kw) != 0) or
1348            (_guess is None and len(kw) != 1)):
1349            raise TypeError("Reader() takes exactly 1 argument")
1350
1351        # Will be the first 8 bytes, later on.  See validate_signature.
1352        self.signature = None
1353        self.transparent = None
1354        # A pair of (len,type) if a chunk has been read but its data and
1355        # checksum have not (in other words the file position is just
1356        # past the 4 bytes that specify the chunk type).  See preamble
1357        # method for how this is used.
1358        self.atchunk = None
1359
1360        if _guess is not None:
1361            if isarray(_guess):
1362                kw["bytes"] = _guess
1363            elif isinstance(_guess, str):
1364                kw["filename"] = _guess
1365            elif isinstance(_guess, file):
1366                kw["file"] = _guess
1367
1368        if "filename" in kw:
1369            self.file = open(kw["filename"], "rb")
1370        elif "file" in kw:
1371            self.file = kw["file"]
1372        elif "bytes" in kw:
1373            self.file = _readable(kw["bytes"])
1374        else:
1375            raise TypeError("expecting filename, file or bytes array")
1376
1377
1378    def chunk(self, seek=None, lenient=False):
1379        """
1380        Read the next PNG chunk from the input file; returns a
1381        (*type*,*data*) tuple.  *type* is the chunk's type as a string
1382        (all PNG chunk types are 4 characters long).  *data* is the
1383        chunk's data content, as a string.
1384
1385        If the optional `seek` argument is
1386        specified then it will keep reading chunks until it either runs
1387        out of file or finds the type specified by the argument.  Note
1388        that in general the order of chunks in PNGs is unspecified, so
1389        using `seek` can cause you to miss chunks.
1390
1391        If the optional `lenient` argument evaluates to True,
1392        checksum failures will raise warnings rather than exceptions.
1393        """
1394
1395        self.validate_signature()
1396
1397        while True:
1398            # http://www.w3.org/TR/PNG/#5Chunk-layout
1399            if not self.atchunk:
1400                self.atchunk = self.chunklentype()
1401            length,type = self.atchunk
1402            self.atchunk = None
1403            data = self.file.read(length)
1404            if len(data) != length:
1405                raise ChunkError('Chunk %s too short for required %i octets.'
1406                  % (type, length))
1407            checksum = self.file.read(4)
1408            if len(checksum) != 4:
1409                raise ValueError('Chunk %s too short for checksum.', tag)
1410            if seek and type != seek:
1411                continue
1412            verify = zlib.crc32(strtobytes(type))
1413            verify = zlib.crc32(data, verify)
1414            # Whether the output from zlib.crc32 is signed or not varies
1415            # according to hideous implementation details, see
1416            # http://bugs.python.org/issue1202 .
1417            # We coerce it to be positive here (in a way which works on
1418            # Python 2.3 and older).
1419            verify &= 2**32 - 1
1420            verify = struct.pack('!I', verify)
1421            if checksum != verify:
1422                # print repr(checksum)
1423                (a, ) = struct.unpack('!I', checksum)
1424                (b, ) = struct.unpack('!I', verify)
1425                message = "Checksum error in %s chunk: 0x%08X != 0x%08X." % (type, a, b)
1426                if lenient:
1427                    warnings.warn(message, RuntimeWarning)
1428                else:
1429                    raise ChunkError(message)
1430            return type, data
1431
1432    def chunks(self):
1433        """Return an iterator that will yield each chunk as a
1434        (*chunktype*, *content*) pair.
1435        """
1436
1437        while True:
1438            t,v = self.chunk()
1439            yield t,v
1440            if t == 'IEND':
1441                break
1442
1443    def undo_filter(self, filter_type, scanline, previous):
1444        """Undo the filter for a scanline.  `scanline` is a sequence of
1445        bytes that does not include the initial filter type byte.
1446        `previous` is decoded previous scanline (for straightlaced
1447        images this is the previous pixel row, but for interlaced
1448        images, it is the previous scanline in the reduced image, which
1449        in general is not the previous pixel row in the final image).
1450        When there is no previous scanline (the first row of a
1451        straightlaced image, or the first row in one of the passes in an
1452        interlaced image), then this argument should be ``None``.
1453
1454        The scanline will have the effects of filtering removed, and the
1455        result will be returned as a fresh sequence of bytes.
1456        """
1457
1458        # :todo: Would it be better to update scanline in place?
1459        # Yes, with the Cython extension making the undo_filter fast,
1460        # updating scanline inplace makes the code 3 times faster
1461        # (reading 50 images of 800x800 went from 40s to 16s)
1462        result = scanline
1463
1464        if filter_type == 0:
1465            return result
1466
1467        if filter_type not in (1,2,3,4):
1468            raise FormatError('Invalid PNG Filter Type.'
1469              '  See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .')
1470
1471        # Filter unit.  The stride from one pixel to the corresponding
1472        # byte from the previous previous.  Normally this is the pixel
1473        # size in bytes, but when this is smaller than 1, the previous
1474        # byte is used instead.
1475        fu = max(1, self.psize)
1476
1477        # For the first line of a pass, synthesize a dummy previous
1478        # line.  An alternative approach would be to observe that on the
1479        # first line 'up' is the same as 'null', 'paeth' is the same
1480        # as 'sub', with only 'average' requiring any special case.
1481        if not previous:
1482            previous = array('B', [0]*len(scanline))
1483
1484        def sub():
1485            """Undo sub filter."""
1486
1487            ai = 0
1488            # Loops starts at index fu.  Observe that the initial part
1489            # of the result is already filled in correctly with
1490            # scanline.
1491            for i in range(fu, len(result)):
1492                x = scanline[i]
1493                a = result[ai]
1494                result[i] = (x + a) & 0xff
1495                ai += 1
1496
1497        def up():
1498            """Undo up filter."""
1499
1500            for i in range(len(result)):
1501                x = scanline[i]
1502                b = previous[i]
1503                result[i] = (x + b) & 0xff
1504
1505        def average():
1506            """Undo average filter."""
1507
1508            ai = -fu
1509            for i in range(len(result)):
1510                x = scanline[i]
1511                if ai < 0:
1512                    a = 0
1513                else:
1514                    a = result[ai]
1515                b = previous[i]
1516                result[i] = (x + ((a + b) >> 1)) & 0xff
1517                ai += 1
1518
1519        def paeth():
1520            """Undo Paeth filter."""
1521
1522            # Also used for ci.
1523            ai = -fu
1524            for i in range(len(result)):
1525                x = scanline[i]
1526                if ai < 0:
1527                    a = c = 0
1528                else:
1529                    a = result[ai]
1530                    c = previous[ai]
1531                b = previous[i]
1532                p = a + b - c
1533                pa = abs(p - a)
1534                pb = abs(p - b)
1535                pc = abs(p - c)
1536                if pa <= pb and pa <= pc:
1537                    pr = a
1538                elif pb <= pc:
1539                    pr = b
1540                else:
1541                    pr = c
1542                result[i] = (x + pr) & 0xff
1543                ai += 1
1544
1545        # Call appropriate filter algorithm.  Note that 0 has already
1546        # been dealt with.
1547        (None,
1548         pngfilters.undo_filter_sub,
1549         pngfilters.undo_filter_up,
1550         pngfilters.undo_filter_average,
1551         pngfilters.undo_filter_paeth)[filter_type](fu, scanline, previous, result)
1552        return result
1553
1554    def deinterlace(self, raw):
1555        """
1556        Read raw pixel data, undo filters, deinterlace, and flatten.
1557        Return in flat row flat pixel format.
1558        """
1559
1560        # print >> sys.stderr, ("Reading interlaced, w=%s, r=%s, planes=%s," +
1561        #     " bpp=%s") % (self.width, self.height, self.planes, self.bps)
1562        # Values per row (of the target image)
1563        vpr = self.width * self.planes
1564
1565        # Make a result array, and make it big enough.  Interleaving
1566        # writes to the output array randomly (well, not quite), so the
1567        # entire output array must be in memory.
1568        fmt = 'BH'[self.bitdepth > 8]
1569        a = array(fmt, [0]*vpr*self.height)
1570        source_offset = 0
1571
1572        for xstart, ystart, xstep, ystep in _adam7:
1573            # print >> sys.stderr, "Adam7: start=%s,%s step=%s,%s" % (
1574            #     xstart, ystart, xstep, ystep)
1575            if xstart >= self.width:
1576                continue
1577            # The previous (reconstructed) scanline.  None at the
1578            # beginning of a pass to indicate that there is no previous
1579            # line.
1580            recon = None
1581            # Pixels per row (reduced pass image)
1582            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
1583            # Row size in bytes for this pass.
1584            row_size = int(math.ceil(self.psize * ppr))
1585            for y in range(ystart, self.height, ystep):
1586                filter_type = raw[source_offset]
1587                source_offset += 1
1588                scanline = raw[source_offset:source_offset+row_size]
1589                source_offset += row_size
1590                recon = self.undo_filter(filter_type, scanline, recon)
1591                # Convert so that there is one element per pixel value
1592                flat = self.serialtoflat(recon, ppr)
1593                if xstep == 1:
1594                    assert xstart == 0
1595                    offset = y * vpr
1596                    a[offset:offset+vpr] = flat
1597                else:
1598                    offset = y * vpr + xstart * self.planes
1599                    end_offset = (y+1) * vpr
1600                    skip = self.planes * xstep
1601                    for i in range(self.planes):
1602                        a[offset+i:end_offset:skip] = \
1603                            flat[i::self.planes]
1604        return a
1605
1606    def iterboxed(self, rows):
1607        """Iterator that yields each scanline in boxed row flat pixel
1608        format.  `rows` should be an iterator that yields the bytes of
1609        each row in turn.
1610        """
1611
1612        def asvalues(raw):
1613            """Convert a row of raw bytes into a flat row.  Result may
1614            or may not share with argument"""
1615
1616            if self.bitdepth == 8:
1617                return raw
1618            if self.bitdepth == 16:
1619                raw = tostring(raw)
1620                return array('H', struct.unpack('!%dH' % (len(raw)//2), raw))
1621            assert self.bitdepth < 8
1622            width = self.width
1623            # Samples per byte
1624            spb = 8//self.bitdepth
1625            out = array('B')
1626            mask = 2**self.bitdepth - 1
1627            shifts = map(self.bitdepth.__mul__, reversed(range(spb)))
1628            for o in raw:
1629                out.extend(map(lambda i: mask&(o>>i), shifts))
1630            return out[:width]
1631
1632        return itertools.imap(asvalues, rows)
1633
1634    def serialtoflat(self, bytes, width=None):
1635        """Convert serial format (byte stream) pixel data to flat row
1636        flat pixel.
1637        """
1638
1639        if self.bitdepth == 8:
1640            return bytes
1641        if self.bitdepth == 16:
1642            bytes = tostring(bytes)
1643            return array('H',
1644              struct.unpack('!%dH' % (len(bytes)//2), bytes))
1645        assert self.bitdepth < 8
1646        if width is None:
1647            width = self.width
1648        # Samples per byte
1649        spb = 8//self.bitdepth
1650        out = array('B')
1651        mask = 2**self.bitdepth - 1
1652        shifts = map(self.bitdepth.__mul__, reversed(range(spb)))
1653        l = width
1654        for o in bytes:
1655            out.extend([(mask&(o>>s)) for s in shifts][:l])
1656            l -= spb
1657            if l <= 0:
1658                l = width
1659        return out
1660
1661    def iterstraight(self, raw):
1662        """Iterator that undoes the effect of filtering, and yields each
1663        row in serialised format (as a sequence of bytes).  Assumes input
1664        is straightlaced.  `raw` should be an iterable that yields the
1665        raw bytes in chunks of arbitrary size."""
1666
1667        # length of row, in bytes
1668        rb = self.row_bytes
1669        a = array('B')
1670        # The previous (reconstructed) scanline.  None indicates first
1671        # line of image.
1672        recon = None
1673        for some in raw:
1674            a.extend(some)
1675            while len(a) >= rb + 1:
1676                filter_type = a[0]
1677                scanline = a[1:rb+1]
1678                del a[:rb+1]
1679                recon = self.undo_filter(filter_type, scanline, recon)
1680                yield recon
1681        if len(a) != 0:
1682            # :file:format We get here with a file format error: when the
1683            # available bytes (after decompressing) do not pack into exact
1684            # rows.
1685            raise FormatError(
1686              'Wrong size for decompressed IDAT chunk.')
1687        assert len(a) == 0
1688
1689    def validate_signature(self):
1690        """If signature (header) has not been read then read and
1691        validate it; otherwise do nothing.
1692        """
1693
1694        if self.signature:
1695            return
1696        self.signature = self.file.read(8)
1697        if self.signature != _signature:
1698            raise FormatError("PNG file has invalid signature.")
1699
1700    def preamble(self, lenient=False):
1701        """
1702        Extract the image metadata by reading the initial part of the PNG
1703        file up to the start of the ``IDAT`` chunk.  All the chunks that
1704        precede the ``IDAT`` chunk are read and either processed for
1705        metadata or discarded.
1706
1707        If the optional `lenient` argument evaluates to True,
1708        checksum failures will raise warnings rather than exceptions.
1709        """
1710
1711        self.validate_signature()
1712
1713        while True:
1714            if not self.atchunk:
1715                self.atchunk = self.chunklentype()
1716                if self.atchunk is None:
1717                    raise FormatError(
1718                      'This PNG file has no IDAT chunks.')
1719            if self.atchunk[1] == 'IDAT':
1720                return
1721            self.process_chunk(lenient=lenient)
1722
1723    def chunklentype(self):
1724        """Reads just enough of the input to determine the next
1725        chunk's length and type, returned as a (*length*, *type*) pair
1726        where *type* is a string.  If there are no more chunks, ``None``
1727        is returned.
1728        """
1729
1730        x = self.file.read(8)
1731        if not x:
1732            return None
1733        if len(x) != 8:
1734            raise FormatError(
1735              'End of file whilst reading chunk length and type.')
1736        length,type = struct.unpack('!I4s', x)
1737        type = bytestostr(type)
1738        if length > 2**31-1:
1739            raise FormatError('Chunk %s is too large: %d.' % (type,length))
1740        return length,type
1741
1742    def process_chunk(self, lenient=False):
1743        """Process the next chunk and its data.  This only processes the
1744        following chunk types, all others are ignored: ``IHDR``,
1745        ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``.
1746
1747        If the optional `lenient` argument evaluates to True,
1748        checksum failures will raise warnings rather than exceptions.
1749        """
1750
1751        type, data = self.chunk(lenient=lenient)
1752        if type == 'IHDR':
1753            # http://www.w3.org/TR/PNG/#11IHDR
1754            if len(data) != 13:
1755                raise FormatError('IHDR chunk has incorrect length.')
1756            (self.width, self.height, self.bitdepth, self.color_type,
1757             self.compression, self.filter,
1758             self.interlace) = struct.unpack("!2I5B", data)
1759
1760            # Check that the header specifies only valid combinations.
1761            if self.bitdepth not in (1,2,4,8,16):
1762                raise Error("invalid bit depth %d" % self.bitdepth)
1763            if self.color_type not in (0,2,3,4,6):
1764                raise Error("invalid colour type %d" % self.color_type)
1765            # Check indexed (palettized) images have 8 or fewer bits
1766            # per pixel; check only indexed or greyscale images have
1767            # fewer than 8 bits per pixel.
1768            if ((self.color_type & 1 and self.bitdepth > 8) or
1769                (self.bitdepth < 8 and self.color_type not in (0,3))):
1770                raise FormatError("Illegal combination of bit depth (%d)"
1771                  " and colour type (%d)."
1772                  " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
1773                  % (self.bitdepth, self.color_type))
1774            if self.compression != 0:
1775                raise Error("unknown compression method %d" % self.compression)
1776            if self.filter != 0:
1777                raise FormatError("Unknown filter method %d,"
1778                  " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ."
1779                  % self.filter)
1780            if self.interlace not in (0,1):
1781                raise FormatError("Unknown interlace method %d,"
1782                  " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ."
1783                  % self.interlace)
1784
1785            # Derived values
1786            # http://www.w3.org/TR/PNG/#6Colour-values
1787            colormap =  bool(self.color_type & 1)
1788            greyscale = not (self.color_type & 2)
1789            alpha = bool(self.color_type & 4)
1790            color_planes = (3,1)[greyscale or colormap]
1791            planes = color_planes + alpha
1792
1793            self.colormap = colormap
1794            self.greyscale = greyscale
1795            self.alpha = alpha
1796            self.color_planes = color_planes
1797            self.planes = planes
1798            self.psize = float(self.bitdepth)/float(8) * planes
1799            if int(self.psize) == self.psize:
1800                self.psize = int(self.psize)
1801            self.row_bytes = int(math.ceil(self.width * self.psize))
1802            # Stores PLTE chunk if present, and is used to check
1803            # chunk ordering constraints.
1804            self.plte = None
1805            # Stores tRNS chunk if present, and is used to check chunk
1806            # ordering constraints.
1807            self.trns = None
1808            # Stores sbit chunk if present.
1809            self.sbit = None
1810        elif type == 'PLTE':
1811            # http://www.w3.org/TR/PNG/#11PLTE
1812            if self.plte:
1813                warnings.warn("Multiple PLTE chunks present.")
1814            self.plte = data
1815            if len(data) % 3 != 0:
1816                raise FormatError(
1817                  "PLTE chunk's length should be a multiple of 3.")
1818            if len(data) > (2**self.bitdepth)*3:
1819                raise FormatError("PLTE chunk is too long.")
1820            if len(data) == 0:
1821                raise FormatError("Empty PLTE is not allowed.")
1822        elif type == 'bKGD':
1823            try:
1824                if self.colormap:
1825                    if not self.plte:
1826                        warnings.warn(
1827                          "PLTE chunk is required before bKGD chunk.")
1828                    self.background = struct.unpack('B', data)
1829                else:
1830                    self.background = struct.unpack("!%dH" % self.color_planes,
1831                      data)
1832            except struct.error:
1833                raise FormatError("bKGD chunk has incorrect length.")
1834        elif type == 'tRNS':
1835            # http://www.w3.org/TR/PNG/#11tRNS
1836            self.trns = data
1837            if self.colormap:
1838                if not self.plte:
1839                    warnings.warn("PLTE chunk is required before tRNS chunk.")
1840                else:
1841                    if len(data) > len(self.plte)/3:
1842                        # Was warning, but promoted to Error as it
1843                        # would otherwise cause pain later on.
1844                        raise FormatError("tRNS chunk is too long.")
1845            else:
1846                if self.alpha:
1847                    raise FormatError(
1848                      "tRNS chunk is not valid with colour type %d." %
1849                      self.color_type)
1850                try:
1851                    self.transparent = \
1852                        struct.unpack("!%dH" % self.color_planes, data)
1853                except struct.error:
1854                    raise FormatError("tRNS chunk has incorrect length.")
1855        elif type == 'gAMA':
1856            try:
1857                self.gamma = struct.unpack("!L", data)[0] / 100000.0
1858            except struct.error:
1859                raise FormatError("gAMA chunk has incorrect length.")
1860        elif type == 'sBIT':
1861            self.sbit = data
1862            if (self.colormap and len(data) != 3 or
1863                not self.colormap and len(data) != self.planes):
1864                raise FormatError("sBIT chunk has incorrect length.")
1865
1866    def read(self, lenient=False):
1867        """
1868        Read the PNG file and decode it.  Returns (`width`, `height`,
1869        `pixels`, `metadata`).
1870
1871        May use excessive memory.
1872
1873        `pixels` are returned in boxed row flat pixel format.
1874
1875        If the optional `lenient` argument evaluates to True,
1876        checksum failures will raise warnings rather than exceptions.
1877        """
1878
1879        def iteridat():
1880            """Iterator that yields all the ``IDAT`` chunks as strings."""
1881            while True:
1882                try:
1883                    type, data = self.chunk(lenient=lenient)
1884                except ValueError, e:
1885                    raise ChunkError(e.args[0])
1886                if type == 'IEND':
1887                    # http://www.w3.org/TR/PNG/#11IEND
1888                    break
1889                if type != 'IDAT':
1890                    continue
1891                # type == 'IDAT'
1892                # http://www.w3.org/TR/PNG/#11IDAT
1893                if self.colormap and not self.plte:
1894                    warnings.warn("PLTE chunk is required before IDAT chunk")
1895                yield data
1896
1897        def iterdecomp(idat):
1898            """Iterator that yields decompressed strings.  `idat` should
1899            be an iterator that yields the ``IDAT`` chunk data.
1900            """
1901
1902            # Currently, with no max_length paramter to decompress, this
1903            # routine will do one yield per IDAT chunk.  So not very
1904            # incremental.
1905            d = zlib.decompressobj()
1906            # Each IDAT chunk is passed to the decompressor, then any
1907            # remaining state is decompressed out.
1908            for data in idat:
1909                # :todo: add a max_length argument here to limit output
1910                # size.
1911                yield array('B', d.decompress(data))
1912            yield array('B', d.flush())
1913
1914        self.preamble(lenient=lenient)
1915        raw = iterdecomp(iteridat())
1916
1917        if self.interlace:
1918            raw = array('B', itertools.chain(*raw))
1919            arraycode = 'BH'[self.bitdepth>8]
1920            # Like :meth:`group` but producing an array.array object for
1921            # each row.
1922            pixels = itertools.imap(lambda *row: array(arraycode, row),
1923                       *[iter(self.deinterlace(raw))]*self.width*self.planes)
1924        else:
1925            pixels = self.iterboxed(self.iterstraight(raw))
1926        meta = dict()
1927        for attr in 'greyscale alpha planes bitdepth interlace'.split():
1928            meta[attr] = getattr(self, attr)
1929        meta['size'] = (self.width, self.height)
1930        for attr in 'gamma transparent background'.split():
1931            a = getattr(self, attr, None)
1932            if a is not None:
1933                meta[attr] = a
1934        if self.plte:
1935            meta['palette'] = self.palette()
1936        return self.width, self.height, pixels, meta
1937
1938
1939    def read_flat(self):
1940        """
1941        Read a PNG file and decode it into flat row flat pixel format.
1942        Returns (*width*, *height*, *pixels*, *metadata*).
1943
1944        May use excessive memory.
1945
1946        `pixels` are returned in flat row flat pixel format.
1947
1948        See also the :meth:`read` method which returns pixels in the
1949        more stream-friendly boxed row flat pixel format.
1950        """
1951
1952        x, y, pixel, meta = self.read()
1953        arraycode = 'BH'[meta['bitdepth']>8]
1954        pixel = array(arraycode, itertools.chain(*pixel))
1955        return x, y, pixel, meta
1956
1957    def palette(self, alpha='natural'):
1958        """Returns a palette that is a sequence of 3-tuples or 4-tuples,
1959        synthesizing it from the ``PLTE`` and ``tRNS`` chunks.  These
1960        chunks should have already been processed (for example, by
1961        calling the :meth:`preamble` method).  All the tuples are the
1962        same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when
1963        there is a ``tRNS`` chunk.  Assumes that the image is colour type
1964        3 and therefore a ``PLTE`` chunk is required.
1965
1966        If the `alpha` argument is ``'force'`` then an alpha channel is
1967        always added, forcing the result to be a sequence of 4-tuples.
1968        """
1969
1970        if not self.plte:
1971            raise FormatError(
1972                "Required PLTE chunk is missing in colour type 3 image.")
1973        plte = group(array('B', self.plte), 3)
1974        if self.trns or alpha == 'force':
1975            trns = array('B', self.trns or '')
1976            trns.extend([255]*(len(plte)-len(trns)))
1977            plte = map(operator.add, plte, group(trns, 1))
1978        return plte
1979
1980    def asDirect(self):
1981        """Returns the image data as a direct representation of an
1982        ``x * y * planes`` array.  This method is intended to remove the
1983        need for callers to deal with palettes and transparency
1984        themselves.  Images with a palette (colour type 3)
1985        are converted to RGB or RGBA; images with transparency (a
1986        ``tRNS`` chunk) are converted to LA or RGBA as appropriate.
1987        When returned in this format the pixel values represent the
1988        colour value directly without needing to refer to palettes or
1989        transparency information.
1990
1991        Like the :meth:`read` method this method returns a 4-tuple:
1992
1993        (*width*, *height*, *pixels*, *meta*)
1994
1995        This method normally returns pixel values with the bit depth
1996        they have in the source image, but when the source PNG has an
1997        ``sBIT`` chunk it is inspected and can reduce the bit depth of
1998        the result pixels; pixel values will be reduced according to
1999        the bit depth specified in the ``sBIT`` chunk (PNG nerds should
2000        note a single result bit depth is used for all channels; the
2001        maximum of the ones specified in the ``sBIT`` chunk.  An RGB565
2002        image will be rescaled to 6-bit RGB666).
2003
2004        The *meta* dictionary that is returned reflects the `direct`
2005        format and not the original source image.  For example, an RGB
2006        source image with a ``tRNS`` chunk to represent a transparent
2007        colour, will have ``planes=3`` and ``alpha=False`` for the
2008        source image, but the *meta* dictionary returned by this method
2009        will have ``planes=4`` and ``alpha=True`` because an alpha
2010        channel is synthesized and added.
2011
2012        *pixels* is the pixel data in boxed row flat pixel format (just
2013        like the :meth:`read` method).
2014
2015        All the other aspects of the image data are not changed.
2016        """
2017
2018        self.preamble()
2019
2020        # Simple case, no conversion necessary.
2021        if not self.colormap and not self.trns and not self.sbit:
2022            return self.read()
2023
2024        x,y,pixels,meta = self.read()
2025
2026        if self.colormap:
2027            meta['colormap'] = False
2028            meta['alpha'] = bool(self.trns)
2029            meta['bitdepth'] = 8
2030            meta['planes'] = 3 + bool(self.trns)
2031            plte = self.palette()
2032            def iterpal(pixels):
2033                for row in pixels:
2034                    row = map(plte.__getitem__, row)
2035                    yield array('B', itertools.chain(*row))
2036            pixels = iterpal(pixels)
2037        elif self.trns:
2038            # It would be nice if there was some reasonable way of doing
2039            # this without generating a whole load of intermediate tuples.
2040            # But tuples does seem like the easiest way, with no other way
2041            # clearly much simpler or much faster.  (Actually, the L to LA
2042            # conversion could perhaps go faster (all those 1-tuples!), but
2043            # I still wonder whether the code proliferation is worth it)
2044            it = self.transparent
2045            maxval = 2**meta['bitdepth']-1
2046            planes = meta['planes']
2047            meta['alpha'] = True
2048            meta['planes'] += 1
2049            typecode = 'BH'[meta['bitdepth']>8]
2050            def itertrns(pixels):
2051                for row in pixels:
2052                    # For each row we group it into pixels, then form a
2053                    # characterisation vector that says whether each pixel
2054                    # is opaque or not.  Then we convert True/False to
2055                    # 0/maxval (by multiplication), and add it as the extra
2056                    # channel.
2057                    row = group(row, planes)
2058                    opa = map(it.__ne__, row)
2059                    opa = map(maxval.__mul__, opa)
2060                    opa = zip(opa) # convert to 1-tuples
2061                    yield array(typecode,
2062                      itertools.chain(*map(operator.add, row, opa)))
2063            pixels = itertrns(pixels)
2064        targetbitdepth = None
2065        if self.sbit:
2066            sbit = struct.unpack('%dB' % len(self.sbit), self.sbit)
2067            targetbitdepth = max(sbit)
2068            if targetbitdepth > meta['bitdepth']:
2069                raise Error('sBIT chunk %r exceeds bitdepth %d' %
2070                    (sbit,self.bitdepth))
2071            if min(sbit) <= 0:
2072                raise Error('sBIT chunk %r has a 0-entry' % sbit)
2073            if targetbitdepth == meta['bitdepth']:
2074                targetbitdepth = None
2075        if targetbitdepth:
2076            shift = meta['bitdepth'] - targetbitdepth
2077            meta['bitdepth'] = targetbitdepth
2078            def itershift(pixels):
2079                for row in pixels:
2080                    yield map(shift.__rrshift__, row)
2081            pixels = itershift(pixels)
2082        return x,y,pixels,meta
2083
2084    def asFloat(self, maxval=1.0):
2085        """Return image pixels as per :meth:`asDirect` method, but scale
2086        all pixel values to be floating point values between 0.0 and
2087        *maxval*.
2088        """
2089
2090        x,y,pixels,info = self.asDirect()
2091        sourcemaxval = 2**info['bitdepth']-1
2092        del info['bitdepth']
2093        info['maxval'] = float(maxval)
2094        factor = float(maxval)/float(sourcemaxval)
2095        def iterfloat():
2096            for row in pixels:
2097                yield map(factor.__mul__, row)
2098        return x,y,iterfloat(),info
2099
2100    def _as_rescale(self, get, targetbitdepth):
2101        """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`."""
2102
2103        width,height,pixels,meta = get()
2104        maxval = 2**meta['bitdepth'] - 1
2105        targetmaxval = 2**targetbitdepth - 1
2106        factor = float(targetmaxval) / float(maxval)
2107        meta['bitdepth'] = targetbitdepth
2108        def iterscale():
2109            for row in pixels:
2110                yield map(lambda x: int(round(x*factor)), row)
2111        if maxval == targetmaxval:
2112            return width, height, pixels, meta
2113        else:
2114            return width, height, iterscale(), meta
2115
2116    def asRGB8(self):
2117        """Return the image data as an RGB pixels with 8-bits per
2118        sample.  This is like the :meth:`asRGB` method except that
2119        this method additionally rescales the values so that they
2120        are all between 0 and 255 (8-bit).  In the case where the
2121        source image has a bit depth < 8 the transformation preserves
2122        all the information; where the source image has bit depth
2123        > 8, then rescaling to 8-bit values loses precision.  No
2124        dithering is performed.  Like :meth:`asRGB`, an alpha channel
2125        in the source image will raise an exception.
2126
2127        This function returns a 4-tuple:
2128        (*width*, *height*, *pixels*, *metadata*).
2129        *width*, *height*, *metadata* are as per the :meth:`read` method.
2130
2131        *pixels* is the pixel data in boxed row flat pixel format.
2132        """
2133
2134        return self._as_rescale(self.asRGB, 8)
2135
2136    def asRGBA8(self):
2137        """Return the image data as RGBA pixels with 8-bits per
2138        sample.  This method is similar to :meth:`asRGB8` and
2139        :meth:`asRGBA`:  The result pixels have an alpha channel, *and*
2140        values are rescaled to the range 0 to 255.  The alpha channel is
2141        synthesized if necessary (with a small speed penalty).
2142        """
2143
2144        return self._as_rescale(self.asRGBA, 8)
2145
2146    def asRGB(self):
2147        """Return image as RGB pixels.  RGB colour images are passed
2148        through unchanged; greyscales are expanded into RGB
2149        triplets (there is a small speed overhead for doing this).
2150
2151        An alpha channel in the source image will raise an
2152        exception.
2153
2154        The return values are as for the :meth:`read` method
2155        except that the *metadata* reflect the returned pixels, not the
2156        source image.  In particular, for this method
2157        ``metadata['greyscale']`` will be ``False``.
2158        """
2159
2160        width,height,pixels,meta = self.asDirect()
2161        if meta['alpha']:
2162            raise Error("will not convert image with alpha channel to RGB")
2163        if not meta['greyscale']:
2164            return width,height,pixels,meta
2165        meta['greyscale'] = False
2166        typecode = 'BH'[meta['bitdepth'] > 8]
2167        def iterrgb():
2168            for row in pixels:
2169                a = array(typecode, [0]) * 3 * width
2170                for i in range(3):
2171                    a[i::3] = row
2172                yield a
2173        return width,height,iterrgb(),meta
2174
2175    def asRGBA(self):
2176        """Return image as RGBA pixels.  Greyscales are expanded into
2177        RGB triplets; an alpha channel is synthesized if necessary.
2178        The return values are as for the :meth:`read` method
2179        except that the *metadata* reflect the returned pixels, not the
2180        source image.  In particular, for this method
2181        ``metadata['greyscale']`` will be ``False``, and
2182        ``metadata['alpha']`` will be ``True``.
2183        """
2184
2185        width,height,pixels,meta = self.asDirect()
2186        if meta['alpha'] and not meta['greyscale']:
2187            return width,height,pixels,meta
2188        typecode = 'BH'[meta['bitdepth'] > 8]
2189        maxval = 2**meta['bitdepth'] - 1
2190        maxbuffer = struct.pack('=' + typecode, maxval) * 4 * width
2191        def newarray():
2192            return array(typecode, maxbuffer)
2193
2194        if meta['alpha'] and meta['greyscale']:
2195            # LA to RGBA
2196            def convert():
2197                for row in pixels:
2198                    # Create a fresh target row, then copy L channel
2199                    # into first three target channels, and A channel
2200                    # into fourth channel.
2201                    a = newarray()
2202                    pngfilters.convert_la_to_rgba(row, a)
2203                    yield a
2204        elif meta['greyscale']:
2205            # L to RGBA
2206            def convert():
2207                for row in pixels:
2208                    a = newarray()
2209                    pngfilters.convert_l_to_rgba(row, a)
2210                    yield a
2211        else:
2212            assert not meta['alpha'] and not meta['greyscale']
2213            # RGB to RGBA
2214            def convert():
2215                for row in pixels:
2216                    a = newarray()
2217                    pngfilters.convert_rgb_to_rgba(row, a)
2218                    yield a
2219        meta['alpha'] = True
2220        meta['greyscale'] = False
2221        return width,height,convert(),meta
2222
2223
2224# === Legacy Version Support ===
2225
2226# :pyver:old:  PyPNG works on Python versions 2.3 and 2.2, but not
2227# without some awkward problems.  Really PyPNG works on Python 2.4 (and
2228# above); it works on Pythons 2.3 and 2.2 by virtue of fixing up
2229# problems here.  It's a bit ugly (which is why it's hidden down here).
2230#
2231# Generally the strategy is one of pretending that we're running on
2232# Python 2.4 (or above), and patching up the library support on earlier
2233# versions so that it looks enough like Python 2.4.  When it comes to
2234# Python 2.2 there is one thing we cannot patch: extended slices
2235# http://www.python.org/doc/2.3/whatsnew/section-slices.html.
2236# Instead we simply declare that features that are implemented using
2237# extended slices will not work on Python 2.2.
2238#
2239# In order to work on Python 2.3 we fix up a recurring annoyance involving
2240# the array type.  In Python 2.3 an array cannot be initialised with an
2241# array, and it cannot be extended with a list (or other sequence).
2242# Both of those are repeated issues in the code.  Whilst I would not
2243# normally tolerate this sort of behaviour, here we "shim" a replacement
2244# for array into place (and hope no-ones notices).  You never read this.
2245#
2246# In an amusing case of warty hacks on top of warty hacks... the array
2247# shimming we try and do only works on Python 2.3 and above (you can't
2248# subclass array.array in Python 2.2).  So to get it working on Python
2249# 2.2 we go for something much simpler and (probably) way slower.
2250try:
2251    array('B').extend([])
2252    array('B', array('B'))
2253except:
2254    # Expect to get here on Python 2.3
2255    try:
2256        class _array_shim(array):
2257            true_array = array
2258            def __new__(cls, typecode, init=None):
2259                super_new = super(_array_shim, cls).__new__
2260                it = super_new(cls, typecode)
2261                if init is None:
2262                    return it
2263                it.extend(init)
2264                return it
2265            def extend(self, extension):
2266                super_extend = super(_array_shim, self).extend
2267                if isinstance(extension, self.true_array):
2268                    return super_extend(extension)
2269                if not isinstance(extension, (list, str)):
2270                    # Convert to list.  Allows iterators to work.
2271                    extension = list(extension)
2272                return super_extend(self.true_array(self.typecode, extension))
2273        array = _array_shim
2274    except:
2275        # Expect to get here on Python 2.2
2276        def array(typecode, init=()):
2277            if type(init) == str:
2278                return map(ord, init)
2279            return list(init)
2280
2281# Further hacks to get it limping along on Python 2.2
2282try:
2283    enumerate
2284except:
2285    def enumerate(seq):
2286        i=0
2287        for x in seq:
2288            yield i,x
2289            i += 1
2290
2291try:
2292    reversed
2293except:
2294    def reversed(l):
2295        l = list(l)
2296        l.reverse()
2297        for x in l:
2298            yield x
2299
2300try:
2301    itertools
2302except:
2303    class _dummy_itertools:
2304        pass
2305    itertools = _dummy_itertools()
2306    def _itertools_imap(f, seq):
2307        for x in seq:
2308            yield f(x)
2309    itertools.imap = _itertools_imap
2310    def _itertools_chain(*iterables):
2311        for it in iterables:
2312            for element in it:
2313                yield element
2314    itertools.chain = _itertools_chain
2315
2316
2317# === Support for users without Cython ===
2318
2319try:
2320    pngfilters
2321except:
2322    class pngfilters(object):
2323        def undo_filter_sub(filter_unit, scanline, previous, result):
2324            """Undo sub filter."""
2325
2326            ai = 0
2327            # Loops starts at index fu.  Observe that the initial part
2328            # of the result is already filled in correctly with
2329            # scanline.
2330            for i in range(filter_unit, len(result)):
2331                x = scanline[i]
2332                a = result[ai]
2333                result[i] = (x + a) & 0xff
2334                ai += 1
2335        undo_filter_sub = staticmethod(undo_filter_sub)
2336
2337        def undo_filter_up(filter_unit, scanline, previous, result):
2338            """Undo up filter."""
2339
2340            for i in range(len(result)):
2341                x = scanline[i]
2342                b = previous[i]
2343                result[i] = (x + b) & 0xff
2344        undo_filter_up = staticmethod(undo_filter_up)
2345
2346        def undo_filter_average(filter_unit, scanline, previous, result):
2347            """Undo up filter."""
2348
2349            ai = -filter_unit
2350            for i in range(len(result)):
2351                x = scanline[i]
2352                if ai < 0:
2353                    a = 0
2354                else:
2355                    a = result[ai]
2356                b = previous[i]
2357                result[i] = (x + ((a + b) >> 1)) & 0xff
2358                ai += 1
2359        undo_filter_average = staticmethod(undo_filter_average)
2360
2361        def undo_filter_paeth(filter_unit, scanline, previous, result):
2362            """Undo Paeth filter."""
2363
2364            # Also used for ci.
2365            ai = -filter_unit
2366            for i in range(len(result)):
2367                x = scanline[i]
2368                if ai < 0:
2369                    a = c = 0
2370                else:
2371                    a = result[ai]
2372                    c = previous[ai]
2373                b = previous[i]
2374                p = a + b - c
2375                pa = abs(p - a)
2376                pb = abs(p - b)
2377                pc = abs(p - c)
2378                if pa <= pb and pa <= pc:
2379                    pr = a
2380                elif pb <= pc:
2381                    pr = b
2382                else:
2383                    pr = c
2384                result[i] = (x + pr) & 0xff
2385                ai += 1
2386        undo_filter_paeth = staticmethod(undo_filter_paeth)
2387
2388        def convert_la_to_rgba(row, result):
2389            for i in range(3):
2390                result[i::4] = row[0::2]
2391            result[3::4] = row[1::2]
2392        convert_la_to_rgba = staticmethod(convert_la_to_rgba)
2393
2394        def convert_l_to_rgba(row, result):
2395            """Convert a grayscale image to RGBA. This method assumes the alpha
2396            channel in result is already correctly initialized."""
2397            for i in range(3):
2398                result[i::4] = row
2399        convert_l_to_rgba = staticmethod(convert_l_to_rgba)
2400
2401        def convert_rgb_to_rgba(row, result):
2402            """Convert an RGB image to RGBA. This method assumes the alpha
2403            channel in result is already correctly initialized."""
2404            for i in range(3):
2405                result[i::4] = row[i::3]
2406        convert_rgb_to_rgba = staticmethod(convert_rgb_to_rgba)
2407
2408
2409# === Internal Test Support ===
2410
2411# This section comprises the tests that are internally validated (as
2412# opposed to tests which produce output files that are externally
2413# validated).  Primarily they are unittests.
2414
2415# Note that it is difficult to internally validate the results of
2416# writing a PNG file.  The only thing we can do is read it back in
2417# again, which merely checks consistency, not that the PNG file we
2418# produce is valid.
2419
2420# Run the tests from the command line:
2421# python -c 'import png;png.test()'
2422
2423# (For an in-memory binary file IO object) We use BytesIO where
2424# available, otherwise we use StringIO, but name it BytesIO.
2425try:
2426    from io import BytesIO
2427except:
2428    from StringIO import StringIO as BytesIO
2429import tempfile
2430# http://www.python.org/doc/2.4.4/lib/module-unittest.html
2431import unittest
2432
2433
2434def test():
2435    unittest.main(__name__)
2436
2437def topngbytes(name, rows, x, y, **k):
2438    """Convenience function for creating a PNG file "in memory" as a
2439    string.  Creates a :class:`Writer` instance using the keyword arguments,
2440    then passes `rows` to its :meth:`Writer.write` method.  The resulting
2441    PNG file is returned as a string.  `name` is used to identify the file for
2442    debugging.
2443    """
2444
2445    import os
2446
2447    print name
2448    f = BytesIO()
2449    w = Writer(x, y, **k)
2450    w.write(f, rows)
2451    if os.environ.get('PYPNG_TEST_TMP'):
2452        w = open(name, 'wb')
2453        w.write(f.getvalue())
2454        w.close()
2455    return f.getvalue()
2456
2457def testWithIO(inp, out, f):
2458    """Calls the function `f` with ``sys.stdin`` changed to `inp`
2459    and ``sys.stdout`` changed to `out`.  They are restored when `f`
2460    returns.  This function returns whatever `f` returns.
2461    """
2462
2463    import os
2464
2465    try:
2466        oldin,sys.stdin = sys.stdin,inp
2467        oldout,sys.stdout = sys.stdout,out
2468        x = f()
2469    finally:
2470        sys.stdin = oldin
2471        sys.stdout = oldout
2472    if os.environ.get('PYPNG_TEST_TMP') and hasattr(out,'getvalue'):
2473        name = mycallersname()
2474        if name:
2475            w = open(name+'.png', 'wb')
2476            w.write(out.getvalue())
2477            w.close()
2478    return x
2479
2480def mycallersname():
2481    """Returns the name of the caller of the caller of this function
2482    (hence the name of the caller of the function in which
2483    "mycallersname()" textually appears).  Returns None if this cannot
2484    be determined."""
2485
2486    # http://docs.python.org/library/inspect.html#the-interpreter-stack
2487    import inspect
2488
2489    frame = inspect.currentframe()
2490    if not frame:
2491        return None
2492    frame_,filename_,lineno_,funname,linelist_,listi_ = (
2493      inspect.getouterframes(frame)[2])
2494    return funname
2495
2496def seqtobytes(s):
2497    """Convert a sequence of integers to a *bytes* instance.  Good for
2498    plastering over Python 2 / Python 3 cracks.
2499    """
2500
2501    return strtobytes(''.join(chr(x) for x in s))
2502
2503class Test(unittest.TestCase):
2504    # This member is used by the superclass.  If we don't define a new
2505    # class here then when we use self.assertRaises() and the PyPNG code
2506    # raises an assertion then we get no proper traceback.  I can't work
2507    # out why, but defining a new class here means we get a proper
2508    # traceback.
2509    class failureException(Exception):
2510        pass
2511
2512    def helperLN(self, n):
2513        mask = (1 << n) - 1
2514        # Use small chunk_limit so that multiple chunk writing is
2515        # tested.  Making it a test for Issue 20.
2516        w = Writer(15, 17, greyscale=True, bitdepth=n, chunk_limit=99)
2517        f = BytesIO()
2518        w.write_array(f, array('B', map(mask.__and__, range(1, 256))))
2519        r = Reader(bytes=f.getvalue())
2520        x,y,pixels,meta = r.read()
2521        self.assertEqual(x, 15)
2522        self.assertEqual(y, 17)
2523        self.assertEqual(list(itertools.chain(*pixels)),
2524                         map(mask.__and__, range(1,256)))
2525    def testL8(self):
2526        return self.helperLN(8)
2527    def testL4(self):
2528        return self.helperLN(4)
2529    def testL2(self):
2530        "Also tests asRGB8."
2531        w = Writer(1, 4, greyscale=True, bitdepth=2)
2532        f = BytesIO()
2533        w.write_array(f, array('B', range(4)))
2534        r = Reader(bytes=f.getvalue())
2535        x,y,pixels,meta = r.asRGB8()
2536        self.assertEqual(x, 1)
2537        self.assertEqual(y, 4)
2538        for i,row in enumerate(pixels):
2539            self.assertEqual(len(row), 3)
2540            self.assertEqual(list(row), [0x55*i]*3)
2541    def testP2(self):
2542        "2-bit palette."
2543        a = (255,255,255)
2544        b = (200,120,120)
2545        c = (50,99,50)
2546        w = Writer(1, 4, bitdepth=2, palette=[a,b,c])
2547        f = BytesIO()
2548        w.write_array(f, array('B', (0,1,1,2)))
2549        r = Reader(bytes=f.getvalue())
2550        x,y,pixels,meta = r.asRGB8()
2551        self.assertEqual(x, 1)
2552        self.assertEqual(y, 4)
2553        self.assertEqual(map(list, pixels), map(list, [a, b, b, c]))
2554    def testPtrns(self):
2555        "Test colour type 3 and tRNS chunk (and 4-bit palette)."
2556        a = (50,99,50,50)
2557        b = (200,120,120,80)
2558        c = (255,255,255)
2559        d = (200,120,120)
2560        e = (50,99,50)
2561        w = Writer(3, 3, bitdepth=4, palette=[a,b,c,d,e])
2562        f = BytesIO()
2563        w.write_array(f, array('B', (4, 3, 2, 3, 2, 0, 2, 0, 1)))
2564        r = Reader(bytes=f.getvalue())
2565        x,y,pixels,meta = r.asRGBA8()
2566        self.assertEqual(x, 3)
2567        self.assertEqual(y, 3)
2568        c = c+(255,)
2569        d = d+(255,)
2570        e = e+(255,)
2571        boxed = [(e,d,c),(d,c,a),(c,a,b)]
2572        flat = map(lambda row: itertools.chain(*row), boxed)
2573        self.assertEqual(map(list, pixels), map(list, flat))
2574    def testRGBtoRGBA(self):
2575        "asRGBA8() on colour type 2 source."""
2576        # Test for Issue 26
2577        r = Reader(bytes=_pngsuite['basn2c08'])
2578        x,y,pixels,meta = r.asRGBA8()
2579        # Test the pixels at row 9 columns 0 and 1.
2580        row9 = list(pixels)[9]
2581        self.assertEqual(list(row9[0:8]),
2582                         [0xff, 0xdf, 0xff, 0xff, 0xff, 0xde, 0xff, 0xff])
2583    def testLtoRGBA(self):
2584        "asRGBA() on grey source."""
2585        # Test for Issue 60
2586        r = Reader(bytes=_pngsuite['basi0g08'])
2587        x,y,pixels,meta = r.asRGBA()
2588        row9 = list(list(pixels)[9])
2589        self.assertEqual(row9[0:8],
2590          [222, 222, 222, 255, 221, 221, 221, 255])
2591    def testCtrns(self):
2592        "Test colour type 2 and tRNS chunk."
2593        # Test for Issue 25
2594        r = Reader(bytes=_pngsuite['tbrn2c08'])
2595        x,y,pixels,meta = r.asRGBA8()
2596        # I just happen to know that the first pixel is transparent.
2597        # In particular it should be #7f7f7f00
2598        row0 = list(pixels)[0]
2599        self.assertEqual(tuple(row0[0:4]), (0x7f, 0x7f, 0x7f, 0x00))
2600    def testAdam7read(self):
2601        """Adam7 interlace reading.
2602        Specifically, test that for images in the PngSuite that
2603        have both an interlaced and straightlaced pair that both
2604        images from the pair produce the same array of pixels."""
2605        for candidate in _pngsuite:
2606            if not candidate.startswith('basn'):
2607                continue
2608            candi = candidate.replace('n', 'i')
2609            if candi not in _pngsuite:
2610                continue
2611            print 'adam7 read', candidate
2612            straight = Reader(bytes=_pngsuite[candidate])
2613            adam7 = Reader(bytes=_pngsuite[candi])
2614            # Just compare the pixels.  Ignore x,y (because they're
2615            # likely to be correct?); metadata is ignored because the
2616            # "interlace" member differs.  Lame.
2617            straight = straight.read()[2]
2618            adam7 = adam7.read()[2]
2619            self.assertEqual(map(list, straight), map(list, adam7))
2620    def testAdam7write(self):
2621        """Adam7 interlace writing.
2622        For each test image in the PngSuite, write an interlaced
2623        and a straightlaced version.  Decode both, and compare results.
2624        """
2625        # Not such a great test, because the only way we can check what
2626        # we have written is to read it back again.
2627
2628        for name,bytes in _pngsuite.items():
2629            # Only certain colour types supported for this test.
2630            if name[3:5] not in ['n0', 'n2', 'n4', 'n6']:
2631                continue
2632            it = Reader(bytes=bytes)
2633            x,y,pixels,meta = it.read()
2634            pngi = topngbytes('adam7wn'+name+'.png', pixels,
2635              x=x, y=y, bitdepth=it.bitdepth,
2636              greyscale=it.greyscale, alpha=it.alpha,
2637              transparent=it.transparent,
2638              interlace=False)
2639            x,y,ps,meta = Reader(bytes=pngi).read()
2640            it = Reader(bytes=bytes)
2641            x,y,pixels,meta = it.read()
2642            pngs = topngbytes('adam7wi'+name+'.png', pixels,
2643              x=x, y=y, bitdepth=it.bitdepth,
2644              greyscale=it.greyscale, alpha=it.alpha,
2645              transparent=it.transparent,
2646              interlace=True)
2647            x,y,pi,meta = Reader(bytes=pngs).read()
2648            self.assertEqual(map(list, ps), map(list, pi))
2649    def testPGMin(self):
2650        """Test that the command line tool can read PGM files."""
2651        def do():
2652            return _main(['testPGMin'])
2653        s = BytesIO()
2654        s.write(strtobytes('P5 2 2 3\n'))
2655        s.write(strtobytes('\x00\x01\x02\x03'))
2656        s.flush()
2657        s.seek(0)
2658        o = BytesIO()
2659        testWithIO(s, o, do)
2660        r = Reader(bytes=o.getvalue())
2661        x,y,pixels,meta = r.read()
2662        self.assertTrue(r.greyscale)
2663        self.assertEqual(r.bitdepth, 2)
2664    def testPAMin(self):
2665        """Test that the command line tool can read PAM file."""
2666        def do():
2667            return _main(['testPAMin'])
2668        s = BytesIO()
2669        s.write(strtobytes('P7\nWIDTH 3\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\n'
2670                'TUPLTYPE RGB_ALPHA\nENDHDR\n'))
2671        # The pixels in flat row flat pixel format
2672        flat =  [255,0,0,255, 0,255,0,120, 0,0,255,30]
2673        asbytes = seqtobytes(flat)
2674        s.write(asbytes)
2675        s.flush()
2676        s.seek(0)
2677        o = BytesIO()
2678        testWithIO(s, o, do)
2679        r = Reader(bytes=o.getvalue())
2680        x,y,pixels,meta = r.read()
2681        self.assertTrue(r.alpha)
2682        self.assertTrue(not r.greyscale)
2683        self.assertEqual(list(itertools.chain(*pixels)), flat)
2684    def testLA4(self):
2685        """Create an LA image with bitdepth 4."""
2686        bytes = topngbytes('la4.png', [[5, 12]], 1, 1,
2687          greyscale=True, alpha=True, bitdepth=4)
2688        sbit = Reader(bytes=bytes).chunk('sBIT')[1]
2689        self.assertEqual(sbit, strtobytes('\x04\x04'))
2690    def testPal(self):
2691        """Test that a palette PNG returns the palette in info."""
2692        r = Reader(bytes=_pngsuite['basn3p04'])
2693        x,y,pixels,info = r.read()
2694        self.assertEqual(x, 32)
2695        self.assertEqual(y, 32)
2696        self.assertTrue('palette' in info)
2697    def testPalWrite(self):
2698        """Test metadata for paletted PNG can be passed from one PNG
2699        to another."""
2700        r = Reader(bytes=_pngsuite['basn3p04'])
2701        x,y,pixels,info = r.read()
2702        w = Writer(**info)
2703        o = BytesIO()
2704        w.write(o, pixels)
2705        o.flush()
2706        o.seek(0)
2707        r = Reader(file=o)
2708        _,_,_,again_info = r.read()
2709        # Same palette
2710        self.assertEqual(again_info['palette'], info['palette'])
2711    def testPalExpand(self):
2712        """Test that bitdepth can be used to fiddle with pallete image."""
2713        r = Reader(bytes=_pngsuite['basn3p04'])
2714        x,y,pixels,info = r.read()
2715        pixels = [list(row) for row in pixels]
2716        info['bitdepth'] = 8
2717        w = Writer(**info)
2718        o = BytesIO()
2719        w.write(o, pixels)
2720        o.flush()
2721        o.seek(0)
2722        r = Reader(file=o)
2723        _,_,again_pixels,again_info = r.read()
2724        # Same pixels
2725        again_pixels = [list(row) for row in again_pixels]
2726        self.assertEqual(again_pixels, pixels)
2727
2728    def testPNMsbit(self):
2729        """Test that PNM files can generates sBIT chunk."""
2730        def do():
2731            return _main(['testPNMsbit'])
2732        s = BytesIO()
2733        s.write(strtobytes('P6 8 1 1\n'))
2734        for pixel in range(8):
2735            s.write(struct.pack('<I', (0x4081*pixel)&0x10101)[:3])
2736        s.flush()
2737        s.seek(0)
2738        o = BytesIO()
2739        testWithIO(s, o, do)
2740        r = Reader(bytes=o.getvalue())
2741        sbit = r.chunk('sBIT')[1]
2742        self.assertEqual(sbit, strtobytes('\x01\x01\x01'))
2743    def testLtrns0(self):
2744        """Create greyscale image with tRNS chunk."""
2745        return self.helperLtrns(0)
2746    def testLtrns1(self):
2747        """Using 1-tuple for transparent arg."""
2748        return self.helperLtrns((0,))
2749    def helperLtrns(self, transparent):
2750        """Helper used by :meth:`testLtrns*`."""
2751        pixels = zip([0x00, 0x38, 0x4c, 0x54, 0x5c, 0x40, 0x38, 0x00])
2752        o = BytesIO()
2753        w = Writer(8, 8, greyscale=True, bitdepth=1, transparent=transparent)
2754        w.write_packed(o, pixels)
2755        r = Reader(bytes=o.getvalue())
2756        x,y,pixels,meta = r.asDirect()
2757        self.assertTrue(meta['alpha'])
2758        self.assertTrue(meta['greyscale'])
2759        self.assertEqual(meta['bitdepth'], 1)
2760    def testWinfo(self):
2761        """Test the dictionary returned by a `read` method can be used
2762        as args for :meth:`Writer`.
2763        """
2764        r = Reader(bytes=_pngsuite['basn2c16'])
2765        info = r.read()[3]
2766        w = Writer(**info)
2767    def testPackedIter(self):
2768        """Test iterator for row when using write_packed.
2769
2770        Indicative for Issue 47.
2771        """
2772        w = Writer(16, 2, greyscale=True, alpha=False, bitdepth=1)
2773        o = BytesIO()
2774        w.write_packed(o, [itertools.chain([0x0a], [0xaa]),
2775                           itertools.chain([0x0f], [0xff])])
2776        r = Reader(bytes=o.getvalue())
2777        x,y,pixels,info = r.asDirect()
2778        pixels = list(pixels)
2779        self.assertEqual(len(pixels), 2)
2780        self.assertEqual(len(pixels[0]), 16)
2781    def testInterlacedArray(self):
2782        """Test that reading an interlaced PNG yields each row as an
2783        array."""
2784        r = Reader(bytes=_pngsuite['basi0g08'])
2785        list(r.read()[2])[0].tostring
2786    def testTrnsArray(self):
2787        """Test that reading a type 2 PNG with tRNS chunk yields each
2788        row as an array (using asDirect)."""
2789        r = Reader(bytes=_pngsuite['tbrn2c08'])
2790        list(r.asDirect()[2])[0].tostring
2791
2792    # Invalid file format tests.  These construct various badly
2793    # formatted PNG files, then feed them into a Reader.  When
2794    # everything is working properly, we should get FormatError
2795    # exceptions raised.
2796    def testEmpty(self):
2797        """Test empty file."""
2798
2799        r = Reader(bytes='')
2800        self.assertRaises(FormatError, r.asDirect)
2801    def testSigOnly(self):
2802        """Test file containing just signature bytes."""
2803
2804        r = Reader(bytes=_signature)
2805        self.assertRaises(FormatError, r.asDirect)
2806    def testExtraPixels(self):
2807        """Test file that contains too many pixels."""
2808
2809        def eachchunk(chunk):
2810            if chunk[0] != 'IDAT':
2811                return chunk
2812            data = zlib.decompress(chunk[1])
2813            data += strtobytes('\x00garbage')
2814            data = zlib.compress(data)
2815            chunk = (chunk[0], data)
2816            return chunk
2817        self.assertRaises(FormatError, self.helperFormat, eachchunk)
2818    def testNotEnoughPixels(self):
2819        def eachchunk(chunk):
2820            if chunk[0] != 'IDAT':
2821                return chunk
2822            # Remove last byte.
2823            data = zlib.decompress(chunk[1])
2824            data = data[:-1]
2825            data = zlib.compress(data)
2826            return (chunk[0], data)
2827        self.assertRaises(FormatError, self.helperFormat, eachchunk)
2828    def helperFormat(self, f):
2829        r = Reader(bytes=_pngsuite['basn0g01'])
2830        o = BytesIO()
2831        def newchunks():
2832            for chunk in r.chunks():
2833                yield f(chunk)
2834        write_chunks(o, newchunks())
2835        r = Reader(bytes=o.getvalue())
2836        return list(r.asDirect()[2])
2837    def testBadFilter(self):
2838        def eachchunk(chunk):
2839            if chunk[0] != 'IDAT':
2840                return chunk
2841            data = zlib.decompress(chunk[1])
2842            # Corrupt the first filter byte
2843            data = strtobytes('\x99') + data[1:]
2844            data = zlib.compress(data)
2845            return (chunk[0], data)
2846        self.assertRaises(FormatError, self.helperFormat, eachchunk)
2847
2848    def testFlat(self):
2849        """Test read_flat."""
2850        import hashlib
2851
2852        r = Reader(bytes=_pngsuite['basn0g02'])
2853        x,y,pixel,meta = r.read_flat()
2854        d = hashlib.md5(seqtobytes(pixel)).digest()
2855        self.assertEqual(_enhex(d), '255cd971ab8cd9e7275ff906e5041aa0')
2856    def testfromarray(self):
2857        img = from_array([[0, 0x33, 0x66], [0xff, 0xcc, 0x99]], 'L')
2858        img.save('testfromarray.png')
2859    def testfromarrayL16(self):
2860        img = from_array(group(range(2**16), 256), 'L;16')
2861        img.save('testL16.png')
2862    def testfromarrayRGB(self):
2863        img = from_array([[0,0,0, 0,0,1, 0,1,0, 0,1,1],
2864                          [1,0,0, 1,0,1, 1,1,0, 1,1,1]], 'RGB;1')
2865        o = BytesIO()
2866        img.save(o)
2867    def testfromarrayIter(self):
2868        import itertools
2869
2870        i = itertools.islice(itertools.count(10), 20)
2871        i = itertools.imap(lambda x: [x, x, x], i)
2872        img = from_array(i, 'RGB;5', dict(height=20))
2873        f = open('testiter.png', 'wb')
2874        img.save(f)
2875        f.close()
2876
2877    # numpy dependent tests.  These are skipped (with a message to
2878    # sys.stderr) if numpy cannot be imported.
2879    def testNumpyuint16(self):
2880        """numpy uint16."""
2881
2882        try:
2883            import numpy
2884        except ImportError:
2885            print >>sys.stderr, "skipping numpy test"
2886            return
2887
2888        rows = [map(numpy.uint16, range(0,0x10000,0x5555))]
2889        b = topngbytes('numpyuint16.png', rows, 4, 1,
2890            greyscale=True, alpha=False, bitdepth=16)
2891    def testNumpyuint8(self):
2892        """numpy uint8."""
2893
2894        try:
2895            import numpy
2896        except ImportError:
2897            print >>sys.stderr, "skipping numpy test"
2898            return
2899
2900        rows = [map(numpy.uint8, range(0,0x100,0x55))]
2901        b = topngbytes('numpyuint8.png', rows, 4, 1,
2902            greyscale=True, alpha=False, bitdepth=8)
2903    def testNumpybool(self):
2904        """numpy bool."""
2905
2906        try:
2907            import numpy
2908        except ImportError:
2909            print >>sys.stderr, "skipping numpy test"
2910            return
2911
2912        rows = [map(numpy.bool, [0,1])]
2913        b = topngbytes('numpybool.png', rows, 2, 1,
2914            greyscale=True, alpha=False, bitdepth=1)
2915    def testNumpyarray(self):
2916        """numpy array."""
2917        try:
2918            import numpy
2919        except ImportError:
2920            print >>sys.stderr, "skipping numpy test"
2921            return
2922
2923        pixels = numpy.array([[0,0x5555],[0x5555,0xaaaa]], numpy.uint16)
2924        img = from_array(pixels, 'L')
2925        img.save('testnumpyL16.png')
2926
2927    def paeth(self, x, a, b, c):
2928        p = a + b - c
2929        pa = abs(p - a)
2930        pb = abs(p - b)
2931        pc = abs(p - c)
2932        if pa <= pb and pa <= pc:
2933            pr = a
2934        elif pb <= pc:
2935            pr = b
2936        else:
2937            pr = c
2938        return x - pr
2939
2940    # test filters and unfilters
2941    def testFilterScanlineFirstLine(self):
2942        fo = 3  # bytes per pixel
2943        line = [30, 31, 32, 230, 231, 232]
2944        out = filter_scanline(0, line, fo, None)  # none
2945        self.assertEqual(list(out), [0, 30, 31, 32, 230, 231, 232])
2946        out = filter_scanline(1, line, fo, None)  # sub
2947        self.assertEqual(list(out), [1, 30, 31, 32, 200, 200, 200])
2948        out = filter_scanline(2, line, fo, None)  # up
2949        # TODO: All filtered scanlines start with a byte indicating the filter
2950        # algorithm, except "up". Is this a bug? Should the expected output
2951        # start with 2 here?
2952        self.assertEqual(list(out), [30, 31, 32, 230, 231, 232])
2953        out = filter_scanline(3, line, fo, None)  # average
2954        self.assertEqual(list(out), [3, 30, 31, 32, 215, 216, 216])
2955        out = filter_scanline(4, line, fo, None)  # paeth
2956        self.assertEqual(list(out), [
2957            4, self.paeth(30, 0, 0, 0), self.paeth(31, 0, 0, 0),
2958            self.paeth(32, 0, 0, 0), self.paeth(230, 30, 0, 0),
2959            self.paeth(231, 31, 0, 0), self.paeth(232, 32, 0, 0)
2960            ])
2961    def testFilterScanline(self):
2962        prev = [20, 21, 22, 210, 211, 212]
2963        line = [30, 32, 34, 230, 233, 236]
2964        fo = 3
2965        out = filter_scanline(0, line, fo, prev)  # none
2966        self.assertEqual(list(out), [0, 30, 32, 34, 230, 233, 236])
2967        out = filter_scanline(1, line, fo, prev)  # sub
2968        self.assertEqual(list(out), [1, 30, 32, 34, 200, 201, 202])
2969        out = filter_scanline(2, line, fo, prev)  # up
2970        self.assertEqual(list(out), [2, 10, 11, 12, 20, 22, 24])
2971        out = filter_scanline(3, line, fo, prev)  # average
2972        self.assertEqual(list(out), [3, 20, 22, 23, 110, 112, 113])
2973        out = filter_scanline(4, line, fo, prev)  # paeth
2974        self.assertEqual(list(out), [
2975            4, self.paeth(30, 0, 20, 0), self.paeth(32, 0, 21, 0),
2976            self.paeth(34, 0, 22, 0), self.paeth(230, 30, 210, 20),
2977            self.paeth(233, 32, 211, 21), self.paeth(236, 34, 212, 22)
2978            ])
2979    def testUnfilterScanline(self):
2980        reader = Reader(bytes='')
2981        reader.psize = 3
2982        scanprev = array('B', [20, 21, 22, 210, 211, 212])
2983        scanline = array('B', [30, 32, 34, 230, 233, 236])
2984        def cp(a):
2985            return array('B', a)
2986
2987        out = reader.undo_filter(0, cp(scanline), cp(scanprev))
2988        self.assertEqual(list(out), list(scanline))  # none
2989        out = reader.undo_filter(1, cp(scanline), cp(scanprev))
2990        self.assertEqual(list(out), [30, 32, 34, 4, 9, 14])  # sub
2991        out = reader.undo_filter(2, cp(scanline), cp(scanprev))
2992        self.assertEqual(list(out), [50, 53, 56, 184, 188, 192])  # up
2993        out = reader.undo_filter(3, cp(scanline), cp(scanprev))
2994        self.assertEqual(list(out), [40, 42, 45, 99, 103, 108])  # average
2995        out = reader.undo_filter(4, cp(scanline), cp(scanprev))
2996        self.assertEqual(list(out), [50, 53, 56, 184, 188, 192])  # paeth
2997    def testUnfilterScanlinePaeth(self):
2998        # This tests more edge cases in the paeth unfilter
2999        reader = Reader(bytes='')
3000        reader.psize = 3
3001        scanprev = array('B', [2, 0, 0, 0, 9, 11])
3002        scanline = array('B', [6, 10, 9, 100, 101, 102])
3003
3004        out = reader.undo_filter(4, scanline, scanprev)
3005        self.assertEqual(list(out), [8, 10, 9, 108, 111, 113])  # paeth
3006    def testIterstraight(self):
3007        def arraify(list_of_str):
3008            return [array('B', s) for s in list_of_str]
3009        reader = Reader(bytes='')
3010        reader.row_bytes = 6
3011        reader.psize = 3
3012        rows = reader.iterstraight(arraify(['\x00abcdef', '\x00ghijkl']))
3013        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
3014
3015        rows = reader.iterstraight(arraify(['\x00abc', 'def\x00ghijkl']))
3016        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
3017
3018        rows = reader.iterstraight(arraify(['\x00abcdef\x00ghijkl']))
3019        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
3020
3021        rows = reader.iterstraight(arraify(['\x00abcdef\x00ghi', 'jkl']))
3022        self.assertEqual(list(rows), arraify(['abcdef', 'ghijkl']))
3023
3024# === Command Line Support ===
3025
3026def _dehex(s):
3027    """Liberally convert from hex string to binary string."""
3028    import re
3029    import binascii
3030
3031    # Remove all non-hexadecimal digits
3032    s = re.sub(r'[^a-fA-F\d]', '', s)
3033    # binscii.unhexlify works in Python 2 and Python 3 (unlike
3034    # thing.decode('hex')).
3035    return binascii.unhexlify(strtobytes(s))
3036def _enhex(s):
3037    """Convert from binary string (bytes) to hex string (str)."""
3038
3039    import binascii
3040
3041    return bytestostr(binascii.hexlify(s))
3042
3043# Copies of PngSuite test files taken
3044# from http://www.schaik.com/pngsuite/pngsuite_bas_png.html
3045# on 2009-02-19 by drj and converted to hex.
3046# Some of these are not actually in PngSuite (but maybe they should
3047# be?), they use the same naming scheme, but start with a capital
3048# letter.
3049_pngsuite = {
3050  'basi0g01': _dehex("""
305189504e470d0a1a0a0000000d49484452000000200000002001000000012c0677
3052cf0000000467414d41000186a031e8965f0000009049444154789c2d8d310ec2
3053300c45dfc682c415187a00a42e197ab81e83b127e00c5639001363a580d8582c
305465c910357c4b78b0bfbfdf4f70168c19e7acb970a3f2d1ded9695ce5bf5963df
3055d92aaf4c9fd927ea449e6487df5b9c36e799b91bdf082b4d4bd4014fe4014b01
3056ab7a17aee694d28d328a2d63837a70451e1648702d9a9ff4a11d2f7a51aa21e5
3057a18c7ffd0094e3511d661822f20000000049454e44ae426082
3058"""),
3059  'basi0g02': _dehex("""
306089504e470d0a1a0a0000000d49484452000000200000002002000000016ba60d
30611f0000000467414d41000186a031e8965f0000005149444154789c635062e860
306200e17286bb609c93c370ec189494960631366e4467b3ae675dcf10f521ea0303
306390c1ca006444e11643482064114a4852c710baea3f18c31918020c30410403a6
30640ac1a09239009c52804d85b6d97d0000000049454e44ae426082
3065"""),
3066  'basi0g04': _dehex("""
306789504e470d0a1a0a0000000d4948445200000020000000200400000001e4e6f8
3068bf0000000467414d41000186a031e8965f000000ae49444154789c658e5111c2
3069301044171c141c141c041c843a287510ea20d441c041c141c141c04191102454
307003994998cecd7edcecedbb9bdbc3b2c2b6457545fbc4bac1be437347f7c66a77
30713c23d60db15e88f5c5627338a5416c2e691a9b475a89cd27eda12895ae8dfdab
307243d61e590764f5c83a226b40d669bec307f93247701687723abf31ff83a2284b
3073a5b4ae6b63ac6520ad730ca4ed7b06d20e030369bd6720ed383290360406d24e
307413811f2781eba9d34d07160000000049454e44ae426082
3075"""),
3076  'basi0g08': _dehex("""
307789504e470d0a1a0a0000000d4948445200000020000000200800000001211615
3078be0000000467414d41000186a031e8965f000000b549444154789cb5905d0ac2
30793010849dbac81c42c47bf843cf253e8878b0aa17110f214bdca6be240f5d21a5
308094ced3e49bcd322c1624115515154998aa424822a82a5624a1aa8a8b24c58f99
3081999908130989a04a00d76c2c09e76cf21adcb209393a6553577da17140a2c59e
308270ecbfa388dff1f03b82fb82bd07f05f7cb13f80bb07ad2fd60c011c3c588eef
3083f1f4e03bbec7ce832dca927aea005e431b625796345307b019c845e6bfc3bb98
3084769d84f9efb02ea6c00f9bb9ff45e81f9f280000000049454e44ae426082
3085"""),
3086  'basi0g16': _dehex("""
308789504e470d0a1a0a0000000d49484452000000200000002010000000017186c9
3088fd0000000467414d41000186a031e8965f000000e249444154789cb5913b0ec2
3089301044c7490aa8f85d81c3e4301c8f53a4ca0da8902c8144b3920b4043111282
309023bc4956681a6bf5fc3c5a3ba0448912d91a4de2c38dd8e380231eede4c4f7a1
30914677700bec7bd9b1d344689315a3418d1a6efbe5b8305ba01f8ff4808c063e26
3092c60d5c81edcf6c58c535e252839e93801b15c0a70d810ae0d306b205dc32b187
3093272b64057e4720ff0502154034831520154034c3df81400510cdf0015c86e5cc
30945c79c639fddba9dcb5456b51d7980eb52d8e7d7fa620a75120d6064641a05120
3095b606771a05626b401a05f1f589827cf0fe44c1f0bae0055698ee8914fffffe00
309600000049454e44ae426082
3097"""),
3098  'basi2c08': _dehex("""
309989504e470d0a1a0a0000000d49484452000000200000002008020000018b1fdd
3100350000000467414d41000186a031e8965f000000f249444154789cd59341aa04
3101210c44abc07b78133d59d37333bd89d76868b566d10cf4675af8596431a11662
31027c5688919280e312257dd6a0a4cf1a01008ee312a5f3c69c37e6fcc3f47e6776
3103a07f8bdaf5b40feed2d33e025e2ff4fe2d4a63e1a16d91180b736d8bc45854c5
31046d951863f4a7e0b66dcf09a900f3ffa2948d4091e53ca86c048a64390f662b50
31054a999660ced906182b9a01a8be00a56404a6ede182b1223b4025e32c4de34304
310663457680c93aada6c99b73865aab2fc094920d901a203f5ddfe1970d28456783
310726cffbafeffcd30654f46d119be4793f827387fc0d189d5bc4d69a3c23d45a7f
3108db803146578337df4d0a3121fc3d330000000049454e44ae426082
3109"""),
3110  'basi2c16': _dehex("""
311189504e470d0a1a0a0000000d4948445200000020000000201002000001db8f01
3112760000000467414d41000186a031e8965f0000020a49444154789cd5962173e3
31133010853fcf1838cc61a1818185a53e56787fa13fa130852e3b5878b4b0b03081
3114b97f7030070b53e6b057a0a8912bbb9163b9f109ececbc59bd7dcf2b45492409
3115d66f00eb1dd83cb5497d65456aeb8e1040913b3b2c04504c936dd5a9c7e2c6eb
3116b1b8f17a58e8d043da56f06f0f9f62e5217b6ba3a1b76f6c9e99e8696a2a72e2
3117c4fb1e4d452e92ec9652b807486d12b6669be00db38d9114b0c1961e375461a5
31185f76682a85c367ad6f682ff53a9c2a353191764b78bb07d8ddc3c97c1950f391
31196745c7b9852c73c2f212605a466a502705c8338069c8b9e84efab941eb393a97
3120d4c9fd63148314209f1c1d3434e847ead6380de291d6f26a25c1ebb5047f5f24
3121d85c49f0f22cc1d34282c72709cab90477bf25b89d49f0f351822297e0ea9704
3122f34c82bc94002448ede51866e5656aef5d7c6a385cb4d80e6a538ceba04e6df2
3123480e9aa84ddedb413bb5c97b3838456df2d4fec2c7a706983e7474d085fae820
3124a841776a83073838973ac0413fea2f1dc4a06e71108fda73109bdae48954ad60
3125bf867aac3ce44c7c1589a711cf8a81df9b219679d96d1cec3d8bbbeaa2012626
3126df8c7802eda201b2d2e0239b409868171fc104ba8b76f10b4da09f6817ffc609
3127c413ede267fd1fbab46880c90f80eccf0013185eb48b47ba03df2bdaadef3181
3128cb8976f18e13188768170f98c0f844bb78cb04c62ddac59d09fc3fa25dfc1da4
312914deb3df1344f70000000049454e44ae426082
3130"""),
3131  'basi3p08': _dehex("""
313289504e470d0a1a0a0000000d494844520000002000000020080300000133a3ba
3133500000000467414d41000186a031e8965f00000300504c5445224400f5ffed77
3134ff77cbffff110a003a77002222ffff11ff110000222200ffac5566ff66ff6666
3135ff01ff221200dcffffccff994444ff005555220000cbcbff44440055ff55cbcb
313600331a00ffecdcedffffe4ffcbffdcdc44ff446666ff330000442200ededff66
31376600ffa444ffffaaeded0000cbcbfefffffdfffeffff0133ff33552a000101ff
31388888ff00aaaa010100440000888800ffe4cbba5b0022ff22663200ffff99aaaa
3139ff550000aaaa00cb630011ff11d4ffaa773a00ff4444dc6b0066000001ff0188
31404200ecffdc6bdc00ffdcba00333300ed00ed7300ffff88994a0011ffff770000
3141ff8301ffbabafe7b00fffeff00cb00ff999922ffff880000ffff77008888ffdc
3142ff1a33000000aa33ffff009900990000000001326600ffbaff44ffffffaaff00
3143770000fefeaa00004a9900ffff66ff22220000998bff1155ffffff0101ff88ff
3144005500001111fffffefffdfea4ff4466ffffff66ff003300ffff55ff77770000
314588ff44ff00110077ffff006666ffffed000100fff5ed1111ffffff44ff22ffff
3146eded11110088ffff00007793ff2200dcdc3333fffe00febabaff99ffff333300
314763cb00baba00acff55ffffdcffff337bfe00ed00ed5555ffaaffffdcdcff5555
314800000066dcdc00dc00dc83ff017777fffefeffffffcbff5555777700fefe00cb
314900cb0000fe010200010000122200ffff220044449bff33ffd4aa0000559999ff
3150999900ba00ba2a5500ffcbcbb4ff66ff9b33ffffbaaa00aa42880053aa00ffaa
3151aa0000ed00babaffff1100fe00000044009999990099ffcc99ba000088008800
3152dc00ff93220000dcfefffeaa5300770077020100cb0000000033ffedff00ba00
3153ff3333edffedffc488bcff7700aa00660066002222dc0000ffcbffdcffdcff8b
3154110000cb00010155005500880000002201ffffcbffcbed0000ff88884400445b
3155ba00ffbc77ff99ff006600baffba00777773ed00fe00003300330000baff77ff
3156004400aaffaafffefe000011220022c4ff8800eded99ff99ff55ff002200ffb4
3157661100110a1100ff1111dcffbabaffff88ff88010001ff33ffb98ed362000002
3158a249444154789c65d0695c0b001806f03711a9904a94d24dac63292949e5a810
3159d244588a14ca5161d1a1323973252242d62157d12ae498c8124d25ca3a11398a
316016e55a3cdffab0ffe7f77d7fcff3528645349b584c3187824d9d19d4ec2e3523
31619eb0ae975cf8de02f2486d502191841b42967a1ad49e5ddc4265f69a899e26b5
3162e9e468181baae3a71a41b95669da8df2ea3594c1b31046d7b17bfb86592e4cbe
3163d89b23e8db0af6304d756e60a8f4ad378bdc2552ae5948df1d35b52143141533
316433bbbbababebeb3b3bc9c9c9c6c6c0c0d7b7b535323225a5aa8a02024a4bedec
31650a0a2a2bcdcd7d7cf2f3a9a9c9cdcdd8b8adcdd5b5ababa828298982824a4ab2
3166b21212acadbdbc1414e2e24859b9a72730302f4f49292c4c57373c9c0a0b7372
31678c8c1c1c3a3a92936d6dfdfd293e3e26262a4a4eaea2424b4b5fbfbc9c323278
31683c0b0ba1303abaae8ecdeeed950d6669a9a7a7a141d4de9e9d5d5cdcd2229b94
3169c572716132f97cb1d8db9bc3110864a39795d9db6b6a26267a7a9a98d4d6a6a7
3170cb76090ef6f030354d4d75766e686030545464cb393a1a1ac6c68686eae8f8f9
3171a9aa4644c8b66d6e1689dcdd2512a994cb35330b0991ad9f9b6b659596a6addd
3172d8282fafae5e5323fb8f41d01f76c22fd8061be01bfc041a0323e1002c81cd30
31730b9ec027a0c930014ec035580fc3e112bc069a0b53e11c0c8095f00176c163a0
3174e5301baec06a580677600ddc05ba0f13e120bc81a770133ec355a017300d4ec2
31750c7800bbe1219c02fa08f3e13c1c85dbb00a2ec05ea0dff00a6ec15a98027360
3176070c047a06d7e1085c84f1b014f6c03fa0b33018b6c0211801ebe018fc00da0a
31776f61113c877eb01d4ec317a085700f26c130f80efbe132bc039a0733e106fc81
3178f7f017f6c10aa0d1300a0ec374780943e1382c06fa0a9b60238c83473016cec0
317902f80f73fefe1072afc1e50000000049454e44ae426082
3180"""),
3181  'basi6a08': _dehex("""
318289504e470d0a1a0a0000000d4948445200000020000000200806000001047d4a
3183620000000467414d41000186a031e8965f0000012049444154789cc595414ec3
31843010459fa541b8bbb26641b8069b861e8b4d12c1c112c1452a710a2a65d840d5
3185949041fc481ec98ae27c7f3f8d27e3e4648047600fec0d1f390fbbe2633a31e2
31869389e4e4ea7bfdbf3d9a6b800ab89f1bd6b553cfcbb0679e960563d72e0a9293
3187b7337b9f988cc67f5f0e186d20e808042f1c97054e1309da40d02d7e27f92e03
31886cbfc64df0fc3117a6210a1b6ad1a00df21c1abcf2a01944c7101b0cb568a001
3189909c9cf9e399cf3d8d9d4660a875405d9a60d000b05e2de55e25780b7a5268e0
3190622118e2399aab063a815808462f1ab86890fc2e03e48bb109ded7d26ce4bf59
31910db91bac0050747fec5015ce80da0e5700281be533f0ce6d5900b59bcb00ea6d
3192200314cf801faab200ea752803a8d7a90c503a039f824a53f4694e7342000000
31930049454e44ae426082
3194"""),
3195  'basn0g01': _dehex("""
319689504e470d0a1a0a0000000d49484452000000200000002001000000005b0147
3197590000000467414d41000186a031e8965f0000005b49444154789c2dccb10903
3198300c05d1ebd204b24a200b7a346f90153c82c18d0a61450751f1e08a2faaead2
3199a4846ccea9255306e753345712e211b221bf4b263d1b427325255e8bdab29e6f
32006aca30692e9d29616ee96f3065f0bf1f1087492fd02f14c90000000049454e44
3201ae426082
3202"""),
3203  'basn0g02': _dehex("""
320489504e470d0a1a0a0000000d49484452000000200000002002000000001ca13d
3205890000000467414d41000186a031e8965f0000001f49444154789c6360085df5
32061f8cf1308850c20053868f0133091f6390b90700bd497f818b0989a900000000
320749454e44ae426082
3208"""),
3209  # A version of basn0g04 dithered down to 3 bits.
3210  'Basn0g03': _dehex("""
321189504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
32122900000001734249540371d88211000000fd49444154789c6d90d18906210c84
3213c356f22356b2889588604301b112112b11d94a96bb495cf7fe87f32d996f2689
321444741cc658e39c0b118f883e1f63cc89dafbc04c0f619d7d898396c54b875517
321583f3a2e7ac09a2074430e7f497f00f1138a5444f82839c5206b1f51053cca968
321663258821e7f2b5438aac16fbecc052b646e709de45cf18996b29648508728612
3217952ca606a73566d44612b876845e9a347084ea4868d2907ff06be4436c4b41a3
3218a3e1774285614c5affb40dbd931a526619d9fa18e4c2be420858de1df0e69893
3219a0e3e5523461be448561001042b7d4a15309ce2c57aef2ba89d1c13794a109d7
3220b5880aa27744fc5c4aecb5e7bcef5fe528ec6293a930690000000049454e44ae
3221426082
3222"""),
3223  'basn0g04': _dehex("""
322489504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
3225290000000467414d41000186a031e8965f0000004849444154789c6360601014
3226545232367671090d4d4b2b2f6720430095dbd1418e002a77e64c720450b9ab56
3227912380caddbd9b1c0154ee9933e408a072efde25470095fbee1d1902001f14ee
322801eaff41fa0000000049454e44ae426082
3229"""),
3230  'basn0g08': _dehex("""
323189504e470d0a1a0a0000000d4948445200000020000000200800000000561125
3232280000000467414d41000186a031e8965f0000004149444154789c6364602400
32331408c8b30c05058c0f0829f8f71f3f6079301c1430ca11906764a2795c0c0605
32348c8ff0cafeffcff887e67131181430cae0956564040050e5fe7135e2d8590000
3235000049454e44ae426082
3236"""),
3237  'basn0g16': _dehex("""
323889504e470d0a1a0a0000000d49484452000000200000002010000000000681f9
32396b0000000467414d41000186a031e8965f0000005e49444154789cd5d2310ac0
3240300c4351395bef7fc6dca093c0287b32d52a04a3d98f3f3880a7b857131363a0
32413a82601d089900dd82f640ca04e816dc06422640b7a03d903201ba05b7819009
3242d02d680fa44c603f6f07ec4ff41938cf7f0016d84bd85fae2b9fd70000000049
3243454e44ae426082
3244"""),
3245  'basn2c08': _dehex("""
324689504e470d0a1a0a0000000d4948445200000020000000200802000000fc18ed
3247a30000000467414d41000186a031e8965f0000004849444154789cedd5c10900
3248300c024085ec91fdb772133b442bf4a1f8cee12bb40d043b800a14f81ca0ede4
32497d4c784081020f4a871fc284071428f0a0743823a94081bb7077a3c00182b1f9
32505e0f40cf4b0000000049454e44ae426082
3251"""),
3252  'basn2c16': _dehex("""
325389504e470d0a1a0a0000000d4948445200000020000000201002000000ac8831
3254e00000000467414d41000186a031e8965f000000e549444154789cd596c10a83
3255301044a7e0417fcb7eb7fdadf6961e06039286266693cc7a188645e43dd6a08f
32561042003e2fe09aef6472737e183d27335fcee2f35a77b702ebce742870a23397
3257f3edf2705dd10160f3b2815fe8ecf2027974a6b0c03f74a6e4192843e75c6c03
325835e8ec3202f5e84c0181bbe8cca967a00d9df3491bb040671f2e6087ce1c2860
32598d1e05f8c7ee0f1d00b667e70df44467ef26d01fbd9bc028f42860f71d188bce
3260fb8d3630039dbd59601e7ab3c06cf428507f0634d039afdc80123a7bb1801e7a
3261b1802a7a14c89f016d74ce331bf080ce9e08f8414f04bca133bfe642fe5e07bb
3262c4ec0000000049454e44ae426082
3263"""),
3264  'basn3p04': _dehex("""
326589504e470d0a1a0a0000000d4948445200000020000000200403000000815467
3266c70000000467414d41000186a031e8965f000000037342495404040477f8b5a3
32670000002d504c54452200ff00ffff8800ff22ff000099ffff6600dd00ff77ff00
3268ff000000ff99ddff00ff00bbffbb000044ff00ff44d2b049bd00000047494441
326954789c63e8e8080d3d7366d5aaf27263e377ef66ce64204300952b28488e002a
3270d7c5851c0154eeddbbe408a07119c81140e52a29912380ca4d4b23470095bb7b
327137190200e0c4ead10f82057d0000000049454e44ae426082
3272"""),
3273  'basn6a08': _dehex("""
327489504e470d0a1a0a0000000d4948445200000020000000200806000000737a7a
3275f40000000467414d41000186a031e8965f0000006f49444154789cedd6310a80
3276300c46e12764684fa1f73f55048f21c4ddc545781d52e85028fc1f4d28d98a01
3277305e7b7e9cffba33831d75054703ca06a8f90d58a0074e351e227d805c8254e3
32781bb0420f5cdc2e0079208892ffe2a00136a07b4007943c1004d900195036407f
3279011bf00052201a9c160fb84c0000000049454e44ae426082
3280"""),
3281  'cs3n3p08': _dehex("""
328289504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
3283c60000000467414d41000186a031e8965f0000000373424954030303a392a042
328400000054504c544592ff0000ff9200ffff00ff0000dbff00ff6dffb600006dff
3285b6ff00ff9200dbff000049ffff2400ff000024ff0049ff0000ffdb00ff4900ff
3286b6ffff0000ff2400b6ffffdb000092ffff6d000024ffff49006dff00df702b17
32870000004b49444154789c85cac70182000000b1b3625754b0edbfa72324ef7486
3288184ed0177a437b680bcdd0031c0ed00ea21f74852ed00a1c9ed0086da0057487
32896ed0121cd6d004bda0013a421ff803224033e177f4ae260000000049454e44ae
3290426082
3291"""),
3292  's09n3p02': _dehex("""
329389504e470d0a1a0a0000000d49484452000000090000000902030000009dffee
3294830000000467414d41000186a031e8965f000000037342495404040477f8b5a3
32950000000c504c544500ff000077ffff00ffff7700ff5600640000001f49444154
3296789c63600002fbff0c0c56ab19182ca381581a4283f82071200000696505c36a
3297437f230000000049454e44ae426082
3298"""),
3299  'tbgn3p08': _dehex("""
330089504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
3301c60000000467414d41000186a031e8965f00000207504c54457f7f7fafafafab
3302abab110000222200737300999999510d00444400959500959595e6e600919191
33038d8d8d620d00898989666600b7b700911600000000730d007373736f6f6faaaa
3304006b6b6b676767c41a00cccc0000f30000ef00d51e0055555567670000dd0051
3305515100d1004d4d4de61e0038380000b700160d0d00ab00560d00090900009500
3306009100008d003333332f2f2f2f2b2f2b2b000077007c7c001a05002b27000073
3307002b2b2b006f00bb1600272727780d002323230055004d4d00cc1e00004d00cc
33081a000d00003c09006f6f00002f003811271111110d0d0d55554d090909001100
33094d0900050505000d00e2e200000900000500626200a6a6a6a2a2a29e9e9e8484
331000fb00fbd5d500801100800d00ea00ea555500a6a600e600e6f7f700e200e233
33110500888888d900d9848484c01a007777003c3c05c8c8008080804409007c7c7c
3312bb00bbaa00aaa600a61e09056262629e009e9a009af322005e5e5e05050000ee
3313005a5a5adddd00a616008d008d00e20016050027270088110078780000c40078
331400787300736f006f44444400aa00c81e004040406600663c3c3c090000550055
33151a1a00343434d91e000084004d004d007c004500453c3c00ea1e00222222113c
3316113300331e1e1efb22001a1a1a004400afaf00270027003c001616161e001e0d
3317160d2f2f00808000001e00d1d1001100110d000db7b7b7090009050005b3b3b3
33186d34c4230000000174524e530040e6d86600000001624b474402660b7c640000
331901f249444154789c6360c0048c8c58049100575f215ee92e6161ef109cd2a15e
33204b9645ce5d2c8f433aa4c24f3cbd4c98833b2314ab74a186f094b9c2c27571d2
33216a2a58e4253c5cda8559057a392363854db4d9d0641973660b0b0bb76bb16656
332206970997256877a07a95c75a1804b2fbcd128c80b482a0b0300f8a824276a9a8
3323ec6e61612b3e57ee06fbf0009619d5fac846ac5c60ed20e754921625a2daadc6
33241967e29e97d2239c8aec7e61fdeca9cecebef54eb36c848517164514af16169e
3325866444b2b0b7b55534c815cc2ec22d89cd1353800a8473100a4485852d924a6a
3326412adc74e7ad1016ceed043267238c901716f633a812022998a4072267c4af02
332792127005c0f811b62830054935ce017b38bf0948cc5c09955f030a24617d9d46
332863371fd940b0827931cbfdf4956076ac018b592f72d45594a9b1f307f3261b1a
3329084bc2ad50018b1900719ba6ba4ca325d0427d3f6161449486f981144cf3100e
33302a5f2a1ce8683e4ddf1b64275240c8438d98af0c729bbe07982b8a1c94201dc2
3331b3174c9820bcc06201585ad81b25b64a2146384e3798290c05ad280a18c0a62e
3332e898260c07fca80a24c076cc864b777131a00190cdfa3069035eccbc038c30e1
33333e88b46d16b6acc5380d6ac202511c392f4b789aa7b0b08718765990111606c2
33349e854c38e5191878fbe471e749b0112bb18902008dc473b2b2e8e72700000000
333549454e44ae426082
3336"""),
3337  'Tp2n3p08': _dehex("""
333889504e470d0a1a0a0000000d494844520000002000000020080300000044a48a
3339c60000000467414d41000186a031e8965f00000300504c544502ffff80ff05ff
33407f0703ff7f0180ff04ff00ffff06ff000880ff05ff7f07ffff06ff000804ff00
33410180ff02ffff03ff7f02ffff80ff0503ff7f0180ffff0008ff7f0704ff00ffff
334206ff000802ffffff7f0704ff0003ff7fffff0680ff050180ff04ff000180ffff
33430008ffff0603ff7f80ff05ff7f0702ffffff000880ff05ffff0603ff7f02ffff
3344ff7f070180ff04ff00ffff06ff000880ff050180ffff7f0702ffff04ff0003ff
33457fff7f0704ff0003ff7f0180ffffff06ff000880ff0502ffffffff0603ff7fff
33467f0702ffff04ff000180ff80ff05ff0008ff7f07ffff0680ff0504ff00ff0008
33470180ff03ff7f02ffff02ffffffff0604ff0003ff7f0180ffff000880ff05ff7f
33480780ff05ff00080180ff02ffffff7f0703ff7fffff0604ff00ff7f07ff0008ff
3349ff0680ff0504ff0002ffff0180ff03ff7fff0008ffff0680ff0504ff000180ff
335002ffff03ff7fff7f070180ff02ffff04ff00ffff06ff0008ff7f0780ff0503ff
33517fffff06ff0008ff7f0780ff0502ffff03ff7f0180ff04ff0002ffffff7f07ff
3352ff0604ff0003ff7fff00080180ff80ff05ffff0603ff7f0180ffff000804ff00
335380ff0502ffffff7f0780ff05ffff0604ff000180ffff000802ffffff7f0703ff
33547fff0008ff7f070180ff03ff7f02ffff80ff05ffff0604ff00ff0008ffff0602
3355ffff0180ff04ff0003ff7f80ff05ff7f070180ff04ff00ff7f0780ff0502ffff
3356ff000803ff7fffff0602ffffff7f07ffff0680ff05ff000804ff0003ff7f0180
3357ff02ffff0180ffff7f0703ff7fff000804ff0080ff05ffff0602ffff04ff00ff
3358ff0603ff7fff7f070180ff80ff05ff000803ff7f0180ffff7f0702ffffff0008
335904ff00ffff0680ff0503ff7f0180ff04ff0080ff05ffff06ff000802ffffff7f
33600780ff05ff0008ff7f070180ff03ff7f04ff0002ffffffff0604ff00ff7f07ff
3361000880ff05ffff060180ff02ffff03ff7f80ff05ffff0602ffff0180ff03ff7f
336204ff00ff7f07ff00080180ffff000880ff0502ffff04ff00ff7f0703ff7fffff
336306ff0008ffff0604ff00ff7f0780ff0502ffff03ff7f0180ffdeb83387000000
3364f874524e53000000000000000008080808080808081010101010101010181818
33651818181818202020202020202029292929292929293131313131313131393939
3366393939393941414141414141414a4a4a4a4a4a4a4a52525252525252525a5a5a
33675a5a5a5a5a62626262626262626a6a6a6a6a6a6a6a73737373737373737b7b7b
33687b7b7b7b7b83838383838383838b8b8b8b8b8b8b8b94949494949494949c9c9c
33699c9c9c9c9ca4a4a4a4a4a4a4a4acacacacacacacacb4b4b4b4b4b4b4b4bdbdbd
3370bdbdbdbdbdc5c5c5c5c5c5c5c5cdcdcdcdcdcdcdcdd5d5d5d5d5d5d5d5dedede
3371dededededee6e6e6e6e6e6e6e6eeeeeeeeeeeeeeeef6f6f6f6f6f6f6f6b98ac5
3372ca0000012c49444154789c6360e7169150d230b475f7098d4ccc28a96ced9e32
337363c1da2d7b8e9fb97af3d1fb8f3f18e8a0808953544a4dd7c4c2c9233c2621bf
3374b4aab17fdacce5ab36ee3a72eafaad87efbefea68702362e7159652d031b07cf
3375c0b8a4cce28aa68e89f316aedfb4ffd0b92bf79fbcfcfe931e0a183904e55435
33768decdcbcc22292b3caaadb7b27cc5db67af3be63e72fdf78fce2d31f7a2860e5
3377119356d037b374f10e8a4fc92eaa6fee99347fc9caad7b0f9ebd74f7c1db2fbf
3378e8a180995f484645dbdccad12f38363dafbcb6a573faeca5ebb6ed3e7ce2c29d
3379e76fbefda38702063e0149751d537b67ff80e8d4dcc29a86bea97316add9b0e3
3380c0e96bf79ebdfafc971e0a587885e515f58cad5d7d43a2d2720aeadaba26cf5a
3381bc62fbcea3272fde7efafac37f3a28000087c0fe101bc2f85f0000000049454e
338244ae426082
3383"""),
3384  'tbbn1g04': _dehex("""
338589504e470d0a1a0a0000000d494844520000002000000020040000000093e1c8
3386290000000467414d41000186a031e8965f0000000274524e530007e8f7589b00
3387000002624b47440000aa8d23320000013e49444154789c55d1cd4b024118c7f1
3388efbe6419045b6a48a72d352808b435284f9187ae9b098627a1573a19945beba5
3389e8129e8222af11d81e3a4545742de8ef6af6d5762e0fbf0fc33c33f36085cb76
3390bc4204778771b867260683ee57e13f0c922df5c719c2b3b6c6c25b2382cea4b9
33919f7d4f244370746ac71f4ca88e0f173a6496749af47de8e44ba8f3bf9bdfa98a
33920faf857a7dd95c7dc8d7c67c782c99727997f41eb2e3c1e554152465bb00fe8e
3393b692d190b718d159f4c0a45c4435915a243c58a7a4312a7a57913f05747594c6
339446169866c57101e4d4ce4d511423119c419183a3530cc63db88559ae28e7342a
33951e9c8122b71139b8872d6e913153224bc1f35b60e4445bd4004e20ed6682c759
33961d9873b3da0fbf50137dc5c9bde84fdb2ec8bde1189e0448b63584735993c209
33977a601bd2710caceba6158797285b7f2084a2f82c57c01a0000000049454e44ae
3398426082
3399"""),
3400  'tbrn2c08': _dehex("""
340189504e470d0a1a0a0000000d4948445200000020000000200802000000fc18ed
3402a30000000467414d41000186a031e8965f0000000674524e53007f007f007f8a
340333334f00000006624b474400ff0000000033277cf3000004d649444154789cad
3404965f68537714c73fd912d640235e692f34d0406fa0c1663481045ab060065514
340556660a295831607df0a1488715167060840a1614e6431e9cb34fd2c00a762c85
3406f6a10f816650c13b0cf40612e1822ddc4863bd628a8924d23d6464f9d3665dd9
3407f7e977ce3dbff3cd3939bfdfef6bb87dfb364782dbed065ebe7cd93acc78b4ec
3408a228debd7bb7bfbfbfbbbbfb7f261045311a8d261209405194274f9ea4d3e916
3409f15f1c3eb5dd6e4fa5fecce526239184a2b0b8486f6f617171b1f5ae4311381c
34108e57af5e5dbd7a351088150a78bd389d44222c2f93cdfe66b7db8f4ee07038b6
3411b6b6bebf766d7e7e7e60a06432313b4ba984c3c1c4049a46b95c5a58583822c1
3412dbb76f27272733d1b9df853c3030c0f232562b9108cf9eb1b888d7cbf030abab
341331abd5fa1f08dc6ef7e7cf9f1f3f7e1c8944745d4f1400c62c001313acad21cb
3414b8dd2c2c603271eb1640341aad4c6d331aa7e8c48913a150a861307ecc11e964
341574899919bc5e14e56fffc404f1388502f178dceff7ef4bf0a5cfe7abb533998c
3416e5f9ea2f1dd88c180d64cb94412df3dd57e83a6b3b3c7a84c98420100c72fd3a
3417636348bae726379fe69e8e8d8dbd79f3a6558b0607079796965256479b918085
34187b02db12712b6181950233023f3f647494ee6e2e5ea45864cce5b8a7fe3acffc
34193aebb22c2bd5d20e22d0757d7b7bbbbdbd3d94a313bed1b0aa3cd069838b163a
34208d4c59585f677292d0b84d9a995bd337def3fe6bbe5e6001989b9b6bfe27ea08
342136373781542ab56573248b4c5bc843ac4048c7ab21aa24ca00534c25482828a3
34228c9ee67475bbaaaab22cb722c8e57240a150301a8d219de94e44534d7d90e885
342387acb0e2c4f9800731629b6c5ee14a35a6b9887d2a0032994cb9cf15dbe59650
3424ff7b46a04c9a749e7cc5112214266cc65c31354d5b5d5d3d90209bcd5616a552
3425a95c2e87f2a659bd9ee01c2cd73964e438f129a6aa9e582c363838b80f81d7eb
34265555b56a2a8ad2d9d7affd0409f8015c208013fea00177b873831b0282c964f2
3427783c1e8fa7582cee5f81a669b5e6eeeeaee58e8559b0c233d8843c7c0b963a82
342834e94b5cb2396d7d7d7db22c8ba258fb0afd43f0e2c58b919191ba9de9b4d425
3429118329b0c3323c8709d02041b52b4ea7f39de75d2a934a2693c0a953a76a93d4
34305d157ebf7f6565a5542a553df97c5e10045dd731c130b86113cc300cbd489224
343108422a952a140a95788fc763b1d41558d7a2d7af5f5fb870a1d6a3aaaacd6603
343218802da84c59015bd2e6897b745d9765b99a1df0f97c0daf74e36deaf7fbcd66
343373ad2797cb89a2c839880188a2e8743a8bc5a22ccbba5e376466b3b9bdbdbd21
34346123413a9d0e0402b51e4dd3bababa788eb022b85caeb6b6364551b6b7b76942
343543f7f727007a7a7a04a1ee8065b3595fde2768423299ac1ec6669c3973e65004
3436c0f8f878ad69341a33994ced2969c0d0d0502412f9f8f163f3a7fd654b474787
3437288ad53e74757535df6215b85cae60302849d2410aecc037f9f2e5cbd5b5c160
3438680eb0dbede170381c0e7ff8f0a185be3b906068684892a4ca7a6f6faff69328
34398ad3d3d3f7efdfdfdbdbfb57e96868a14d0d0643381c96242997cbe5f3794010
344084603078fcf8f1d6496bd14a3aba5c2ea7d369341a5555b5582c8140e0fcf9f3
34411b1b1b87cf4eeb0a8063c78e45a3d19e9e1ebfdfdf5a831e844655d18093274f
34429e3d7bf6d3a74f3b3b3b47c80efc05ff7af28fefb70d9b0000000049454e44ae
3443426082
3444"""),
3445  'basn6a16': _dehex("""
344689504e470d0a1a0a0000000d494844520000002000000020100600000023eaa6
3447b70000000467414d41000186a031e8965f00000d2249444154789cdd995f6c1c
3448d775c67ff38fb34b724d2ee55a8e4b04a0ac87049100cab4dbd8c6528902cb4d
344910881620592e52d4325ac0905bc98a94025e71fd622cb5065ac98a0c283050c0
3450728a00b6e542a1d126885cd3298928891d9a0444037e904434951d4b90b84b2f
3451c9dde1fcebc33977a95555348f411e16dfce9d3b77ee77eebde77ce78c95a669
34520ad07c17009a13edd898b87dfb1fcb7d2b4d1bff217f33df80deb1e6267df0ff
3453c1e6e6dfafdf1f5a7fd30f9aef66b6d546dd355bf02c40662e3307f9725a96c6
3454744c3031f83782f171c148dbc3bf1774f5dad1e79d6f095a3f54d4fbec5234ef
3455d9a2f8d73afe4f14f57ef4f42def7b44f19060f06b45bddf1c5534d77fd922be
34562973a15a82e648661c6e3240aa3612ead952b604bde57458894f29deaf133bac
345713d2766f5227a4a3b8cf08da7adfd6fbd6bd8a4fe9dbb43d35e3dfa3f844fbf8
34589119bf4f7144094fb56333abf8a86063ca106f94b3a3b512343765e60082097f
34591bb86ba72439a653519b09f5cee1ce61c897d37eedf5553580ae60f4af8af33a
3460b14fd400b6a0f34535c0434afc0b3a9f07147527a5fa7ca218ff56c74d74dc3f
3461155cfd3325fc278acf2ae1cb4a539f5f9937c457263b0bd51234c732a300cdd1
3462cc1840f0aaff54db0e4874ed5a9b5d6d27d4bb36746d80de72baa877ff4b275a
3463d7895ed1897ea4139b5143fcbb1a62560da1ed9662aaed895ec78a91c18795b8
34645e07ab4af8ba128e95e682e0728bf8f2e5ae815a091a53d902ac1920d8e05f06
3465589de8d8d66680789f4e454fb9d9ec66cd857af796ee2d902fa73fd5bba775a2
3466153580ae44705ed0d37647d15697cb8f14bfa3e3e8fdf8031d47af571503357c
3467f30d25acedcbbf135c9a35c49766ba07ab255859e8ec03684e66860182dff8f7
34680304bff6ff1c20fc81b7afdd00a71475539a536e36bb5973a19e3b923b02bde5
3469e4efd4003ac170eb2d13fe274157afedbd82d6fb3a9a1e85e4551d47cf7078f8
34709671fe4289ebf5f2bf08d63f37c4eb4773c55a0996efeefa0ca011671d8060ca
34712f0004c7fcc300e166ef0240f825efe3361f106d57d423d0723f7acacd66376b
34722ed47b7a7a7a205f4ef4ac4691e0aad9aa0d41cf13741c3580a506487574ddca
347361a8c403c1863ebfbcac3475168b2de28b8b3d77544bb05ce92a02aceced3c0d
3474d0cc65ea371b201cf1c601c24dde1c4078cedbdeb60322f50126a019bf6edc9b
347539e566b39b3517eaf97c3e0fbde5e4491d45bd74537145d155b476aa0176e868
3476c6abebf30dbd5e525c54ac8e18e2d56abeb756827a3d970358a97416019a6f64
3477f60004fdfe1580d5c98e618070cc1b05887eee7e0d209a70db7d8063029889b4
3478c620ead78d7b33a7dc6c76b3e6427ddddbebde867c393aa7845e5403e8ca794a
3479d0d6fb897af5f03525fe5782f5e7046bdaef468bf88d1debc6ab25583cd17310
34806079b9ab0ba059c914018245bf076075b5a303200c3c1f209a733701444fbbaf
348100c4134ebb016c5d0b23614c243701cdf875e3decce9349bddacb9505fbf7dfd
348276e82d87736a00f5d2b5ffd4b7dce2719a4d25ae717ee153c1abef18e257cfad
34837fa45682da48ef38c052b53b0fd06864b300c151ff08c0ea431de701a287dd5f
3484004497dc7b01a253ee3e80b8c7f91c20f967fb6fdb7c80ada7d8683723614c24
34853701cdf875e3decc29379bddacb950ef3fd47f08f2e5a61ea4aa2a3eb757cd55
348613345efcfa59c12b2f19e2578ef77fb75a82854ffbee01a83f977b11a031931d
3487040802df07082b5e11207cc17b1e209a770700e2df0a83e409fb7580f827c230
348899b06fd901fb058d6835dacd481813c94d40337eddb83773cacd66376b2ed437
3489bebcf165e82d2f4e4beb7f3fa6e652c2d7ee10bc78c010bfb87fe3c95a09ae9f
3490bd732740bd2fb700d0f865f64180e059ff044018ca0ca28a5b04883f701e0088
3491bfec7c0c909cb71f0448c6ec518074b375012079d9dedf66004bcfbc51eb2dd1
3492aadacd481813c94d40337eddb83773cacd66376b2ed487868686205fbe7c49ef
34935605a73f34c4a7a787eeab96e0da81bb4e022c15ba27019a5b339300e16bf286
3494a8eae601e25866907cdf3e0890acb36f00245fb57f05904e59c300e92561946e
3495b2e600d209ab7d07f04d458dfb46ad1bd16ab49b913026929b8066fcba716fe6
3496949bcd6ed65ca8ef7e7cf7e3d05b7e7c8f217ee6cdddbb6a25a856f37980e0c7
3497fe4e80a82623c48193014846ec7180f4acf518409aca0cd28a5504e03b32c374
3498de1a00608a0240faaa327a4b19fe946fb6f90054dbb5f2333d022db56eb4966a
34993723614c243701cdf8f556bea8a7dc6c76b3e66bd46584ddbbcebc0990cf4b0f
3500ff4070520c282338a7e26700ec725202b01e4bcf0258963c6f1d4d8f0030cb20
3501805549c520930c03584fa522b676f11600ffc03fde3e1b3489a9c9054c9aa23b
3502c08856a3dd8c843191dc0434e3d78d7b33a75c36fb993761f7ae5a69f72ef97f
3503e6ad336fed7e1c60e8bee96980bbdebbb60da07b7069062033d9dc0ae03d296f
350470ab511ec071640676252902d833c916007b3e1900b0a6d2028035968e025861
3505ea01581369fb11488c34d18cbc95989afccca42baad65ba2d5683723614c24d7
35068066fcbab8b7e96918baaf5aaa56219f975fb50a43f7c9bde90fa73f1c1a02d8
350778f2e27e803b77ca08b90519315b6fe400fc1392097a9eccc0ad444500e70199
3508a1331f0f00d8934901c07e5d526ceb87c2d07e2579badd005a2b31a5089391b7
35091253358049535a6add8856dd0146c298482e01ede27ed878b256ba7600ee3a09
3510c18fc1df09fe01084ec25defc1b56db0f1a4f4bd78e0e2818d2f0334e7330300
35117df7c888b917e50dd9c1c60c80efcb0cbc63e1f700bce7c31700dccbd1060027
35128add9b0de06c8e2f00d84962b7d7030e2a61538331b98051f92631bd253f336a
3513dd8856a3dd44c25c390efddfad96ae9f853b77c25201ba27c533b8bdf28b6ad0
35143d084b33d2e7fa59099e9901b8f2d29597fa0f01848f78e70082117f1ca07b76
35156910209b9519f895a008d031bbba05c09d8f06005c5b18b8fba25300cea6780e
3516c03e911c6ccf06d507b48a4fa606634a114609de929f9934c5a87511ad57cfc1
3517fa476aa5854fa1ef1e3910b905686e85cc24c40138198915f133d2d6dc2a7dea
35187df2ccc2a752faf2cec1d577aebeb37e3b4034eeee0008dff3be0e6b923773b4
35197904c0ef9119767cb4fa1500ef1361e08e452500f71561e84cc4ed3e20fab6a2
3520c905f40cb76a3026bf3319b91ac2e46792a6dcd801ebc6aba5da08f48ecb81c8
3521bd088d5f42f6417191de93908c803d0e76199292b485af41b60e8d9c3c537f0e
35228211f0c7211a077707dc18b931b2ee6d80a4d7ae024491ebc24d4a708ff70680
35237f25e807e8785f1878e322d6ddaf453f0770ff2dfa769b01423dbbad72a391b6
35245a7c3235985629423372494cab55c8f7d64a8b27a0e7202c55a13b0f8d19c80e
35254ae9ca3f015115dc3ca467c17a4c7ee95970ab10e5a54ff0ac3cd39881ee5958
35261a84f03df0be0e492fd855a8d6aa35d10b4962dbb0a604a3d3ee5e80a8eee600
3527a24977f8660378bf0bbf00e01d0a8fb7f980f04b8aa6ce6aca8d5a7533c52753
3528839152c4e222f4dc512dd5eb90cbc981e8ea12cf90cd8a8bf47d89159e2741d3
35297124f65b96fcd254dae258fa84a13c13043246a32129574787e49eae2b49b86d
3530c3e2e78b9ff7f4002415bb08907c66df0d103b4e0c104db90500ff70700c203a
3531ee1e82dba4c3e16e256c0acca6ceaae9afd1f612d7eb472157ac95962bd05594
35327dd1598466053245088e827f44628657942a825b84e4fb601f84b4025611aca3
3533901e01bb024911dc0a4445f08e41f83df02b10142173149ab71baf027611ea95
35347a257704201d14cd9af4d90b00f194530088cb4e09c0df1c5c0088f7393f6833
3535c0aa3ac156655de3bca9b34ab9716906ba07aba5e5bba1eb3358d90b9da7c533
353664f6888bf47b60f521e8380fe10be03d2feac17900927560df40f4e48f805960
353750328d648bf4893f9067c217a0631656b7c898c122847bc07b03a2d3e0ee85e4
353833b0ef867450c4fad2ecd26cf7168074c0ba0c904cdac300c9cfec4701924df6
35391cdca61e10685c6f7d52d0caba1498972f43d740adb4b2009d7d7220b20e3473
354090a943d00ffe959bb6eac3e0fe42ea49ee00c45f06e76329b1dabf127d690d80
35415581b408f63c2403e0cc433c00ee658836803b0fd100747c04ab5f917704fd10
3542d5c1cd41ec801343d207f602a403605d86e5f9e5f9ae0d00e994556833806685
3543c931fb709b0f08b4e869bea5c827859549e82c544b8d29c816a0390999613920
35447e610d5727a16318c2003c1fa24be0de2b32caf92224e7c17e5004b6350c4c01
354505601218066b0ad28224e149019c086257ca315102de2712903bde97b8144d82
35463b2c6ac52d403c054e019249b087f53d0558995a99ea946c70cc927458b3c1ff
3547550f30050df988d4284376b4566a8e416654cc921985e037e0df0fc131f00f4b
3548acf0c6211c036f14a239703741740adc7da227edd7e56b833d0ae92549b4d357
354925dfb49ed2ff63908e6adf27d6d0dda7638d4154d2778daca17f58e61297c129
355041f233b01f5dc3740cac51688c35c6b22580f48224fee9b83502569a66b629f1
355109f3713473413e2666e7fe6f6c6efefdfafda1f56f6e06f93496d9d67cb7366a
35529964b6f92e64b689196ec6c604646fd3fe4771ff1bf03f65d8ecc3addbb5f300
355300000049454e44ae426082
3554"""),
3555}
3556
3557def read_pam_header(infile):
3558    """
3559    Read (the rest of a) PAM header.  `infile` should be positioned
3560    immediately after the initial 'P7' line (at the beginning of the
3561    second line).  Returns are as for `read_pnm_header`.
3562    """
3563
3564    # Unlike PBM, PGM, and PPM, we can read the header a line at a time.
3565    header = dict()
3566    while True:
3567        l = infile.readline().strip()
3568        if l == strtobytes('ENDHDR'):
3569            break
3570        if not l:
3571            raise EOFError('PAM ended prematurely')
3572        if l[0] == strtobytes('#'):
3573            continue
3574        l = l.split(None, 1)
3575        if l[0] not in header:
3576            header[l[0]] = l[1]
3577        else:
3578            header[l[0]] += strtobytes(' ') + l[1]
3579
3580    required = ['WIDTH', 'HEIGHT', 'DEPTH', 'MAXVAL']
3581    required = [strtobytes(x) for x in required]
3582    WIDTH,HEIGHT,DEPTH,MAXVAL = required
3583    present = [x for x in required if x in header]
3584    if len(present) != len(required):
3585        raise Error('PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL')
3586    width = int(header[WIDTH])
3587    height = int(header[HEIGHT])
3588    depth = int(header[DEPTH])
3589    maxval = int(header[MAXVAL])
3590    if (width <= 0 or
3591        height <= 0 or
3592        depth <= 0 or
3593        maxval <= 0):
3594        raise Error(
3595          'WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers')
3596    return 'P7', width, height, depth, maxval
3597
3598def read_pnm_header(infile, supported=('P5','P6')):
3599    """
3600    Read a PNM header, returning (format,width,height,depth,maxval).
3601    `width` and `height` are in pixels.  `depth` is the number of
3602    channels in the image; for PBM and PGM it is synthesized as 1, for
3603    PPM as 3; for PAM images it is read from the header.  `maxval` is
3604    synthesized (as 1) for PBM images.
3605    """
3606
3607    # Generally, see http://netpbm.sourceforge.net/doc/ppm.html
3608    # and http://netpbm.sourceforge.net/doc/pam.html
3609
3610    supported = [strtobytes(x) for x in supported]
3611
3612    # Technically 'P7' must be followed by a newline, so by using
3613    # rstrip() we are being liberal in what we accept.  I think this
3614    # is acceptable.
3615    type = infile.read(3).rstrip()
3616    if type not in supported:
3617        raise NotImplementedError('file format %s not supported' % type)
3618    if type == strtobytes('P7'):
3619        # PAM header parsing is completely different.
3620        return read_pam_header(infile)
3621    # Expected number of tokens in header (3 for P4, 4 for P6)
3622    expected = 4
3623    pbm = ('P1', 'P4')
3624    if type in pbm:
3625        expected = 3
3626    header = [type]
3627
3628    # We have to read the rest of the header byte by byte because the
3629    # final whitespace character (immediately following the MAXVAL in
3630    # the case of P6) may not be a newline.  Of course all PNM files in
3631    # the wild use a newline at this point, so it's tempting to use
3632    # readline; but it would be wrong.
3633    def getc():
3634        c = infile.read(1)
3635        if not c:
3636            raise Error('premature EOF reading PNM header')
3637        return c
3638
3639    c = getc()
3640    while True:
3641        # Skip whitespace that precedes a token.
3642        while c.isspace():
3643            c = getc()
3644        # Skip comments.
3645        while c == '#':
3646            while c not in '\n\r':
3647                c = getc()
3648        if not c.isdigit():
3649            raise Error('unexpected character %s found in header' % c)
3650        # According to the specification it is legal to have comments
3651        # that appear in the middle of a token.
3652        # This is bonkers; I've never seen it; and it's a bit awkward to
3653        # code good lexers in Python (no goto).  So we break on such
3654        # cases.
3655        token = strtobytes('')
3656        while c.isdigit():
3657            token += c
3658            c = getc()
3659        # Slight hack.  All "tokens" are decimal integers, so convert
3660        # them here.
3661        header.append(int(token))
3662        if len(header) == expected:
3663            break
3664    # Skip comments (again)
3665    while c == '#':
3666        while c not in '\n\r':
3667            c = getc()
3668    if not c.isspace():
3669        raise Error('expected header to end with whitespace, not %s' % c)
3670
3671    if type in pbm:
3672        # synthesize a MAXVAL
3673        header.append(1)
3674    depth = (1,3)[type == strtobytes('P6')]
3675    return header[0], header[1], header[2], depth, header[3]
3676
3677def write_pnm(file, width, height, pixels, meta):
3678    """Write a Netpbm PNM/PAM file."""
3679
3680    bitdepth = meta['bitdepth']
3681    maxval = 2**bitdepth - 1
3682    # Rudely, the number of image planes can be used to determine
3683    # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM).
3684    planes = meta['planes']
3685    # Can be an assert as long as we assume that pixels and meta came
3686    # from a PNG file.
3687    assert planes in (1,2,3,4)
3688    if planes in (1,3):
3689        if 1 == planes:
3690            # PGM
3691            # Could generate PBM if maxval is 1, but we don't (for one
3692            # thing, we'd have to convert the data, not just blat it
3693            # out).
3694            fmt = 'P5'
3695        else:
3696            # PPM
3697            fmt = 'P6'
3698        file.write('%s %d %d %d\n' % (fmt, width, height, maxval))
3699    if planes in (2,4):
3700        # PAM
3701        # See http://netpbm.sourceforge.net/doc/pam.html
3702        if 2 == planes:
3703            tupltype = 'GRAYSCALE_ALPHA'
3704        else:
3705            tupltype = 'RGB_ALPHA'
3706        file.write('P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n'
3707                   'TUPLTYPE %s\nENDHDR\n' %
3708                   (width, height, planes, maxval, tupltype))
3709    # Values per row
3710    vpr = planes * width
3711    # struct format
3712    fmt = '>%d' % vpr
3713    if maxval > 0xff:
3714        fmt = fmt + 'H'
3715    else:
3716        fmt = fmt + 'B'
3717    for row in pixels:
3718        file.write(struct.pack(fmt, *row))
3719    file.flush()
3720
3721def color_triple(color):
3722    """
3723    Convert a command line colour value to a RGB triple of integers.
3724    FIXME: Somewhere we need support for greyscale backgrounds etc.
3725    """
3726    if color.startswith('#') and len(color) == 4:
3727        return (int(color[1], 16),
3728                int(color[2], 16),
3729                int(color[3], 16))
3730    if color.startswith('#') and len(color) == 7:
3731        return (int(color[1:3], 16),
3732                int(color[3:5], 16),
3733                int(color[5:7], 16))
3734    elif color.startswith('#') and len(color) == 13:
3735        return (int(color[1:5], 16),
3736                int(color[5:9], 16),
3737                int(color[9:13], 16))
3738
3739def _add_common_options(parser):
3740    """Call *parser.add_option* for each of the options that are
3741    common between this PNG--PNM conversion tool and the gen
3742    tool.
3743    """
3744    parser.add_option("-i", "--interlace",
3745                      default=False, action="store_true",
3746                      help="create an interlaced PNG file (Adam7)")
3747    parser.add_option("-t", "--transparent",
3748                      action="store", type="string", metavar="#RRGGBB",
3749                      help="mark the specified colour as transparent")
3750    parser.add_option("-b", "--background",
3751                      action="store", type="string", metavar="#RRGGBB",
3752                      help="save the specified background colour")
3753    parser.add_option("-g", "--gamma",
3754                      action="store", type="float", metavar="value",
3755                      help="save the specified gamma value")
3756    parser.add_option("-c", "--compression",
3757                      action="store", type="int", metavar="level",
3758                      help="zlib compression level (0-9)")
3759    return parser
3760
3761def _main(argv):
3762    """
3763    Run the PNG encoder with options from the command line.
3764    """
3765
3766    # Parse command line arguments
3767    from optparse import OptionParser
3768    import re
3769    version = '%prog ' + re.sub(r'( ?\$|URL: |Rev:)', '', __version__)
3770    parser = OptionParser(version=version)
3771    parser.set_usage("%prog [options] [imagefile]")
3772    parser.add_option('-r', '--read-png', default=False,
3773                      action='store_true',
3774                      help='Read PNG, write PNM')
3775    parser.add_option("-a", "--alpha",
3776                      action="store", type="string", metavar="pgmfile",
3777                      help="alpha channel transparency (RGBA)")
3778    _add_common_options(parser)
3779
3780    (options, args) = parser.parse_args(args=argv[1:])
3781
3782    # Convert options
3783    if options.transparent is not None:
3784        options.transparent = color_triple(options.transparent)
3785    if options.background is not None:
3786        options.background = color_triple(options.background)
3787
3788    # Prepare input and output files
3789    if len(args) == 0:
3790        infilename = '-'
3791        infile = sys.stdin
3792    elif len(args) == 1:
3793        infilename = args[0]
3794        infile = open(infilename, 'rb')
3795    else:
3796        parser.error("more than one input file")
3797    outfile = sys.stdout
3798    if sys.platform == "win32":
3799        import msvcrt, os
3800        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
3801
3802    if options.read_png:
3803        # Encode PNG to PPM
3804        png = Reader(file=infile)
3805        width,height,pixels,meta = png.asDirect()
3806        write_pnm(outfile, width, height, pixels, meta)
3807    else:
3808        # Encode PNM to PNG
3809        format, width, height, depth, maxval = \
3810          read_pnm_header(infile, ('P5','P6','P7'))
3811        # When it comes to the variety of input formats, we do something
3812        # rather rude.  Observe that L, LA, RGB, RGBA are the 4 colour
3813        # types supported by PNG and that they correspond to 1, 2, 3, 4
3814        # channels respectively.  So we use the number of channels in
3815        # the source image to determine which one we have.  We do not
3816        # care about TUPLTYPE.
3817        greyscale = depth <= 2
3818        pamalpha = depth in (2,4)
3819        supported = map(lambda x: 2**x-1, range(1,17))
3820        try:
3821            mi = supported.index(maxval)
3822        except ValueError:
3823            raise NotImplementedError(
3824              'your maxval (%s) not in supported list %s' %
3825              (maxval, str(supported)))
3826        bitdepth = mi+1
3827        writer = Writer(width, height,
3828                        greyscale=greyscale,
3829                        bitdepth=bitdepth,
3830                        interlace=options.interlace,
3831                        transparent=options.transparent,
3832                        background=options.background,
3833                        alpha=bool(pamalpha or options.alpha),
3834                        gamma=options.gamma,
3835                        compression=options.compression)
3836        if options.alpha:
3837            pgmfile = open(options.alpha, 'rb')
3838            format, awidth, aheight, adepth, amaxval = \
3839              read_pnm_header(pgmfile, 'P5')
3840            if amaxval != '255':
3841                raise NotImplementedError(
3842                  'maxval %s not supported for alpha channel' % amaxval)
3843            if (awidth, aheight) != (width, height):
3844                raise ValueError("alpha channel image size mismatch"
3845                                 " (%s has %sx%s but %s has %sx%s)"
3846                                 % (infilename, width, height,
3847                                    options.alpha, awidth, aheight))
3848            writer.convert_ppm_and_pgm(infile, pgmfile, outfile)
3849        else:
3850            writer.convert_pnm(infile, outfile)
3851
3852
3853if __name__ == '__main__':
3854    try:
3855        _main(sys.argv)
3856    except Error, e:
3857        print >>sys.stderr, e
3858