source: titan/mediathek/localhoster/lib/png.py @ 38962

Last change on this file since 38962 was 38962, checked in by obi, 7 years ago

localhoster add needed libs

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