1#!/usr/bin/env python
2
3# png.py - PNG encoder/decoder in pure Python
4#
5# Copyright (C) 2006 Johann C. Rocholl <johann@browsershots.org>
6# Portions Copyright (C) 2009 David Jones <drj@pobox.com>
7# And probably portions Copyright (C) 2006 Nicko van Someren <nicko@nicko.org>
8#
9# Original concept by Johann C. Rocholl.
10#
11# LICENCE (MIT)
12#
13# Permission is hereby granted, free of charge, to any person
14# obtaining a copy of this software and associated documentation files
15# (the "Software"), to deal in the Software without restriction,
16# including without limitation the rights to use, copy, modify, merge,
17# publish, distribute, sublicense, and/or sell copies of the Software,
18# and to permit persons to whom the Software is furnished to do so,
19# subject to the following conditions:
20#
21# The above copyright notice and this permission notice shall be
22# included in all copies or substantial portions of the Software.
23#
24# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31# SOFTWARE.
32
33"""
34Pure Python PNG Reader/Writer
35
36This Python module implements support for PNG images (see PNG
37specification at http://www.w3.org/TR/2003/REC-PNG-20031110/ ). It reads
38and writes PNG files with all allowable bit depths
39(1/2/4/8/16/24/32/48/64 bits per pixel) and colour combinations:
40greyscale (1/2/4/8/16 bit); RGB, RGBA, LA (greyscale with alpha) with
418/16 bits per channel; colour mapped images (1/2/4/8 bit).
42Adam7 interlacing is supported for reading and
43writing.  A number of optional chunks can be specified (when writing)
44and understood (when reading): ``tRNS``, ``bKGD``, ``gAMA``.
45
46For help, type ``import png; help(png)`` in your python interpreter.
47
48A good place to start is the :class:`Reader` and :class:`Writer`
49classes.
50
51Requires Python 2.3.  Limited support is available for Python 2.2, but
52not everything works.  Best with Python 2.4 and higher.  Installation is
53trivial, but see the ``README.txt`` file (with the source distribution)
54for details.
55
56This file can also be used as a command-line utility to convert
57`Netpbm <http://netpbm.sourceforge.net/>`_ PNM files to PNG, and the
58reverse conversion from PNG to PNM. The interface is similar to that
59of the ``pnmtopng`` program from Netpbm.  Type ``python png.py --help``
60at the shell prompt for usage and a list of options.
61
62A note on spelling and terminology
63----------------------------------
64
65Generally British English spelling is used in the documentation.  So
66that's "greyscale" and "colour".  This not only matches the author's
67native language, it's also used by the PNG specification.
68
69The major colour models supported by PNG (and hence by PyPNG) are:
70greyscale, RGB, greyscale--alpha, RGB--alpha.  These are sometimes
71referred to using the abbreviations: L, RGB, LA, RGBA.  In this case
72each letter abbreviates a single channel: *L* is for Luminance or Luma
73or Lightness which is the channel used in greyscale images; *R*, *G*,
74*B* stand for Red, Green, Blue, the components of a colour image; *A*
75stands for Alpha, the opacity channel (used for transparency effects,
76but higher values are more opaque, so it makes sense to call it
77opacity).
78
79A note on formats
80-----------------
81
82When getting pixel data out of this module (reading) and presenting
83data to this module (writing) there are a number of ways the data could
84be represented as a Python value.  Generally this module uses one of
85three formats called "flat row flat pixel", "boxed row flat pixel", and
86"boxed row boxed pixel".  Basically the concern is whether each pixel
87and each row comes in its own little tuple (box), or not.
88
89Consider an image that is 3 pixels wide by 2 pixels high, and each pixel
90has RGB components:
91
92Boxed row flat pixel::
93
94  list([R,G,B, R,G,B, R,G,B],
95       [R,G,B, R,G,B, R,G,B])
96
97Each row appears as its own list, but the pixels are flattened so
98that three values for one pixel simply follow the three values for
99the previous pixel.  This is the most common format used, because it
100provides a good compromise between space and convenience.  PyPNG regards
101itself as at liberty to replace any sequence type with any sufficiently
102compatible other sequence type; in practice each row is an array (from
103the array module), and the outer list is sometimes an iterator rather
104than an explicit list (so that streaming is possible).
105
106Flat row flat pixel::
107
108  [R,G,B, R,G,B, R,G,B,
109   R,G,B, R,G,B, R,G,B]
110
111The entire image is one single giant sequence of colour values.
112Generally an array will be used (to save space), not a list.
113
114Boxed row boxed pixel::
115
116  list([ (R,G,B), (R,G,B), (R,G,B) ],
117       [ (R,G,B), (R,G,B), (R,G,B) ])
118
119Each row appears in its own list, but each pixel also appears in its own
120tuple.  A serious memory burn in Python.
121
122In all cases the top row comes first, and for each row the pixels are
123ordered from left-to-right.  Within a pixel the values appear in the
124order, R-G-B-A (or L-A for greyscale--alpha).
125
126There is a fourth format, mentioned because it is used internally,
127is close to what lies inside a PNG file itself, and has some support
128from the public API.  This format is called packed.  When packed,
129each row is a sequence of bytes (integers from 0 to 255), just as
130it is before PNG scanline filtering is applied.  When the bit depth
131is 8 this is essentially the same as boxed row flat pixel; when the
132bit depth is less than 8, several pixels are packed into each byte;
133when the bit depth is 16 (the only value more than 8 that is supported
134by the PNG image format) each pixel value is decomposed into 2 bytes
135(and `packed` is a misnomer).  This format is used by the
136:meth:`Writer.write_packed` method.  It isn't usually a convenient
137format, but may be just right if the source data for the PNG image
138comes from something that uses a similar format (for example, 1-bit
139BMPs, or another PNG file).
140
141And now, my famous members
142--------------------------
143"""
144
145# http://www.python.org/doc/2.2.3/whatsnew/node5.html
146
147
148__version__ = "0.0.17"
149
150from array import array
151from functools import reduce
152try: # See :pyver:old
153    import itertools
154except ImportError:
155    pass
156import math
157# http://www.python.org/doc/2.4.4/lib/module-operator.html
158import operator
159import struct
160import sys
161import zlib
162# http://www.python.org/doc/2.4.4/lib/module-warnings.html
163import warnings
164try:
165    # `cpngfilters` is a Cython module: it must be compiled by
166    # Cython for this import to work.
167    # If this import does work, then it overrides pure-python
168    # filtering functions defined later in this file (see `class
169    # pngfilters`).
170    import cpngfilters as pngfilters
171except ImportError:
172    pass
173
174
175__all__ = ['Image', 'Reader', 'Writer', 'write_chunks', 'from_array']
176
177
178# The PNG signature.
179# http://www.w3.org/TR/PNG/#5PNG-file-signature
180_signature = struct.pack('8B', 137, 80, 78, 71, 13, 10, 26, 10)
181
182_adam7 = ((0, 0, 8, 8),
183          (4, 0, 8, 8),
184          (0, 4, 4, 8),
185          (2, 0, 4, 4),
186          (0, 2, 2, 4),
187          (1, 0, 2, 2),
188          (0, 1, 1, 2))
189
190def group(s, n):
191    # See http://www.python.org/doc/2.6/library/functions.html#zip
192    return list(zip(*[iter(s)]*n))
193
194def isarray(x):
195    """Same as ``isinstance(x, array)`` except on Python 2.2, where it
196    always returns ``False``.  This helps PyPNG work on Python 2.2.
197    """
198
199    try:
200        return isinstance(x, array)
201    except TypeError:
202        # Because on Python 2.2 array.array is not a type.
203        return False
204
205try:
206    array.tobytes
207except AttributeError:
208    try:  # see :pyver:old
209        array.tostring
210    except AttributeError:
211        def tostring(row):
212            l = len(row)
213            return struct.pack('%dB' % l, *row)
214    else:
215        def tostring(row):
216            """Convert row of bytes to string.  Expects `row` to be an
217            ``array``.
218            """
219            return row.tostring()
220else:
221    def tostring(row):
222        """ Python3 definition, array.tostring() is deprecated in Python3
223        """
224        return row.tobytes()
225
226# Conditionally convert to bytes.  Works on Python 2 and Python 3.
227try:
228    bytes('', 'ascii')
229    def strtobytes(x): return bytes(x, 'iso8859-1')
230    def bytestostr(x): return str(x, 'iso8859-1')
231except (NameError, TypeError):
232    # We get NameError when bytes() does not exist (most Python
233    # 2.x versions), and TypeError when bytes() exists but is on
234    # Python 2.x (when it is an alias for str() and takes at most
235    # one argument).
236    strtobytes = str
237    bytestostr = str
238
239def interleave_planes(ipixels, apixels, ipsize, apsize):
240    """
241    Interleave (colour) planes, e.g. RGB + A = RGBA.
242
243    Return an array of pixels consisting of the `ipsize` elements of
244    data from each pixel in `ipixels` followed by the `apsize` elements
245    of data from each pixel in `apixels`.  Conventionally `ipixels`
246    and `apixels` are byte arrays so the sizes are bytes, but it
247    actually works with any arrays of the same type.  The returned
248    array is the same type as the input arrays which should be the
249    same type as each other.
250    """
251
252    itotal = len(ipixels)
253    atotal = len(apixels)
254    newtotal = itotal + atotal
255    newpsize = ipsize + apsize
256    # Set up the output buffer
257    # See http://www.python.org/doc/2.4.4/lib/module-array.html#l2h-1356
258    out = array(ipixels.typecode)
259    # It's annoying that there is no cheap way to set the array size :-(
260    out.extend(ipixels)
261    out.extend(apixels)
262    # Interleave in the pixel data
263    for i in range(ipsize):
264        out[i:newtotal:newpsize] = ipixels[i:itotal:ipsize]
265    for i in range(apsize):
266        out[i+ipsize:newtotal:newpsize] = apixels[i:atotal:apsize]
267    return out
268
269def check_palette(palette):
270    """Check a palette argument (to the :class:`Writer` class)
271    for validity.  Returns the palette as a list if okay; raises an
272    exception otherwise.
273    """
274
275    # None is the default and is allowed.
276    if palette is None:
277        return None
278
279    p = list(palette)
280    if not (0 < len(p) <= 256):
281        raise ValueError("a palette must have between 1 and 256 entries")
282    seen_triple = False
283    for i,t in enumerate(p):
284        if len(t) not in (3,4):
285            raise ValueError(
286              "palette entry %d: entries must be 3- or 4-tuples." % i)
287        if len(t) == 3:
288            seen_triple = True
289        if seen_triple and len(t) == 4:
290            raise ValueError(
291              "palette entry %d: all 4-tuples must precede all 3-tuples" % i)
292        for x in t:
293            if int(x) != x or not(0 <= x <= 255):
294                raise ValueError(
295                  "palette entry %d: values must be integer: 0 <= x <= 255" % i)
296    return p
297
298def check_sizes(size, width, height):
299    """Check that these arguments, in supplied, are consistent.
300    Return a (width, height) pair.
301    """
302
303    if not size:
304        return width, height
305
306    if len(size) != 2:
307        raise ValueError(
308          "size argument should be a pair (width, height)")
309    if width is not None and width != size[0]:
310        raise ValueError(
311          "size[0] (%r) and width (%r) should match when both are used."
312            % (size[0], width))
313    if height is not None and height != size[1]:
314        raise ValueError(
315          "size[1] (%r) and height (%r) should match when both are used."
316            % (size[1], height))
317    return size
318
319def check_color(c, greyscale, which):
320    """Checks that a colour argument for transparent or
321    background options is the right form.  Returns the colour
322    (which, if it's a bar integer, is "corrected" to a 1-tuple).
323    """
324
325    if c is None:
326        return c
327    if greyscale:
328        try:
329            l = len(c)
330        except TypeError:
331            c = (c,)
332        if len(c) != 1:
333            raise ValueError("%s for greyscale must be 1-tuple" %
334                which)
335        if not isinteger(c[0]):
336            raise ValueError(
337                "%s colour for greyscale must be integer" % which)
338    else:
339        if not (len(c) == 3 and
340                isinteger(c[0]) and
341                isinteger(c[1]) and
342                isinteger(c[2])):
343            raise ValueError(
344                "%s colour must be a triple of integers" % which)
345    return c
346
347class Error(Exception):
348    def __str__(self):
349        return self.__class__.__name__ + ': ' + ' '.join(self.args)
350
351class FormatError(Error):
352    """Problem with input file format.  In other words, PNG file does
353    not conform to the specification in some way and is invalid.
354    """
355
356class ChunkError(FormatError):
357    pass
358
359
360class Writer:
361    """
362    PNG encoder in pure Python.
363    """
364
365    def __init__(self, width=None, height=None,
366                 size=None,
367                 greyscale=False,
368                 alpha=False,
369                 bitdepth=8,
370                 palette=None,
371                 transparent=None,
372                 background=None,
373                 gamma=None,
374                 compression=None,
375                 interlace=False,
376                 bytes_per_sample=None, # deprecated
377                 planes=None,
378                 colormap=None,
379                 maxval=None,
380                 chunk_limit=2**20):
381        """
382        Create a PNG encoder object.
383
384        Arguments:
385
386        width, height
387          Image size in pixels, as two separate arguments.
388        size
389          Image size (w,h) in pixels, as single argument.
390        greyscale
391          Input data is greyscale, not RGB.
392        alpha
393          Input data has alpha channel (RGBA or LA).
394        bitdepth
395          Bit depth: from 1 to 16.
396        palette
397          Create a palette for a colour mapped image (colour type 3).
398        transparent
399          Specify a transparent colour (create a ``tRNS`` chunk).
400        background
401          Specify a default background colour (create a ``bKGD`` chunk).
402        gamma
403          Specify a gamma value (create a ``gAMA`` chunk).
404        compression
405          zlib compression level: 0 (none) to 9 (more compressed);
406          default: -1 or None.
407        interlace
408          Create an interlaced image.
409        chunk_limit
410          Write multiple ``IDAT`` chunks to save memory.
411
412        The image size (in pixels) can be specified either by using the
413        `width` and `height` arguments, or with the single `size`
414        argument.  If `size` is used it should be a pair (*width*,
415        *height*).
416
417        `greyscale` and `alpha` are booleans that specify whether
418        an image is greyscale (or colour), and whether it has an
419        alpha channel (or not).
420
421        `bitdepth` specifies the bit depth of the source pixel values.
422        Each source pixel value must be an integer between 0 and
423        ``2**bitdepth-1``.  For example, 8-bit images have values
424        between 0 and 255.  PNG only stores images with bit depths of
425        1,2,4,8, or 16.  When `bitdepth` is not one of these values,
426        the next highest valid bit depth is selected, and an ``sBIT``
427        (significant bits) chunk is generated that specifies the
428        original precision of the source image.  In this case the
429        supplied pixel values will be rescaled to fit the range of
430        the selected bit depth.
431
432        The details of which bit depth / colour model combinations the
433        PNG file format supports directly, are somewhat arcane
434        (refer to the PNG specification for full details).  Briefly:
435        "small" bit depths (1,2,4) are only allowed with greyscale and
436        colour mapped images; colour mapped images cannot have bit depth
437        16.
438
439        For colour mapped images (in other words, when the `palette`
440        argument is specified) the `bitdepth` argument must match one of
441        the valid PNG bit depths: 1, 2, 4, or 8.  (It is valid to have a
442        PNG image with a palette and an ``sBIT`` chunk, but the meaning
443        is slightly different; it would be awkward to press the
444        `bitdepth` argument into service for this.)
445
446        The `palette` option, when specified, causes a colour mapped
447        image to be created: the PNG colour type is set to 3; greyscale
448        must not be set; alpha must not be set; transparent must not be
449        set; the bit depth must be 1,2,4, or 8.  When a colour mapped
450        image is created, the pixel values are palette indexes and
451        the `bitdepth` argument specifies the size of these indexes
452        (not the size of the colour values in the palette).
453
454        The palette argument value should be a sequence of 3- or
455        4-tuples.  3-tuples specify RGB palette entries; 4-tuples
456        specify RGBA palette entries.  If both 4-tuples and 3-tuples
457        appear in the sequence then all the 4-tuples must come
458        before all the 3-tuples.  A ``PLTE`` chunk is created; if there
459        are 4-tuples then a ``tRNS`` chunk is created as well.  The
460        ``PLTE`` chunk will contain all the RGB triples in the same
461        sequence; the ``tRNS`` chunk will contain the alpha channel for
462        all the 4-tuples, in the same sequence.  Palette entries
463        are always 8-bit.
464
465        If specified, the `transparent` and `background` parameters must
466        be a tuple with three integer values for red, green, blue, or
467        a simple integer (or singleton tuple) for a greyscale image.
468
469        If specified, the `gamma` parameter must be a positive number
470        (generally, a float).  A ``gAMA`` chunk will be created.
471        Note that this will not change the values of the pixels as
472        they appear in the PNG file, they are assumed to have already
473        been converted appropriately for the gamma specified.
474
475        The `compression` argument specifies the compression level to
476        be used by the ``zlib`` module.  Values from 1 to 9 specify
477        compression, with 9 being "more compressed" (usually smaller
478        and slower, but it doesn't always work out that way).  0 means
479        no compression.  -1 and ``None`` both mean that the default
480        level of compession will be picked by the ``zlib`` module
481        (which is generally acceptable).
482
483        If `interlace` is true then an interlaced image is created
484        (using PNG's so far only interace method, *Adam7*).  This does
485        not affect how the pixels should be presented to the encoder,
486        rather it changes how they are arranged into the PNG file.
487        On slow connexions interlaced images can be partially decoded
488        by the browser to give a rough view of the image that is
489        successively refined as more image data appears.
490
491        .. note ::
492
493          Enabling the `interlace` option requires the entire image
494          to be processed in working memory.
495
496        `chunk_limit` is used to limit the amount of memory used whilst
497        compressing the image.  In order to avoid using large amounts of
498        memory, multiple ``IDAT`` chunks may be created.
499        """
500
501        # At the moment the `planes` argument is ignored;
502        # its purpose is to act as a dummy so that
503        # ``Writer(x, y, **info)`` works, where `info` is a dictionary
504        # returned by Reader.read and friends.
505        # Ditto for `colormap`.
506
507        width, height = check_sizes(size, width, height)
508        del size
509
510        if width <= 0 or height <= 0:
511            raise ValueError("width and height must be greater than zero")
512        if not isinteger(width) or not isinteger(height):
513            raise ValueError("width and height must be integers")
514        # http://www.w3.org/TR/PNG/#7Integers-and-byte-order
515        if width > 2**32-1 or height > 2**32-1:
516            raise ValueError("width and height cannot exceed 2**32-1")
517
518        if alpha and transparent is not None:
519            raise ValueError(
520                "transparent colour not allowed with alpha channel")
521
522        if bytes_per_sample is not None:
523            warnings.warn('please use bitdepth instead of bytes_per_sample',
524                          DeprecationWarning)
525            if bytes_per_sample not in (0.125, 0.25, 0.5, 1, 2):
526                raise ValueError(
527                    "bytes per sample must be .125, .25, .5, 1, or 2")
528            bitdepth = int(8*bytes_per_sample)
529        del bytes_per_sample
530        if not isinteger(bitdepth) or bitdepth < 1 or 16 < bitdepth:
531            raise ValueError("bitdepth (%r) must be a postive integer <= 16" %
532              bitdepth)
533
534        self.rescale = None
535        if palette:
536            if bitdepth not in (1,2,4,8):
537                raise ValueError("with palette, bitdepth must be 1, 2, 4, or 8")
538            if transparent is not None:
539                raise ValueError("transparent and palette not compatible")
540            if alpha:
541                raise ValueError("alpha and palette not compatible")
542            if greyscale:
543                raise ValueError("greyscale and palette not compatible")
544        else:
545            # No palette, check for sBIT chunk generation.
546            if alpha or not greyscale:
547                if bitdepth not in (8,16):
548                    targetbitdepth = (8,16)[bitdepth > 8]
549                    self.rescale = (bitdepth, targetbitdepth)
550                    bitdepth = targetbitdepth
551                    del targetbitdepth
552            else:
553                assert greyscale
554                assert not alpha
555                if bitdepth not in (1,2,4,8,16):
556                    if bitdepth > 8:
557                        targetbitdepth = 16
558                    elif bitdepth == 3:
559                        targetbitdepth = 4
560                    else:
561                        assert bitdepth in (5,6,7)
562                        targetbitdepth = 8
563                    self.rescale = (bitdepth, targetbitdepth)
564                    bitdepth = targetbitdepth
565                    del targetbitdepth
566
567        if bitdepth < 8 and (alpha or not greyscale and not palette):
568            raise ValueError(
569              "bitdepth < 8 only permitted with greyscale or palette")
570        if bitdepth > 8 and palette:
571            raise ValueError(
572                "bit depth must be 8 or less for images with palette")
573
574        transparent = check_color(transparent, greyscale, 'transparent')
575        background = check_color(background, greyscale, 'background')
576
577        # It's important that the true boolean values (greyscale, alpha,
578        # colormap, interlace) are converted to bool because Iverson's
579        # convention is relied upon later on.
580        self.width = width
581        self.height = height
582        self.transparent = transparent
583        self.background = background
584        self.gamma = gamma
585        self.greyscale = bool(greyscale)
586        self.alpha = bool(alpha)
587        self.colormap = bool(palette)
588        self.bitdepth = int(bitdepth)
589        self.compression = compression
590        self.chunk_limit = chunk_limit
591        self.interlace = bool(interlace)
592        self.palette = check_palette(palette)
593
594        self.color_type = 4*self.alpha + 2*(not greyscale) + 1*self.colormap
595        assert self.color_type in (0,2,3,4,6)
596
597        self.color_planes = (3,1)[self.greyscale or self.colormap]
598        self.planes = self.color_planes + self.alpha
599        # :todo: fix for bitdepth < 8
600        self.psize = (self.bitdepth/8) * self.planes
601
602    def make_palette(self):
603        """Create the byte sequences for a ``PLTE`` and if necessary a
604        ``tRNS`` chunk.  Returned as a pair (*p*, *t*).  *t* will be
605        ``None`` if no ``tRNS`` chunk is necessary.
606        """
607
608        p = array('B')
609        t = array('B')
610
611        for x in self.palette:
612            p.extend(x[0:3])
613            if len(x) > 3:
614                t.append(x[3])
615        p = tostring(p)
616        t = tostring(t)
617        if t:
618            return p,t
619        return p,None
620
621    def write(self, outfile, rows):
622        """Write a PNG image to the output file.  `rows` should be
623        an iterable that yields each row in boxed row flat pixel
624        format.  The rows should be the rows of the original image,
625        so there should be ``self.height`` rows of ``self.width *
626        self.planes`` values.  If `interlace` is specified (when
627        creating the instance), then an interlaced PNG file will
628        be written.  Supply the rows in the normal image order;
629        the interlacing is carried out internally.
630
631        .. note ::
632
633          Interlacing will require the entire image to be in working
634          memory.
635        """
636
637        if self.interlace:
638            fmt = 'BH'[self.bitdepth > 8]
639            a = array(fmt, itertools.chain(*rows))
640            return self.write_array(outfile, a)
641        else:
642            nrows = self.write_passes(outfile, rows)
643            if nrows != self.height:
644                raise ValueError(
645                  "rows supplied (%d) does not match height (%d)" %
646                  (nrows, self.height))
647
648    def write_passes(self, outfile, rows, packed=False):
649        """
650        Write a PNG image to the output file.
651
652        Most users are expected to find the :meth:`write` or
653        :meth:`write_array` method more convenient.
654
655        The rows should be given to this method in the order that
656        they appear in the output file.  For straightlaced images,
657        this is the usual top to bottom ordering, but for interlaced
658        images the rows should have already been interlaced before
659        passing them to this function.
660
661        `rows` should be an iterable that yields each row.  When
662        `packed` is ``False`` the rows should be in boxed row flat pixel
663        format; when `packed` is ``True`` each row should be a packed
664        sequence of bytes.
665        """
666
667        # http://www.w3.org/TR/PNG/#5PNG-file-signature
668        outfile.write(_signature)
669
670        # http://www.w3.org/TR/PNG/#11IHDR
671        write_chunk(outfile, 'IHDR',
672                    struct.pack("!2I5B", self.width, self.height,
673                                self.bitdepth, self.color_type,
674                                0, 0, self.interlace))
675
676        # See :chunk:order
677        # http://www.w3.org/TR/PNG/#11gAMA
678        if self.gamma is not None:
679            write_chunk(outfile, 'gAMA',
680                        struct.pack("!L", int(round(self.gamma*1e5))))
681
682        # See :chunk:order
683        # http://www.w3.org/TR/PNG/#11sBIT
684        if self.rescale:
685            write_chunk(outfile, 'sBIT',
686                struct.pack('%dB' % self.planes,
687                            *[self.rescale[0]]*self.planes))
688
689        # :chunk:order: Without a palette (PLTE chunk), ordering is
690        # relatively relaxed.  With one, gAMA chunk must precede PLTE
691        # chunk which must precede tRNS and bKGD.
692        # See http://www.w3.org/TR/PNG/#5ChunkOrdering
693        if self.palette:
694            p,t = self.make_palette()
695            write_chunk(outfile, 'PLTE', p)
696            if t:
697                # tRNS chunk is optional. Only needed if palette entries
698                # have alpha.
699                write_chunk(outfile, 'tRNS', t)
700
701        # http://www.w3.org/TR/PNG/#11tRNS
702        if self.transparent is not None:
703            if self.greyscale:
704                write_chunk(outfile, 'tRNS',
705                            struct.pack("!1H", *self.transparent))
706            else:
707                write_chunk(outfile, 'tRNS',
708                            struct.pack("!3H", *self.transparent))
709
710        # http://www.w3.org/TR/PNG/#11bKGD
711        if self.background is not None:
712            if self.greyscale:
713                write_chunk(outfile, 'bKGD',
714                            struct.pack("!1H", *self.background))
715            else:
716                write_chunk(outfile, 'bKGD',
717                            struct.pack("!3H", *self.background))
718
719        # http://www.w3.org/TR/PNG/#11IDAT
720        if self.compression is not None:
721            compressor = zlib.compressobj(self.compression)
722        else:
723            compressor = zlib.compressobj()
724
725        # Choose an extend function based on the bitdepth.  The extend
726        # function packs/decomposes the pixel values into bytes and
727        # stuffs them onto the data array.
728        data = array('B')
729        if self.bitdepth == 8 or packed:
730            extend = data.extend
731        elif self.bitdepth == 16:
732            # Decompose into bytes
733            def extend(sl):
734                fmt = '!%dH' % len(sl)
735                data.extend(array('B', struct.pack(fmt, *sl)))
736        else:
737            # Pack into bytes
738            assert self.bitdepth < 8
739            # samples per byte
740            spb = int(8/self.bitdepth)
741            def extend(sl):
742                a = array('B', sl)
743                # Adding padding bytes so we can group into a whole
744                # number of spb-tuples.
745                l = float(len(a))
746                extra = math.ceil(l / float(spb))*spb - l
747                a.extend([0]*int(extra))
748                # Pack into bytes
749                l = group(a, spb)
750                l = [reduce(lambda x,y:
751                                           (x << self.bitdepth) + y, e) for e in l]
752                data.extend(l)
753        if self.rescale:
754            oldextend = extend
755            factor = \
756              float(2**self.rescale[1]-1) / float(2**self.rescale[0]-1)
757            def extend(sl):
758                oldextend([int(round(factor*x)) for x in sl])
759
760        # Build the first row, testing mostly to see if we need to
761        # changed the extend function to cope with NumPy integer types
762        # (they cause our ordinary definition of extend to fail, so we
763        # wrap it).  See
764        # http://code.google.com/p/pypng/issues/detail?id=44
765        enumrows = enumerate(rows)
766        del rows
767
768        # First row's filter type.
769        data.append(0)
770        # :todo: Certain exceptions in the call to ``.next()`` or the
771        # following try would indicate no row data supplied.
772        # Should catch.
773        i,row = next(enumrows)
774        try:
775            # If this fails...
776            extend(row)
777        except:
778            # ... try a version that converts the values to int first.
779            # Not only does this work for the (slightly broken) NumPy
780            # types, there are probably lots of other, unknown, "nearly"
781            # int types it works for.
782            def wrapmapint(f):
783                return lambda sl: f(list(map(int, sl)))
784            extend = wrapmapint(extend)
785            del wrapmapint
786            extend(row)
787
788        for i,row in enumrows:
789            # Add "None" filter type.  Currently, it's essential that
790            # this filter type be used for every scanline as we do not
791            # mark the first row of a reduced pass image; that means we
792            # could accidentally compute the wrong filtered scanline if
793            # we used "up", "average", or "paeth" on such a line.
794            data.append(0)
795            extend(row)
796            if len(data) > self.chunk_limit:
797                compressed = compressor.compress(tostring(data))
798                if len(compressed):
799                    write_chunk(outfile, 'IDAT', compressed)
800                # Because of our very witty definition of ``extend``,
801                # above, we must re-use the same ``data`` object.  Hence
802                # we use ``del`` to empty this one, rather than create a
803                # fresh one (which would be my natural FP instinct).
804                del data[:]
805        if len(data):
806            compressed = compressor.compress(tostring(data))
807        else:
808            compressed = strtobytes('')
809        flushed = compressor.flush()
810        if len(compressed) or len(flushed):
811            write_chunk(outfile, 'IDAT', compressed + flushed)
812        # http://www.w3.org/TR/PNG/#11IEND
813        write_chunk(outfile, 'IEND')
814        return i+1
815
816    def write_array(self, outfile, pixels):
817        """
818        Write an array in flat row flat pixel format as a PNG file on
819        the output file.  See also :meth:`write` method.
820        """
821
822        if self.interlace:
823            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
824        else:
825            self.write_passes(outfile, self.array_scanlines(pixels))
826
827    def write_packed(self, outfile, rows):
828        """
829        Write PNG file to `outfile`.  The pixel data comes from `rows`
830        which should be in boxed row packed format.  Each row should be
831        a sequence of packed bytes.
832
833        Technically, this method does work for interlaced images but it
834        is best avoided.  For interlaced images, the rows should be
835        presented in the order that they appear in the file.
836
837        This method should not be used when the source image bit depth
838        is not one naturally supported by PNG; the bit depth should be
839        1, 2, 4, 8, or 16.
840        """
841
842        if self.rescale:
843            raise Error("write_packed method not suitable for bit depth %d" %
844              self.rescale[0])
845        return self.write_passes(outfile, rows, packed=True)
846
847    def convert_pnm(self, infile, outfile):
848        """
849        Convert a PNM file containing raw pixel data into a PNG file
850        with the parameters set in the writer object.  Works for
851        (binary) PGM, PPM, and PAM formats.
852        """
853
854        if self.interlace:
855            pixels = array('B')
856            pixels.fromfile(infile,
857                            (self.bitdepth/8) * self.color_planes *
858                            self.width * self.height)
859            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
860        else:
861            self.write_passes(outfile, self.file_scanlines(infile))
862
863    def convert_ppm_and_pgm(self, ppmfile, pgmfile, outfile):
864        """
865        Convert a PPM and PGM file containing raw pixel data into a
866        PNG outfile with the parameters set in the writer object.
867        """
868        pixels = array('B')
869        pixels.fromfile(ppmfile,
870                        (self.bitdepth/8) * self.color_planes *
871                        self.width * self.height)
872        apixels = array('B')
873        apixels.fromfile(pgmfile,
874                         (self.bitdepth/8) *
875                         self.width * self.height)
876        pixels = interleave_planes(pixels, apixels,
877                                   (self.bitdepth/8) * self.color_planes,
878                                   (self.bitdepth/8))
879        if self.interlace:
880            self.write_passes(outfile, self.array_scanlines_interlace(pixels))
881        else:
882            self.write_passes(outfile, self.array_scanlines(pixels))
883
884    def file_scanlines(self, infile):
885        """
886        Generates boxed rows in flat pixel format, from the input file
887        `infile`.  It assumes that the input file is in a "Netpbm-like"
888        binary format, and is positioned at the beginning of the first
889        pixel.  The number of pixels to read is taken from the image
890        dimensions (`width`, `height`, `planes`) and the number of bytes
891        per value is implied by the image `bitdepth`.
892        """
893
894        # Values per row
895        vpr = self.width * self.planes
896        row_bytes = vpr
897        if self.bitdepth > 8:
898            assert self.bitdepth == 16
899            row_bytes *= 2
900            fmt = '>%dH' % vpr
901            def line():
902                return array('H', struct.unpack(fmt, infile.read(row_bytes)))
903        else:
904            def line():
905                scanline = array('B', infile.read(row_bytes))
906                return scanline
907        for y in range(self.height):
908            yield line()
909
910    def array_scanlines(self, pixels):
911        """
912        Generates boxed rows (flat pixels) from flat rows (flat pixels)
913        in an array.
914        """
915
916        # Values per row
917        vpr = self.width * self.planes
918        stop = 0
919        for y in range(self.height):
920            start = stop
921            stop = start + vpr
922            yield pixels[start:stop]
923
924    def array_scanlines_interlace(self, pixels):
925        """
926        Generator for interlaced scanlines from an array.  `pixels` is
927        the full source image in flat row flat pixel format.  The
928        generator yields each scanline of the reduced passes in turn, in
929        boxed row flat pixel format.
930        """
931
932        # http://www.w3.org/TR/PNG/#8InterlaceMethods
933        # Array type.
934        fmt = 'BH'[self.bitdepth > 8]
935        # Value per row
936        vpr = self.width * self.planes
937        for xstart, ystart, xstep, ystep in _adam7:
938            if xstart >= self.width:
939                continue
940            # Pixels per row (of reduced image)
941            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
942            # number of values in reduced image row.
943            row_len = ppr*self.planes
944            for y in range(ystart, self.height, ystep):
945                if xstep == 1:
946                    offset = y * vpr
947                    yield pixels[offset:offset+vpr]
948                else:
949                    row = array(fmt)
950                    # There's no easier way to set the length of an array
951                    row.extend(pixels[0:row_len])
952                    offset = y * vpr + xstart * self.planes
953                    end_offset = (y+1) * vpr
954                    skip = self.planes * xstep
955                    for i in range(self.planes):
956                        row[i::self.planes] = \
957                            pixels[offset+i:end_offset:skip]
958                    yield row
959
960def write_chunk(outfile, tag, data=strtobytes('')):
961    """
962    Write a PNG chunk to the output file, including length and
963    checksum.
964    """
965
966    # http://www.w3.org/TR/PNG/#5Chunk-layout
967    outfile.write(struct.pack("!I", len(data)))
968    tag = strtobytes(tag)
969    outfile.write(tag)
970    outfile.write(data)
971    checksum = zlib.crc32(tag)
972    checksum = zlib.crc32(data, checksum)
973    checksum &= 2**32-1
974    outfile.write(struct.pack("!I", checksum))
975
976def write_chunks(out, chunks):
977    """Create a PNG file by writing out the chunks."""
978
979    out.write(_signature)
980    for chunk in chunks:
981        write_chunk(out, *chunk)
982
983def filter_scanline(type, line, fo, prev=None):
984    """Apply a scanline filter to a scanline.  `type` specifies the
985    filter type (0 to 4); `line` specifies the current (unfiltered)
986    scanline as a sequence of bytes; `prev` specifies the previous
987    (unfiltered) scanline as a sequence of bytes. `fo` specifies the
988    filter offset; normally this is size of a pixel in bytes (the number
989    of bytes per sample times the number of channels), but when this is
990    < 1 (for bit depths < 8) then the filter offset is 1.
991    """
992
993    assert 0 <= type < 5
994
995    # The output array.  Which, pathetically, we extend one-byte at a
996    # time (fortunately this is linear).
997    out = array('B', [type])
998
999    def sub():
1000        ai = -fo
1001        for x in line:
1002            if ai >= 0:
1003                x = (x - line[ai]) & 0xff
1004            out.append(x)
1005            ai += 1
1006    def up():
1007        for i,x in enumerate(line):
1008            x = (x - prev[i]) & 0xff
1009            out.append(x)
1010    def average():
1011        ai = -fo
1012        for i,x in enumerate(line):
1013            if ai >= 0:
1014                x = (x - ((line[ai] + prev[i]) >> 1)) & 0xff
1015            else:
1016                x = (x - (prev[i] >> 1)) & 0xff
1017            out.append(x)
1018            ai += 1
1019    def paeth():
1020        # http://www.w3.org/TR/PNG/#9Filter-type-4-Paeth
1021        ai = -fo # also used for ci
1022        for i,x in enumerate(line):
1023            a = 0
1024            b = prev[i]
1025            c = 0
1026
1027            if ai >= 0:
1028                a = line[ai]
1029                c = prev[ai]
1030            p = a + b - c
1031            pa = abs(p - a)
1032            pb = abs(p - b)
1033            pc = abs(p - c)
1034            if pa <= pb and pa <= pc: Pr = a
1035            elif pb <= pc: Pr = b
1036            else: Pr = c
1037
1038            x = (x - Pr) & 0xff
1039            out.append(x)
1040            ai += 1
1041
1042    if not prev:
1043        # We're on the first line.  Some of the filters can be reduced
1044        # to simpler cases which makes handling the line "off the top"
1045        # of the image simpler.  "up" becomes "none"; "paeth" becomes
1046        # "left" (non-trivial, but true). "average" needs to be handled
1047        # specially.
1048        if type == 2: # "up"
1049            type = 0
1050        elif type == 3:
1051            prev = [0]*len(line)
1052        elif type == 4: # "paeth"
1053            type = 1
1054    if type == 0:
1055        out.extend(line)
1056    elif type == 1:
1057        sub()
1058    elif type == 2:
1059        up()
1060    elif type == 3:
1061        average()
1062    else: # type == 4
1063        paeth()
1064    return out
1065
1066
1067def from_array(a, mode=None, info={}):
1068    """Create a PNG :class:`Image` object from a 2- or 3-dimensional
1069    array.  One application of this function is easy PIL-style saving:
1070    ``png.from_array(pixels, 'L').save('foo.png')``.
1071
1072    .. note :
1073
1074      The use of the term *3-dimensional* is for marketing purposes
1075      only.  It doesn't actually work.  Please bear with us.  Meanwhile
1076      enjoy the complimentary snacks (on request) and please use a
1077      2-dimensional array.
1078
1079    Unless they are specified using the *info* parameter, the PNG's
1080    height and width are taken from the array size.  For a 3 dimensional
1081    array the first axis is the height; the second axis is the width;
1082    and the third axis is the channel number.  Thus an RGB image that is
1083    16 pixels high and 8 wide will use an array that is 16x8x3.  For 2
1084    dimensional arrays the first axis is the height, but the second axis
1085    is ``width*channels``, so an RGB image that is 16 pixels high and 8
1086    wide will use a 2-dimensional array that is 16x24 (each row will be
1087    8*3==24 sample values).
1088
1089    *mode* is a string that specifies the image colour format in a
1090    PIL-style mode.  It can be:
1091
1092    ``'L'``
1093      greyscale (1 channel)
1094    ``'LA'``
1095      greyscale with alpha (2 channel)
1096    ``'RGB'``
1097      colour image (3 channel)
1098    ``'RGBA'``
1099      colour image with alpha (4 channel)
1100
1101    The mode string can also specify the bit depth (overriding how this
1102    function normally derives the bit depth, see below).  Appending
1103    ``';16'`` to the mode will cause the PNG to be 16 bits per channel;
1104    any decimal from 1 to 16 can be used to specify the bit depth.
1105
1106    When a 2-dimensional array is used *mode* determines how many
1107    channels the image has, and so allows the width to be derived from
1108    the second array dimension.
1109
1110    The array is expected to be a ``numpy`` array, but it can be any
1111    suitable Python sequence.  For example, a list of lists can be used:
1112    ``png.from_array([[0, 255, 0], [255, 0, 255]], 'L')``.  The exact
1113    rules are: ``len(a)`` gives the first dimension, height;
1114    ``len(a[0])`` gives the second dimension; ``len(a[0][0])`` gives the
1115    third dimension, unless an exception is raised in which case a
1116    2-dimensional array is assumed.  It's slightly more complicated than
1117    that because an iterator of rows can be used, and it all still
1118    works.  Using an iterator allows data to be streamed efficiently.
1119
1120    The bit depth of the PNG is normally taken from the array element's
1121    datatype (but if *mode* specifies a bitdepth then that is used
1122    instead).  The array element's datatype is determined in a way which
1123    is supposed to work both for ``numpy`` arrays and for Python
1124    ``array.array`` objects.  A 1 byte datatype will give a bit depth of
1125    8, a 2 byte datatype will give a bit depth of 16.  If the datatype
1126    does not have an implicit size, for example it is a plain Python
1127    list of lists, as above, then a default of 8 is used.
1128
1129    The *info* parameter is a dictionary that can be used to specify
1130    metadata (in the same style as the arguments to the
1131    :class:``png.Writer`` class).  For this function the keys that are
1132    useful are:
1133
1134    height
1135      overrides the height derived from the array dimensions and allows
1136      *a* to be an iterable.
1137    width
1138      overrides the width derived from the array dimensions.
1139    bitdepth
1140      overrides the bit depth derived from the element datatype (but
1141      must match *mode* if that also specifies a bit depth).
1142
1143    Generally anything specified in the
1144    *info* dictionary will override any implicit choices that this
1145    function would otherwise make, but must match any explicit ones.
1146    For example, if the *info* dictionary has a ``greyscale`` key then
1147    this must be true when mode is ``'L'`` or ``'LA'`` and false when
1148    mode is ``'RGB'`` or ``'RGBA'``.
1149    """
1150
1151    # We abuse the *info* parameter by modifying it.  Take a copy here.
1152    # (Also typechecks *info* to some extent).
1153    info = dict(info)
1154
1155    # Syntax check mode string.
1156    bitdepth = None
1157    try:
1158        # Assign the 'L' or 'RGBA' part to `gotmode`.
1159        if mode.startswith('L'):
1160            gotmode = 'L'
1161            mode = mode[1:]
1162        elif mode.startswith('RGB'):
1163            gotmode = 'RGB'
1164            mode = mode[3:]
1165        else:
1166            raise Error()
1167        if mode.startswith('A'):
1168            gotmode += 'A'
1169            mode = mode[1:]
1170
1171        # Skip any optional ';'
1172        while mode.startswith(';'):
1173            mode = mode[1:]
1174
1175        # Parse optional bitdepth
1176        if mode:
1177            try:
1178                bitdepth = int(mode)
1179            except (TypeError, ValueError):
1180                raise Error()
1181    except Error:
1182        raise Error("mode string should be 'RGB' or 'L;16' or similar.")
1183    mode = gotmode
1184
1185    # Get bitdepth from *mode* if possible.
1186    if bitdepth:
1187        if info.get('bitdepth') and bitdepth != info['bitdepth']:
1188            raise Error("mode bitdepth (%d) should match info bitdepth (%d)." %
1189              (bitdepth, info['bitdepth']))
1190        info['bitdepth'] = bitdepth
1191
1192    # Fill in and/or check entries in *info*.
1193    # Dimensions.
1194    if 'size' in info:
1195        # Check width, height, size all match where used.
1196        for dimension,axis in [('width', 0), ('height', 1)]:
1197            if dimension in info:
1198                if info[dimension] != info['size'][axis]:
1199                    raise Error(
1200                      "info[%r] should match info['size'][%r]." %
1201                      (dimension, axis))
1202        info['width'],info['height'] = info['size']
1203    if 'height' not in info:
1204        try:
1205            l = len(a)
1206        except TypeError:
1207            raise Error(
1208              "len(a) does not work, supply info['height'] instead.")
1209        info['height'] = l
1210    # Colour format.
1211    if 'greyscale' in info:
1212        if bool(info['greyscale']) != ('L' in mode):
1213            raise Error("info['greyscale'] should match mode.")
1214    info['greyscale'] = 'L' in mode
1215    if 'alpha' in info:
1216        if bool(info['alpha']) != ('A' in mode):
1217            raise Error("info['alpha'] should match mode.")
1218    info['alpha'] = 'A' in mode
1219
1220    planes = len(mode)
1221    if 'planes' in info:
1222        if info['planes'] != planes:
1223            raise Error("info['planes'] should match mode.")
1224
1225    # In order to work out whether we the array is 2D or 3D we need its
1226    # first row, which requires that we take a copy of its iterator.
1227    # We may also need the first row to derive width and bitdepth.
1228    a,t = itertools.tee(a)
1229    row = next(t)
1230    del t
1231    try:
1232        row[0][0]
1233        threed = True
1234        testelement = row[0]
1235    except (IndexError, TypeError):
1236        threed = False
1237        testelement = row
1238    if 'width' not in info:
1239        if threed:
1240            width = len(row)
1241        else:
1242            width = len(row) // planes
1243        info['width'] = width
1244
1245    # Not implemented yet
1246    assert not threed
1247
1248    if 'bitdepth' not in info:
1249        try:
1250            dtype = testelement.dtype
1251            # goto the "else:" clause.  Sorry.
1252        except AttributeError:
1253            try:
1254                # Try a Python array.array.
1255                bitdepth = 8 * testelement.itemsize
1256            except AttributeError:
1257                # We can't determine it from the array element's
1258                # datatype, use a default of 8.
1259                bitdepth = 8
1260        else:
1261            # If we got here without exception, we now assume that
1262            # the array is a numpy array.
1263            if dtype.kind == 'b':
1264                bitdepth = 1
1265            else:
1266                bitdepth = 8 * dtype.itemsize
1267        info['bitdepth'] = bitdepth
1268
1269    for thing in 'width height bitdepth greyscale alpha'.split():
1270        assert thing in info
1271    return Image(a, info)
1272
1273# So that refugee's from PIL feel more at home.  Not documented.
1274fromarray = from_array
1275
1276class Image:
1277    """A PNG image.  You can create an :class:`Image` object from
1278    an array of pixels by calling :meth:`png.from_array`.  It can be
1279    saved to disk with the :meth:`save` method.
1280    """
1281
1282    def __init__(self, rows, info):
1283        """
1284        .. note ::
1285
1286          The constructor is not public.  Please do not call it.
1287        """
1288
1289        self.rows = rows
1290        self.info = info
1291
1292    def save(self, file):
1293        """Save the image to *file*.  If *file* looks like an open file
1294        descriptor then it is used, otherwise it is treated as a
1295        filename and a fresh file is opened.
1296
1297        In general, you can only call this method once; after it has
1298        been called the first time and the PNG image has been saved, the
1299        source data will have been streamed, and cannot be streamed
1300        again.
1301        """
1302
1303        w = Writer(**self.info)
1304
1305        try:
1306            file.write
1307            def close(): pass
1308        except AttributeError:
1309            file = open(file, 'wb')
1310            def close(): file.close()
1311
1312        try:
1313            w.write(file, self.rows)
1314        finally:
1315            close()
1316
1317class _readable:
1318    """
1319    A simple file-like interface for strings and arrays.
1320    """
1321
1322    def __init__(self, buf):
1323        self.buf = buf
1324        self.offset = 0
1325
1326    def read(self, n):
1327        r = self.buf[self.offset:self.offset+n]
1328        if isarray(r):
1329            r = r.tostring()
1330        self.offset += n
1331        return r
1332
1333
1334class Reader:
1335    """
1336    PNG decoder in pure Python.
1337    """
1338
1339    def __init__(self, _guess=None, **kw):
1340        """
1341        Create a PNG decoder object.
1342
1343        The constructor expects exactly one keyword argument. If you
1344        supply a positional argument instead, it will guess the input
1345        type. You can choose among the following keyword arguments:
1346
1347        filename
1348          Name of input file (a PNG file).
1349        file
1350          A file-like object (object with a read() method).
1351        bytes
1352          ``array`` or ``string`` with PNG data.
1353
1354        """
1355        if ((_guess is not None and len(kw) != 0) or
1356            (_guess is None and len(kw) != 1)):
1357            raise TypeError("Reader() takes exactly 1 argument")
1358
1359        # Will be the first 8 bytes, later on.  See validate_signature.
1360        self.signature = None
1361        self.transparent = None
1362        # A pair of (len,type) if a chunk has been read but its data and
1363        # checksum have not (in other words the file position is just
1364        # past the 4 bytes that specify the chunk type).  See preamble
1365        # method for how this is used.
1366        self.atchunk = None
1367
1368        if _guess is not None:
1369            if isarray(_guess):
1370                kw["bytes"] = _guess
1371            elif isinstance(_guess, str):
1372                kw["filename"] = _guess
1373            elif hasattr(_guess, 'read'):
1374                kw["file"] = _guess
1375
1376        if "filename" in kw:
1377            self.file = open(kw["filename"], "rb")
1378        elif "file" in kw:
1379            self.file = kw["file"]
1380        elif "bytes" in kw:
1381            self.file = _readable(kw["bytes"])
1382        else:
1383            raise TypeError("expecting filename, file or bytes array")
1384
1385
1386    def chunk(self, seek=None, lenient=False):
1387        """
1388        Read the next PNG chunk from the input file; returns a
1389        (*type*,*data*) tuple.  *type* is the chunk's type as a string
1390        (all PNG chunk types are 4 characters long).  *data* is the
1391        chunk's data content, as a string.
1392
1393        If the optional `seek` argument is
1394        specified then it will keep reading chunks until it either runs
1395        out of file or finds the type specified by the argument.  Note
1396        that in general the order of chunks in PNGs is unspecified, so
1397        using `seek` can cause you to miss chunks.
1398
1399        If the optional `lenient` argument evaluates to True,
1400        checksum failures will raise warnings rather than exceptions.
1401        """
1402
1403        self.validate_signature()
1404
1405        while True:
1406            # http://www.w3.org/TR/PNG/#5Chunk-layout
1407            if not self.atchunk:
1408                self.atchunk = self.chunklentype()
1409            length,type = self.atchunk
1410            self.atchunk = None
1411            data = self.file.read(length)
1412            if len(data) != length:
1413                raise ChunkError('Chunk %s too short for required %i octets.'
1414                  % (type, length))
1415            checksum = self.file.read(4)
1416            if len(checksum) != 4:
1417                raise ValueError('Chunk %s too short for checksum.', tag)
1418            if seek and type != seek:
1419                continue
1420            verify = zlib.crc32(strtobytes(type))
1421            verify = zlib.crc32(data, verify)
1422            # Whether the output from zlib.crc32 is signed or not varies
1423            # according to hideous implementation details, see
1424            # http://bugs.python.org/issue1202 .
1425            # We coerce it to be positive here (in a way which works on
1426            # Python 2.3 and older).
1427            verify &= 2**32 - 1
1428            verify = struct.pack('!I', verify)
1429            if checksum != verify:
1430                (a, ) = struct.unpack('!I', checksum)
1431                (b, ) = struct.unpack('!I', verify)
1432                message = "Checksum error in %s chunk: 0x%08X != 0x%08X." % (type, a, b)
1433                if lenient:
1434                    warnings.warn(message, RuntimeWarning)
1435                else:
1436                    raise ChunkError(message)
1437            return type, data
1438
1439    def chunks(self):
1440        """Return an iterator that will yield each chunk as a
1441        (*chunktype*, *content*) pair.
1442        """
1443
1444        while True:
1445            t,v = self.chunk()
1446            yield t,v
1447            if t == 'IEND':
1448                break
1449
1450    def undo_filter(self, filter_type, scanline, previous):
1451        """Undo the filter for a scanline.  `scanline` is a sequence of
1452        bytes that does not include the initial filter type byte.
1453        `previous` is decoded previous scanline (for straightlaced
1454        images this is the previous pixel row, but for interlaced
1455        images, it is the previous scanline in the reduced image, which
1456        in general is not the previous pixel row in the final image).
1457        When there is no previous scanline (the first row of a
1458        straightlaced image, or the first row in one of the passes in an
1459        interlaced image), then this argument should be ``None``.
1460
1461        The scanline will have the effects of filtering removed, and the
1462        result will be returned as a fresh sequence of bytes.
1463        """
1464
1465        # :todo: Would it be better to update scanline in place?
1466        # Yes, with the Cython extension making the undo_filter fast,
1467        # updating scanline inplace makes the code 3 times faster
1468        # (reading 50 images of 800x800 went from 40s to 16s)
1469        result = scanline
1470
1471        if filter_type == 0:
1472            return result
1473
1474        if filter_type not in (1,2,3,4):
1475            raise FormatError('Invalid PNG Filter Type.'
1476              '  See http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters .')
1477
1478        # Filter unit.  The stride from one pixel to the corresponding
1479        # byte from the previous pixel.  Normally this is the pixel
1480        # size in bytes, but when this is smaller than 1, the previous
1481        # byte is used instead.
1482        fu = max(1, self.psize)
1483
1484        # For the first line of a pass, synthesize a dummy previous
1485        # line.  An alternative approach would be to observe that on the
1486        # first line 'up' is the same as 'null', 'paeth' is the same
1487        # as 'sub', with only 'average' requiring any special case.
1488        if not previous:
1489            previous = array('B', [0]*len(scanline))
1490
1491        def sub():
1492            """Undo sub filter."""
1493
1494            ai = 0
1495            # Loop starts at index fu.  Observe that the initial part
1496            # of the result is already filled in correctly with
1497            # scanline.
1498            for i in range(fu, len(result)):
1499                x = scanline[i]
1500                a = result[ai]
1501                result[i] = (x + a) & 0xff
1502                ai += 1
1503
1504        def up():
1505            """Undo up filter."""
1506
1507            for i in range(len(result)):
1508                x = scanline[i]
1509                b = previous[i]
1510                result[i] = (x + b) & 0xff
1511
1512        def average():
1513            """Undo average filter."""
1514
1515            ai = -fu
1516            for i in range(len(result)):
1517                x = scanline[i]
1518                if ai < 0:
1519                    a = 0
1520                else:
1521                    a = result[ai]
1522                b = previous[i]
1523                result[i] = (x + ((a + b) >> 1)) & 0xff
1524                ai += 1
1525
1526        def paeth():
1527            """Undo Paeth filter."""
1528
1529            # Also used for ci.
1530            ai = -fu
1531            for i in range(len(result)):
1532                x = scanline[i]
1533                if ai < 0:
1534                    a = c = 0
1535                else:
1536                    a = result[ai]
1537                    c = previous[ai]
1538                b = previous[i]
1539                p = a + b - c
1540                pa = abs(p - a)
1541                pb = abs(p - b)
1542                pc = abs(p - c)
1543                if pa <= pb and pa <= pc:
1544                    pr = a
1545                elif pb <= pc:
1546                    pr = b
1547                else:
1548                    pr = c
1549                result[i] = (x + pr) & 0xff
1550                ai += 1
1551
1552        # Call appropriate filter algorithm.  Note that 0 has already
1553        # been dealt with.
1554        (None,
1555         pngfilters.undo_filter_sub,
1556         pngfilters.undo_filter_up,
1557         pngfilters.undo_filter_average,
1558         pngfilters.undo_filter_paeth)[filter_type](fu, scanline, previous, result)
1559        return result
1560
1561    def deinterlace(self, raw):
1562        """
1563        Read raw pixel data, undo filters, deinterlace, and flatten.
1564        Return in flat row flat pixel format.
1565        """
1566
1567        # Values per row (of the target image)
1568        vpr = self.width * self.planes
1569
1570        # Make a result array, and make it big enough.  Interleaving
1571        # writes to the output array randomly (well, not quite), so the
1572        # entire output array must be in memory.
1573        fmt = 'BH'[self.bitdepth > 8]
1574        a = array(fmt, [0]*vpr*self.height)
1575        source_offset = 0
1576
1577        for xstart, ystart, xstep, ystep in _adam7:
1578            if xstart >= self.width:
1579                continue
1580            # The previous (reconstructed) scanline.  None at the
1581            # beginning of a pass to indicate that there is no previous
1582            # line.
1583            recon = None
1584            # Pixels per row (reduced pass image)
1585            ppr = int(math.ceil((self.width-xstart)/float(xstep)))
1586            # Row size in bytes for this pass.
1587            row_size = int(math.ceil(self.psize * ppr))
1588            for y in range(ystart, self.height, ystep):
1589                filter_type = raw[source_offset]
1590                source_offset += 1
1591                scanline = raw[source_offset:source_offset+row_size]
1592                source_offset += row_size
1593                recon = self.undo_filter(filter_type, scanline, recon)
1594                # Convert so that there is one element per pixel value
1595                flat = self.serialtoflat(recon, ppr)
1596                if xstep == 1:
1597                    assert xstart == 0
1598                    offset = y * vpr
1599                    a[offset:offset+vpr] = flat
1600                else:
1601                    offset = y * vpr + xstart * self.planes
1602                    end_offset = (y+1) * vpr
1603                    skip = self.planes * xstep
1604                    for i in range(self.planes):
1605                        a[offset+i:end_offset:skip] = \
1606                            flat[i::self.planes]
1607        return a
1608
1609    def iterboxed(self, rows):
1610        """Iterator that yields each scanline in boxed row flat pixel
1611        format.  `rows` should be an iterator that yields the bytes of
1612        each row in turn.
1613        """
1614
1615        def asvalues(raw):
1616            """Convert a row of raw bytes into a flat row.  Result will
1617            be a freshly allocated object, not shared with
1618            argument.
1619            """
1620
1621            if self.bitdepth == 8:
1622                return array('B', raw)
1623            if self.bitdepth == 16:
1624                raw = tostring(raw)
1625                return array('H', struct.unpack('!%dH' % (len(raw)//2), raw))
1626            assert self.bitdepth < 8
1627            width = self.width
1628            # Samples per byte
1629            spb = 8//self.bitdepth
1630            out = array('B')
1631            mask = 2**self.bitdepth - 1
1632            shifts = list(map(self.bitdepth.__mul__, reversed(list(range(spb)))))
1633            for o in raw:
1634                out.extend([mask&(o>>i) for i in shifts])
1635            return out[:width]
1636
1637        return map(asvalues, rows)
1638
1639    def serialtoflat(self, bytes, width=None):
1640        """Convert serial format (byte stream) pixel data to flat row
1641        flat pixel.
1642        """
1643
1644        if self.bitdepth == 8:
1645            return bytes
1646        if self.bitdepth == 16:
1647            bytes = tostring(bytes)
1648            return array('H',
1649              struct.unpack('!%dH' % (len(bytes)//2), bytes))
1650        assert self.bitdepth < 8
1651        if width is None:
1652            width = self.width
1653        # Samples per byte
1654        spb = 8//self.bitdepth
1655        out = array('B')
1656        mask = 2**self.bitdepth - 1
1657        shifts = list(map(self.bitdepth.__mul__, reversed(list(range(spb)))))
1658        l = width
1659        for o in bytes:
1660            out.extend([(mask&(o>>s)) for s in shifts][:l])
1661            l -= spb
1662            if l <= 0:
1663                l = width
1664        return out
1665
1666    def iterstraight(self, raw):
1667        """Iterator that undoes the effect of filtering, and yields
1668        each row in serialised format (as a sequence of bytes).
1669        Assumes input is straightlaced.  `raw` should be an iterable
1670        that yields the raw bytes in chunks of arbitrary size.
1671        """
1672
1673        # length of row, in bytes
1674        rb = self.row_bytes
1675        a = array('B')
1676        # The previous (reconstructed) scanline.  None indicates first
1677        # line of image.
1678        recon = None
1679        for some in raw:
1680            a.extend(some)
1681            while len(a) >= rb + 1:
1682                filter_type = a[0]
1683                scanline = a[1:rb+1]
1684                del a[:rb+1]
1685                recon = self.undo_filter(filter_type, scanline, recon)
1686                yield recon
1687        if len(a) != 0:
1688            # :file:format We get here with a file format error:
1689            # when the available bytes (after decompressing) do not
1690            # pack into exact rows.
1691            raise FormatError(
1692              'Wrong size for decompressed IDAT chunk.')
1693        assert len(a) == 0
1694
1695    def validate_signature(self):
1696        """If signature (header) has not been read then read and
1697        validate it; otherwise do nothing.
1698        """
1699
1700        if self.signature:
1701            return
1702        self.signature = self.file.read(8)
1703        if self.signature != _signature:
1704            raise FormatError("PNG file has invalid signature.")
1705
1706    def preamble(self, lenient=False):
1707        """
1708        Extract the image metadata by reading the initial part of
1709        the PNG file up to the start of the ``IDAT`` chunk.  All the
1710        chunks that precede the ``IDAT`` chunk are read and either
1711        processed for metadata or discarded.
1712
1713        If the optional `lenient` argument evaluates to True, checksum
1714        failures will raise warnings rather than exceptions.
1715        """
1716
1717        self.validate_signature()
1718
1719        while True:
1720            if not self.atchunk:
1721                self.atchunk = self.chunklentype()
1722                if self.atchunk is None:
1723                    raise FormatError(
1724                      'This PNG file has no IDAT chunks.')
1725            if self.atchunk[1] == 'IDAT':
1726                return
1727            self.process_chunk(lenient=lenient)
1728
1729    def chunklentype(self):
1730        """Reads just enough of the input to determine the next
1731        chunk's length and type, returned as a (*length*, *type*) pair
1732        where *type* is a string.  If there are no more chunks, ``None``
1733        is returned.
1734        """
1735
1736        x = self.file.read(8)
1737        if not x:
1738            return None
1739        if len(x) != 8:
1740            raise FormatError(
1741              'End of file whilst reading chunk length and type.')
1742        length,type = struct.unpack('!I4s', x)
1743        type = bytestostr(type)
1744        if length > 2**31-1:
1745            raise FormatError('Chunk %s is too large: %d.' % (type,length))
1746        return length,type
1747
1748    def process_chunk(self, lenient=False):
1749        """Process the next chunk and its data.  This only processes the
1750        following chunk types, all others are ignored: ``IHDR``,
1751        ``PLTE``, ``bKGD``, ``tRNS``, ``gAMA``, ``sBIT``.
1752
1753        If the optional `lenient` argument evaluates to True,
1754        checksum failures will raise warnings rather than exceptions.
1755        """
1756
1757        type, data = self.chunk(lenient=lenient)
1758        method = '_process_' + type
1759        m = getattr(self, method, None)
1760        if m:
1761            m(data)
1762
1763    def _process_IHDR(self, data):
1764        # http://www.w3.org/TR/PNG/#11IHDR
1765        if len(data) != 13:
1766            raise FormatError('IHDR chunk has incorrect length.')
1767        (self.width, self.height, self.bitdepth, self.color_type,
1768         self.compression, self.filter,
1769         self.interlace) = struct.unpack("!2I5B", data)
1770
1771        check_bitdepth_colortype(self.bitdepth, self.color_type)
1772
1773        if self.compression != 0:
1774            raise Error("unknown compression method %d" % self.compression)
1775        if self.filter != 0:
1776            raise FormatError("Unknown filter method %d,"
1777              " see http://www.w3.org/TR/2003/REC-PNG-20031110/#9Filters ."
1778              % self.filter)
1779        if self.interlace not in (0,1):
1780            raise FormatError("Unknown interlace method %d,"
1781              " see http://www.w3.org/TR/2003/REC-PNG-20031110/#8InterlaceMethods ."
1782              % self.interlace)
1783
1784        # Derived values
1785        # http://www.w3.org/TR/PNG/#6Colour-values
1786        colormap =  bool(self.color_type & 1)
1787        greyscale = not (self.color_type & 2)
1788        alpha = bool(self.color_type & 4)
1789        color_planes = (3,1)[greyscale or colormap]
1790        planes = color_planes + alpha
1791
1792        self.colormap = colormap
1793        self.greyscale = greyscale
1794        self.alpha = alpha
1795        self.color_planes = color_planes
1796        self.planes = planes
1797        self.psize = float(self.bitdepth)/float(8) * planes
1798        if int(self.psize) == self.psize:
1799            self.psize = int(self.psize)
1800        self.row_bytes = int(math.ceil(self.width * self.psize))
1801        # Stores PLTE chunk if present, and is used to check
1802        # chunk ordering constraints.
1803        self.plte = None
1804        # Stores tRNS chunk if present, and is used to check chunk
1805        # ordering constraints.
1806        self.trns = None
1807        # Stores sbit chunk if present.
1808        self.sbit = None
1809
1810    def _process_PLTE(self, data):
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
1823    def _process_bKGD(self, data):
1824        try:
1825            if self.colormap:
1826                if not self.plte:
1827                    warnings.warn(
1828                      "PLTE chunk is required before bKGD chunk.")
1829                self.background = struct.unpack('B', data)
1830            else:
1831                self.background = struct.unpack("!%dH" % self.color_planes,
1832                  data)
1833        except struct.error:
1834            raise FormatError("bKGD chunk has incorrect length.")
1835
1836    def _process_tRNS(self, data):
1837        # http://www.w3.org/TR/PNG/#11tRNS
1838        self.trns = data
1839        if self.colormap:
1840            if not self.plte:
1841                warnings.warn("PLTE chunk is required before tRNS chunk.")
1842            else:
1843                if len(data) > len(self.plte)/3:
1844                    # Was warning, but promoted to Error as it
1845                    # would otherwise cause pain later on.
1846                    raise FormatError("tRNS chunk is too long.")
1847        else:
1848            if self.alpha:
1849                raise FormatError(
1850                  "tRNS chunk is not valid with colour type %d." %
1851                  self.color_type)
1852            try:
1853                self.transparent = \
1854                    struct.unpack("!%dH" % self.color_planes, data)
1855            except struct.error:
1856                raise FormatError("tRNS chunk has incorrect length.")
1857
1858    def _process_gAMA(self, data):
1859        try:
1860            self.gamma = struct.unpack("!L", data)[0] / 100000.0
1861        except struct.error:
1862            raise FormatError("gAMA chunk has incorrect length.")
1863
1864    def _process_sBIT(self, data):
1865        self.sbit = data
1866        if (self.colormap and len(data) != 3 or
1867            not self.colormap and len(data) != self.planes):
1868            raise FormatError("sBIT chunk has incorrect length.")
1869
1870    def read(self, lenient=False):
1871        """
1872        Read the PNG file and decode it.  Returns (`width`, `height`,
1873        `pixels`, `metadata`).
1874
1875        May use excessive memory.
1876
1877        `pixels` are returned in boxed row flat pixel format.
1878
1879        If the optional `lenient` argument evaluates to True,
1880        checksum failures will raise warnings rather than exceptions.
1881        """
1882
1883        def iteridat():
1884            """Iterator that yields all the ``IDAT`` chunks as strings."""
1885            while True:
1886                try:
1887                    type, data = self.chunk(lenient=lenient)
1888                except ValueError as e:
1889                    raise ChunkError(e.args[0])
1890                if type == 'IEND':
1891                    # http://www.w3.org/TR/PNG/#11IEND
1892                    break
1893                if type != 'IDAT':
1894                    continue
1895                # type == 'IDAT'
1896                # http://www.w3.org/TR/PNG/#11IDAT
1897                if self.colormap and not self.plte:
1898                    warnings.warn("PLTE chunk is required before IDAT chunk")
1899                yield data
1900
1901        def iterdecomp(idat):
1902            """Iterator that yields decompressed strings.  `idat` should
1903            be an iterator that yields the ``IDAT`` chunk data.
1904            """
1905
1906            # Currently, with no max_length paramter to decompress, this
1907            # routine will do one yield per IDAT chunk.  So not very
1908            # incremental.
1909            d = zlib.decompressobj()
1910            # Each IDAT chunk is passed to the decompressor, then any
1911            # remaining state is decompressed out.
1912            for data in idat:
1913                # :todo: add a max_length argument here to limit output
1914                # size.
1915                yield array('B', d.decompress(data))
1916            yield array('B', d.flush())
1917
1918        self.preamble(lenient=lenient)
1919        raw = iterdecomp(iteridat())
1920
1921        if self.interlace:
1922            raw = array('B', itertools.chain(*raw))
1923            arraycode = 'BH'[self.bitdepth>8]
1924            # Like :meth:`group` but producing an array.array object for
1925            # each row.
1926            pixels = map(lambda *row: array(arraycode, row),
1927                       *[iter(self.deinterlace(raw))]*self.width*self.planes)
1928        else:
1929            pixels = self.iterboxed(self.iterstraight(raw))
1930        meta = dict()
1931        for attr in 'greyscale alpha planes bitdepth interlace'.split():
1932            meta[attr] = getattr(self, attr)
1933        meta['size'] = (self.width, self.height)
1934        for attr in 'gamma transparent background'.split():
1935            a = getattr(self, attr, None)
1936            if a is not None:
1937                meta[attr] = a
1938        if self.plte:
1939            meta['palette'] = self.palette()
1940        return self.width, self.height, pixels, meta
1941
1942
1943    def read_flat(self):
1944        """
1945        Read a PNG file and decode it into flat row flat pixel format.
1946        Returns (*width*, *height*, *pixels*, *metadata*).
1947
1948        May use excessive memory.
1949
1950        `pixels` are returned in flat row flat pixel format.
1951
1952        See also the :meth:`read` method which returns pixels in the
1953        more stream-friendly boxed row flat pixel format.
1954        """
1955
1956        x, y, pixel, meta = self.read()
1957        arraycode = 'BH'[meta['bitdepth']>8]
1958        pixel = array(arraycode, itertools.chain(*pixel))
1959        return x, y, pixel, meta
1960
1961    def palette(self, alpha='natural'):
1962        """Returns a palette that is a sequence of 3-tuples or 4-tuples,
1963        synthesizing it from the ``PLTE`` and ``tRNS`` chunks.  These
1964        chunks should have already been processed (for example, by
1965        calling the :meth:`preamble` method).  All the tuples are the
1966        same size: 3-tuples if there is no ``tRNS`` chunk, 4-tuples when
1967        there is a ``tRNS`` chunk.  Assumes that the image is colour type
1968        3 and therefore a ``PLTE`` chunk is required.
1969
1970        If the `alpha` argument is ``'force'`` then an alpha channel is
1971        always added, forcing the result to be a sequence of 4-tuples.
1972        """
1973
1974        if not self.plte:
1975            raise FormatError(
1976                "Required PLTE chunk is missing in colour type 3 image.")
1977        plte = group(array('B', self.plte), 3)
1978        if self.trns or alpha == 'force':
1979            trns = array('B', self.trns or '')
1980            trns.extend([255]*(len(plte)-len(trns)))
1981            plte = list(map(operator.add, plte, group(trns, 1)))
1982        return plte
1983
1984    def asDirect(self):
1985        """Returns the image data as a direct representation of an
1986        ``x * y * planes`` array.  This method is intended to remove the
1987        need for callers to deal with palettes and transparency
1988        themselves.  Images with a palette (colour type 3)
1989        are converted to RGB or RGBA; images with transparency (a
1990        ``tRNS`` chunk) are converted to LA or RGBA as appropriate.
1991        When returned in this format the pixel values represent the
1992        colour value directly without needing to refer to palettes or
1993        transparency information.
1994
1995        Like the :meth:`read` method this method returns a 4-tuple:
1996
1997        (*width*, *height*, *pixels*, *meta*)
1998
1999        This method normally returns pixel values with the bit depth
2000        they have in the source image, but when the source PNG has an
2001        ``sBIT`` chunk it is inspected and can reduce the bit depth of
2002        the result pixels; pixel values will be reduced according to
2003        the bit depth specified in the ``sBIT`` chunk (PNG nerds should
2004        note a single result bit depth is used for all channels; the
2005        maximum of the ones specified in the ``sBIT`` chunk.  An RGB565
2006        image will be rescaled to 6-bit RGB666).
2007
2008        The *meta* dictionary that is returned reflects the `direct`
2009        format and not the original source image.  For example, an RGB
2010        source image with a ``tRNS`` chunk to represent a transparent
2011        colour, will have ``planes=3`` and ``alpha=False`` for the
2012        source image, but the *meta* dictionary returned by this method
2013        will have ``planes=4`` and ``alpha=True`` because an alpha
2014        channel is synthesized and added.
2015
2016        *pixels* is the pixel data in boxed row flat pixel format (just
2017        like the :meth:`read` method).
2018
2019        All the other aspects of the image data are not changed.
2020        """
2021
2022        self.preamble()
2023
2024        # Simple case, no conversion necessary.
2025        if not self.colormap and not self.trns and not self.sbit:
2026            return self.read()
2027
2028        x,y,pixels,meta = self.read()
2029
2030        if self.colormap:
2031            meta['colormap'] = False
2032            meta['alpha'] = bool(self.trns)
2033            meta['bitdepth'] = 8
2034            meta['planes'] = 3 + bool(self.trns)
2035            plte = self.palette()
2036            def iterpal(pixels):
2037                for row in pixels:
2038                    row = list(map(plte.__getitem__, row))
2039                    yield array('B', itertools.chain(*row))
2040            pixels = iterpal(pixels)
2041        elif self.trns:
2042            # It would be nice if there was some reasonable way
2043            # of doing this without generating a whole load of
2044            # intermediate tuples.  But tuples does seem like the
2045            # easiest way, with no other way clearly much simpler or
2046            # much faster.  (Actually, the L to LA conversion could
2047            # perhaps go faster (all those 1-tuples!), but I still
2048            # wonder whether the code proliferation is worth it)
2049            it = self.transparent
2050            maxval = 2**meta['bitdepth']-1
2051            planes = meta['planes']
2052            meta['alpha'] = True
2053            meta['planes'] += 1
2054            typecode = 'BH'[meta['bitdepth']>8]
2055            def itertrns(pixels):
2056                for row in pixels:
2057                    # For each row we group it into pixels, then form a
2058                    # characterisation vector that says whether each
2059                    # pixel is opaque or not.  Then we convert
2060                    # True/False to 0/maxval (by multiplication),
2061                    # and add it as the extra channel.
2062                    row = group(row, planes)
2063                    opa = list(map(it.__ne__, row))
2064                    opa = list(map(maxval.__mul__, opa))
2065                    opa = list(zip(opa)) # convert to 1-tuples
2066                    yield array(typecode,
2067                      itertools.chain(*list(map(operator.add, row, opa))))
2068            pixels = itertrns(pixels)
2069        targetbitdepth = None
2070        if self.sbit:
2071            sbit = struct.unpack('%dB' % len(self.sbit), self.sbit)
2072            targetbitdepth = max(sbit)
2073            if targetbitdepth > meta['bitdepth']:
2074                raise Error('sBIT chunk %r exceeds bitdepth %d' %
2075                    (sbit,self.bitdepth))
2076            if min(sbit) <= 0:
2077                raise Error('sBIT chunk %r has a 0-entry' % sbit)
2078            if targetbitdepth == meta['bitdepth']:
2079                targetbitdepth = None
2080        if targetbitdepth:
2081            shift = meta['bitdepth'] - targetbitdepth
2082            meta['bitdepth'] = targetbitdepth
2083            def itershift(pixels):
2084                for row in pixels:
2085                    yield list(map(shift.__rrshift__, row))
2086            pixels = itershift(pixels)
2087        return x,y,pixels,meta
2088
2089    def asFloat(self, maxval=1.0):
2090        """Return image pixels as per :meth:`asDirect` method, but scale
2091        all pixel values to be floating point values between 0.0 and
2092        *maxval*.
2093        """
2094
2095        x,y,pixels,info = self.asDirect()
2096        sourcemaxval = 2**info['bitdepth']-1
2097        del info['bitdepth']
2098        info['maxval'] = float(maxval)
2099        factor = float(maxval)/float(sourcemaxval)
2100        def iterfloat():
2101            for row in pixels:
2102                yield list(map(factor.__mul__, row))
2103        return x,y,iterfloat(),info
2104
2105    def _as_rescale(self, get, targetbitdepth):
2106        """Helper used by :meth:`asRGB8` and :meth:`asRGBA8`."""
2107
2108        width,height,pixels,meta = get()
2109        maxval = 2**meta['bitdepth'] - 1
2110        targetmaxval = 2**targetbitdepth - 1
2111        factor = float(targetmaxval) / float(maxval)
2112        meta['bitdepth'] = targetbitdepth
2113        def iterscale():
2114            for row in pixels:
2115                yield [int(round(x*factor)) for x in row]
2116        if maxval == targetmaxval:
2117            return width, height, pixels, meta
2118        else:
2119            return width, height, iterscale(), meta
2120
2121    def asRGB8(self):
2122        """Return the image data as an RGB pixels with 8-bits per
2123        sample.  This is like the :meth:`asRGB` method except that
2124        this method additionally rescales the values so that they
2125        are all between 0 and 255 (8-bit).  In the case where the
2126        source image has a bit depth < 8 the transformation preserves
2127        all the information; where the source image has bit depth
2128        > 8, then rescaling to 8-bit values loses precision.  No
2129        dithering is performed.  Like :meth:`asRGB`, an alpha channel
2130        in the source image will raise an exception.
2131
2132        This function returns a 4-tuple:
2133        (*width*, *height*, *pixels*, *metadata*).
2134        *width*, *height*, *metadata* are as per the
2135        :meth:`read` method.
2136
2137        *pixels* is the pixel data in boxed row flat pixel format.
2138        """
2139
2140        return self._as_rescale(self.asRGB, 8)
2141
2142    def asRGBA8(self):
2143        """Return the image data as RGBA pixels with 8-bits per
2144        sample.  This method is similar to :meth:`asRGB8` and
2145        :meth:`asRGBA`:  The result pixels have an alpha channel, *and*
2146        values are rescaled to the range 0 to 255.  The alpha channel is
2147        synthesized if necessary (with a small speed penalty).
2148        """
2149
2150        return self._as_rescale(self.asRGBA, 8)
2151
2152    def asRGB(self):
2153        """Return image as RGB pixels.  RGB colour images are passed
2154        through unchanged; greyscales are expanded into RGB
2155        triplets (there is a small speed overhead for doing this).
2156
2157        An alpha channel in the source image will raise an
2158        exception.
2159
2160        The return values are as for the :meth:`read` method
2161        except that the *metadata* reflect the returned pixels, not the
2162        source image.  In particular, for this method
2163        ``metadata['greyscale']`` will be ``False``.
2164        """
2165
2166        width,height,pixels,meta = self.asDirect()
2167        if meta['alpha']:
2168            raise Error("will not convert image with alpha channel to RGB")
2169        if not meta['greyscale']:
2170            return width,height,pixels,meta
2171        meta['greyscale'] = False
2172        typecode = 'BH'[meta['bitdepth'] > 8]
2173        def iterrgb():
2174            for row in pixels:
2175                a = array(typecode, [0]) * 3 * width
2176                for i in range(3):
2177                    a[i::3] = row
2178                yield a
2179        return width,height,iterrgb(),meta
2180
2181    def asRGBA(self):
2182        """Return image as RGBA pixels.  Greyscales are expanded into
2183        RGB triplets; an alpha channel is synthesized if necessary.
2184        The return values are as for the :meth:`read` method
2185        except that the *metadata* reflect the returned pixels, not the
2186        source image.  In particular, for this method
2187        ``metadata['greyscale']`` will be ``False``, and
2188        ``metadata['alpha']`` will be ``True``.
2189        """
2190
2191        width,height,pixels,meta = self.asDirect()
2192        if meta['alpha'] and not meta['greyscale']:
2193            return width,height,pixels,meta
2194        typecode = 'BH'[meta['bitdepth'] > 8]
2195        maxval = 2**meta['bitdepth'] - 1
2196        maxbuffer = struct.pack('=' + typecode, maxval) * 4 * width
2197        def newarray():
2198            return array(typecode, maxbuffer)
2199
2200        if meta['alpha'] and meta['greyscale']:
2201            # LA to RGBA
2202            def convert():
2203                for row in pixels:
2204                    # Create a fresh target row, then copy L channel
2205                    # into first three target channels, and A channel
2206                    # into fourth channel.
2207                    a = newarray()
2208                    pngfilters.convert_la_to_rgba(row, a)
2209                    yield a
2210        elif meta['greyscale']:
2211            # L to RGBA
2212            def convert():
2213                for row in pixels:
2214                    a = newarray()
2215                    pngfilters.convert_l_to_rgba(row, a)
2216                    yield a
2217        else:
2218            assert not meta['alpha'] and not meta['greyscale']
2219            # RGB to RGBA
2220            def convert():
2221                for row in pixels:
2222                    a = newarray()
2223                    pngfilters.convert_rgb_to_rgba(row, a)
2224                    yield a
2225        meta['alpha'] = True
2226        meta['greyscale'] = False
2227        return width,height,convert(),meta
2228
2229def check_bitdepth_colortype(bitdepth, colortype):
2230    """Check that `bitdepth` and `colortype` are both valid,
2231    and specified in a valid combination. Returns if valid,
2232    raise an Exception if not valid.
2233    """
2234
2235    if bitdepth not in (1,2,4,8,16):
2236        raise FormatError("invalid bit depth %d" % bitdepth)
2237    if colortype not in (0,2,3,4,6):
2238        raise FormatError("invalid colour type %d" % colortype)
2239    # Check indexed (palettized) images have 8 or fewer bits
2240    # per pixel; check only indexed or greyscale images have
2241    # fewer than 8 bits per pixel.
2242    if colortype & 1 and bitdepth > 8:
2243        raise FormatError(
2244          "Indexed images (colour type %d) cannot"
2245          " have bitdepth > 8 (bit depth %d)."
2246          " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
2247          % (bitdepth, colortype))
2248    if bitdepth < 8 and colortype not in (0,3):
2249        raise FormatError("Illegal combination of bit depth (%d)"
2250          " and colour type (%d)."
2251          " See http://www.w3.org/TR/2003/REC-PNG-20031110/#table111 ."
2252          % (bitdepth, colortype))
2253
2254def isinteger(x):
2255    try:
2256        return int(x) == x
2257    except (TypeError, ValueError):
2258        return False
2259
2260
2261# === Legacy Version Support ===
2262
2263# :pyver:old:  PyPNG works on Python versions 2.3 and 2.2, but not
2264# without some awkward problems.  Really PyPNG works on Python 2.4 (and
2265# above); it works on Pythons 2.3 and 2.2 by virtue of fixing up
2266# problems here.  It's a bit ugly (which is why it's hidden down here).
2267#
2268# Generally the strategy is one of pretending that we're running on
2269# Python 2.4 (or above), and patching up the library support on earlier
2270# versions so that it looks enough like Python 2.4.  When it comes to
2271# Python 2.2 there is one thing we cannot patch: extended slices
2272# http://www.python.org/doc/2.3/whatsnew/section-slices.html.
2273# Instead we simply declare that features that are implemented using
2274# extended slices will not work on Python 2.2.
2275#
2276# In order to work on Python 2.3 we fix up a recurring annoyance involving
2277# the array type.  In Python 2.3 an array cannot be initialised with an
2278# array, and it cannot be extended with a list (or other sequence).
2279# Both of those are repeated issues in the code.  Whilst I would not
2280# normally tolerate this sort of behaviour, here we "shim" a replacement
2281# for array into place (and hope no-one notices).  You never read this.
2282#
2283# In an amusing case of warty hacks on top of warty hacks... the array
2284# shimming we try and do only works on Python 2.3 and above (you can't
2285# subclass array.array in Python 2.2).  So to get it working on Python
2286# 2.2 we go for something much simpler and (probably) way slower.
2287try:
2288    array('B').extend([])
2289    array('B', array('B'))
2290# :todo:(drj) Check that TypeError is correct for Python 2.3
2291except TypeError:
2292    # Expect to get here on Python 2.3
2293    try:
2294        class _array_shim(array):
2295            true_array = array
2296            def __new__(cls, typecode, init=None):
2297                super_new = super(_array_shim, cls).__new__
2298                it = super_new(cls, typecode)
2299                if init is None:
2300                    return it
2301                it.extend(init)
2302                return it
2303            def extend(self, extension):
2304                super_extend = super(_array_shim, self).extend
2305                if isinstance(extension, self.true_array):
2306                    return super_extend(extension)
2307                if not isinstance(extension, (list, str)):
2308                    # Convert to list.  Allows iterators to work.
2309                    extension = list(extension)
2310                return super_extend(self.true_array(self.typecode, extension))
2311        array = _array_shim
2312    except TypeError:
2313        # Expect to get here on Python 2.2
2314        def array(typecode, init=()):
2315            if type(init) == str:
2316                return list(map(ord, init))
2317            return list(init)
2318
2319# Further hacks to get it limping along on Python 2.2
2320try:
2321    enumerate
2322except NameError:
2323    def enumerate(seq):
2324        i=0
2325        for x in seq:
2326            yield i,x
2327            i += 1
2328
2329try:
2330    reversed
2331except NameError:
2332    def reversed(l):
2333        l = list(l)
2334        l.reverse()
2335        for x in l:
2336            yield x
2337
2338try:
2339    itertools
2340except NameError:
2341    class _dummy_itertools:
2342        pass
2343    itertools = _dummy_itertools()
2344    def _itertools_imap(f, seq):
2345        for x in seq:
2346            yield f(x)
2347    itertools.imap = _itertools_imap
2348    def _itertools_chain(*iterables):
2349        for it in iterables:
2350            for element in it:
2351                yield element
2352    itertools.chain = _itertools_chain
2353
2354
2355# === Support for users without Cython ===
2356
2357try:
2358    pngfilters
2359except NameError:
2360    class pngfilters(object):
2361        def undo_filter_sub(filter_unit, scanline, previous, result):
2362            """Undo sub filter."""
2363
2364            ai = 0
2365            # Loops starts at index fu.  Observe that the initial part
2366            # of the result is already filled in correctly with
2367            # scanline.
2368            for i in range(filter_unit, len(result)):
2369                x = scanline[i]
2370                a = result[ai]
2371                result[i] = (x + a) & 0xff
2372                ai += 1
2373        undo_filter_sub = staticmethod(undo_filter_sub)
2374
2375        def undo_filter_up(filter_unit, scanline, previous, result):
2376            """Undo up filter."""
2377
2378            for i in range(len(result)):
2379                x = scanline[i]
2380                b = previous[i]
2381                result[i] = (x + b) & 0xff
2382        undo_filter_up = staticmethod(undo_filter_up)
2383
2384        def undo_filter_average(filter_unit, scanline, previous, result):
2385            """Undo up filter."""
2386
2387            ai = -filter_unit
2388            for i in range(len(result)):
2389                x = scanline[i]
2390                if ai < 0:
2391                    a = 0
2392                else:
2393                    a = result[ai]
2394                b = previous[i]
2395                result[i] = (x + ((a + b) >> 1)) & 0xff
2396                ai += 1
2397        undo_filter_average = staticmethod(undo_filter_average)
2398
2399        def undo_filter_paeth(filter_unit, scanline, previous, result):
2400            """Undo Paeth filter."""
2401
2402            # Also used for ci.
2403            ai = -filter_unit
2404            for i in range(len(result)):
2405                x = scanline[i]
2406                if ai < 0:
2407                    a = c = 0
2408                else:
2409                    a = result[ai]
2410                    c = previous[ai]
2411                b = previous[i]
2412                p = a + b - c
2413                pa = abs(p - a)
2414                pb = abs(p - b)
2415                pc = abs(p - c)
2416                if pa <= pb and pa <= pc:
2417                    pr = a
2418                elif pb <= pc:
2419                    pr = b
2420                else:
2421                    pr = c
2422                result[i] = (x + pr) & 0xff
2423                ai += 1
2424        undo_filter_paeth = staticmethod(undo_filter_paeth)
2425
2426        def convert_la_to_rgba(row, result):
2427            for i in range(3):
2428                result[i::4] = row[0::2]
2429            result[3::4] = row[1::2]
2430        convert_la_to_rgba = staticmethod(convert_la_to_rgba)
2431
2432        def convert_l_to_rgba(row, result):
2433            """Convert a grayscale image to RGBA. This method assumes
2434            the alpha channel in result is already correctly
2435            initialized.
2436            """
2437            for i in range(3):
2438                result[i::4] = row
2439        convert_l_to_rgba = staticmethod(convert_l_to_rgba)
2440
2441        def convert_rgb_to_rgba(row, result):
2442            """Convert an RGB image to RGBA. This method assumes the
2443            alpha channel in result is already correctly initialized.
2444            """
2445            for i in range(3):
2446                result[i::4] = row[i::3]
2447        convert_rgb_to_rgba = staticmethod(convert_rgb_to_rgba)
2448
2449
2450# === Command Line Support ===
2451
2452def read_pam_header(infile):
2453    """
2454    Read (the rest of a) PAM header.  `infile` should be positioned
2455    immediately after the initial 'P7' line (at the beginning of the
2456    second line).  Returns are as for `read_pnm_header`.
2457    """
2458
2459    # Unlike PBM, PGM, and PPM, we can read the header a line at a time.
2460    header = dict()
2461    while True:
2462        l = infile.readline().strip()
2463        if l == strtobytes('ENDHDR'):
2464            break
2465        if not l:
2466            raise EOFError('PAM ended prematurely')
2467        if l[0] == strtobytes('#'):
2468            continue
2469        l = l.split(None, 1)
2470        if l[0] not in header:
2471            header[l[0]] = l[1]
2472        else:
2473            header[l[0]] += strtobytes(' ') + l[1]
2474
2475    required = ['WIDTH', 'HEIGHT', 'DEPTH', 'MAXVAL']
2476    required = [strtobytes(x) for x in required]
2477    WIDTH,HEIGHT,DEPTH,MAXVAL = required
2478    present = [x for x in required if x in header]
2479    if len(present) != len(required):
2480        raise Error('PAM file must specify WIDTH, HEIGHT, DEPTH, and MAXVAL')
2481    width = int(header[WIDTH])
2482    height = int(header[HEIGHT])
2483    depth = int(header[DEPTH])
2484    maxval = int(header[MAXVAL])
2485    if (width <= 0 or
2486        height <= 0 or
2487        depth <= 0 or
2488        maxval <= 0):
2489        raise Error(
2490          'WIDTH, HEIGHT, DEPTH, MAXVAL must all be positive integers')
2491    return 'P7', width, height, depth, maxval
2492
2493def read_pnm_header(infile, supported=('P5','P6')):
2494    """
2495    Read a PNM header, returning (format,width,height,depth,maxval).
2496    `width` and `height` are in pixels.  `depth` is the number of
2497    channels in the image; for PBM and PGM it is synthesized as 1, for
2498    PPM as 3; for PAM images it is read from the header.  `maxval` is
2499    synthesized (as 1) for PBM images.
2500    """
2501
2502    # Generally, see http://netpbm.sourceforge.net/doc/ppm.html
2503    # and http://netpbm.sourceforge.net/doc/pam.html
2504
2505    supported = [strtobytes(x) for x in supported]
2506
2507    # Technically 'P7' must be followed by a newline, so by using
2508    # rstrip() we are being liberal in what we accept.  I think this
2509    # is acceptable.
2510    type = infile.read(3).rstrip()
2511    if type not in supported:
2512        raise NotImplementedError('file format %s not supported' % type)
2513    if type == strtobytes('P7'):
2514        # PAM header parsing is completely different.
2515        return read_pam_header(infile)
2516    # Expected number of tokens in header (3 for P4, 4 for P6)
2517    expected = 4
2518    pbm = ('P1', 'P4')
2519    if type in pbm:
2520        expected = 3
2521    header = [type]
2522
2523    # We have to read the rest of the header byte by byte because the
2524    # final whitespace character (immediately following the MAXVAL in
2525    # the case of P6) may not be a newline.  Of course all PNM files in
2526    # the wild use a newline at this point, so it's tempting to use
2527    # readline; but it would be wrong.
2528    def getc():
2529        c = infile.read(1)
2530        if not c:
2531            raise Error('premature EOF reading PNM header')
2532        return c
2533
2534    c = getc()
2535    while True:
2536        # Skip whitespace that precedes a token.
2537        while c.isspace():
2538            c = getc()
2539        # Skip comments.
2540        while c == '#':
2541            while c not in '\n\r':
2542                c = getc()
2543        if not c.isdigit():
2544            raise Error('unexpected character %s found in header' % c)
2545        # According to the specification it is legal to have comments
2546        # that appear in the middle of a token.
2547        # This is bonkers; I've never seen it; and it's a bit awkward to
2548        # code good lexers in Python (no goto).  So we break on such
2549        # cases.
2550        token = strtobytes('')
2551        while c.isdigit():
2552            token += c
2553            c = getc()
2554        # Slight hack.  All "tokens" are decimal integers, so convert
2555        # them here.
2556        header.append(int(token))
2557        if len(header) == expected:
2558            break
2559    # Skip comments (again)
2560    while c == '#':
2561        while c not in '\n\r':
2562            c = getc()
2563    if not c.isspace():
2564        raise Error('expected header to end with whitespace, not %s' % c)
2565
2566    if type in pbm:
2567        # synthesize a MAXVAL
2568        header.append(1)
2569    depth = (1,3)[type == strtobytes('P6')]
2570    return header[0], header[1], header[2], depth, header[3]
2571
2572def write_pnm(file, width, height, pixels, meta):
2573    """Write a Netpbm PNM/PAM file.
2574    """
2575
2576    bitdepth = meta['bitdepth']
2577    maxval = 2**bitdepth - 1
2578    # Rudely, the number of image planes can be used to determine
2579    # whether we are L (PGM), LA (PAM), RGB (PPM), or RGBA (PAM).
2580    planes = meta['planes']
2581    # Can be an assert as long as we assume that pixels and meta came
2582    # from a PNG file.
2583    assert planes in (1,2,3,4)
2584    if planes in (1,3):
2585        if 1 == planes:
2586            # PGM
2587            # Could generate PBM if maxval is 1, but we don't (for one
2588            # thing, we'd have to convert the data, not just blat it
2589            # out).
2590            fmt = 'P5'
2591        else:
2592            # PPM
2593            fmt = 'P6'
2594        file.write('%s %d %d %d\n' % (fmt, width, height, maxval))
2595    if planes in (2,4):
2596        # PAM
2597        # See http://netpbm.sourceforge.net/doc/pam.html
2598        if 2 == planes:
2599            tupltype = 'GRAYSCALE_ALPHA'
2600        else:
2601            tupltype = 'RGB_ALPHA'
2602        file.write('P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\n'
2603                   'TUPLTYPE %s\nENDHDR\n' %
2604                   (width, height, planes, maxval, tupltype))
2605    # Values per row
2606    vpr = planes * width
2607    # struct format
2608    fmt = '>%d' % vpr
2609    if maxval > 0xff:
2610        fmt = fmt + 'H'
2611    else:
2612        fmt = fmt + 'B'
2613    for row in pixels:
2614        file.write(struct.pack(fmt, *row))
2615    file.flush()
2616
2617def color_triple(color):
2618    """
2619    Convert a command line colour value to a RGB triple of integers.
2620    FIXME: Somewhere we need support for greyscale backgrounds etc.
2621    """
2622    if color.startswith('#') and len(color) == 4:
2623        return (int(color[1], 16),
2624                int(color[2], 16),
2625                int(color[3], 16))
2626    if color.startswith('#') and len(color) == 7:
2627        return (int(color[1:3], 16),
2628                int(color[3:5], 16),
2629                int(color[5:7], 16))
2630    elif color.startswith('#') and len(color) == 13:
2631        return (int(color[1:5], 16),
2632                int(color[5:9], 16),
2633                int(color[9:13], 16))
2634
2635def _add_common_options(parser):
2636    """Call *parser.add_option* for each of the options that are
2637    common between this PNG--PNM conversion tool and the gen
2638    tool.
2639    """
2640    parser.add_option("-i", "--interlace",
2641                      default=False, action="store_true",
2642                      help="create an interlaced PNG file (Adam7)")
2643    parser.add_option("-t", "--transparent",
2644                      action="store", type="string", metavar="#RRGGBB",
2645                      help="mark the specified colour as transparent")
2646    parser.add_option("-b", "--background",
2647                      action="store", type="string", metavar="#RRGGBB",
2648                      help="save the specified background colour")
2649    parser.add_option("-g", "--gamma",
2650                      action="store", type="float", metavar="value",
2651                      help="save the specified gamma value")
2652    parser.add_option("-c", "--compression",
2653                      action="store", type="int", metavar="level",
2654                      help="zlib compression level (0-9)")
2655    return parser
2656
2657def _main(argv):
2658    """
2659    Run the PNG encoder with options from the command line.
2660    """
2661
2662    # Parse command line arguments
2663    from optparse import OptionParser
2664    import re
2665    version = '%prog ' + __version__
2666    parser = OptionParser(version=version)
2667    parser.set_usage("%prog [options] [imagefile]")
2668    parser.add_option('-r', '--read-png', default=False,
2669                      action='store_true',
2670                      help='Read PNG, write PNM')
2671    parser.add_option("-a", "--alpha",
2672                      action="store", type="string", metavar="pgmfile",
2673                      help="alpha channel transparency (RGBA)")
2674    _add_common_options(parser)
2675
2676    (options, args) = parser.parse_args(args=argv[1:])
2677
2678    # Convert options
2679    if options.transparent is not None:
2680        options.transparent = color_triple(options.transparent)
2681    if options.background is not None:
2682        options.background = color_triple(options.background)
2683
2684    # Prepare input and output files
2685    if len(args) == 0:
2686        infilename = '-'
2687        infile = sys.stdin
2688    elif len(args) == 1:
2689        infilename = args[0]
2690        infile = open(infilename, 'rb')
2691    else:
2692        parser.error("more than one input file")
2693    outfile = sys.stdout
2694    if sys.platform == "win32":
2695        import msvcrt, os
2696        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
2697
2698    if options.read_png:
2699        # Encode PNG to PPM
2700        png = Reader(file=infile)
2701        width,height,pixels,meta = png.asDirect()
2702        write_pnm(outfile, width, height, pixels, meta)
2703    else:
2704        # Encode PNM to PNG
2705        format, width, height, depth, maxval = \
2706          read_pnm_header(infile, ('P5','P6','P7'))
2707        # When it comes to the variety of input formats, we do something
2708        # rather rude.  Observe that L, LA, RGB, RGBA are the 4 colour
2709        # types supported by PNG and that they correspond to 1, 2, 3, 4
2710        # channels respectively.  So we use the number of channels in
2711        # the source image to determine which one we have.  We do not
2712        # care about TUPLTYPE.
2713        greyscale = depth <= 2
2714        pamalpha = depth in (2,4)
2715        supported = [2**x-1 for x in range(1,17)]
2716        try:
2717            mi = supported.index(maxval)
2718        except ValueError:
2719            raise NotImplementedError(
2720              'your maxval (%s) not in supported list %s' %
2721              (maxval, str(supported)))
2722        bitdepth = mi+1
2723        writer = Writer(width, height,
2724                        greyscale=greyscale,
2725                        bitdepth=bitdepth,
2726                        interlace=options.interlace,
2727                        transparent=options.transparent,
2728                        background=options.background,
2729                        alpha=bool(pamalpha or options.alpha),
2730                        gamma=options.gamma,
2731                        compression=options.compression)
2732        if options.alpha:
2733            pgmfile = open(options.alpha, 'rb')
2734            format, awidth, aheight, adepth, amaxval = \
2735              read_pnm_header(pgmfile, 'P5')
2736            if amaxval != '255':
2737                raise NotImplementedError(
2738                  'maxval %s not supported for alpha channel' % amaxval)
2739            if (awidth, aheight) != (width, height):
2740                raise ValueError("alpha channel image size mismatch"
2741                                 " (%s has %sx%s but %s has %sx%s)"
2742                                 % (infilename, width, height,
2743                                    options.alpha, awidth, aheight))
2744            writer.convert_ppm_and_pgm(infile, pgmfile, outfile)
2745        else:
2746            writer.convert_pnm(infile, outfile)
2747
2748
2749if __name__ == '__main__':
2750    try:
2751        _main(sys.argv)
2752    except Error as e:
2753        print(e, file=sys.stderr)
2754