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