Python – PNG on-the-fly Stream Writer

Under writing.

import zlib, binascii
from struct import pack, unpack
class PNGColorType():
    GRAYSCALE = 0
    RGB = 2
    PLTE = 3
    GRAYSCALE_A = 4
    RGBA = 6
class PNGCompression():
    NO_COMPRESSION = 0
class PNGFilter():
    NO_FILTER = 0
class PNGInterlace():
    NO_INTERLACE = 0
class PNG():
    SIGNATURE = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
    def __init__(self, width, height, bpp, _color = PNGColorType.GRAYSCALE, _compression = PNGCompression.NO_COMPRESSION, _filter = PNGFilter.NO_FILTER, _interlace = PNGInterlace.NO_INTERLACE ):
        self.width = width
        self.height = height
        self.bpp = bpp
        self.color = _color
        self.compression = _compression
        self.filter = _filter
        self.interlace = _interlace
    def get_signature(self):
        return pack('>{}B'.format( len(PNG.SIGNATURE) ), *PNG.SIGNATURE)
    def get_ihdr(self):
        TYPE = b'IHDR'                   # CHUNK TYPE
        DATA = pack('>IIBBBBB',
            self.width & 0xFFFFFFFF,    # WIDTH
            self.height & 0xFFFFFFFF,   # HEIGHT
            self.bpp & 0xFF,            # BPP
            self.color & 0xFF,          # COLOR TYPE
            self.compression & 0xFF,    # COMPRESSION
            self.filter & 0xFF,         # FILTER
            self.interlace & 0xFF       # INTERLACE
        )
        LENGTH = len(DATA)              # LENGTH
        CRC = binascii.crc32(
            TYPE + DATA
        )
        return pack('>I4s', LENGTH, TYPE) + DATA + pack('>i', CRC)
    def create_idat(self, data):
        if not isinstance(data, list) and not isinstance(data, tuple):
            data = [data]
        TYPE = b'IDAT'                  # CHUNK TYPE
        raw = b"\0" # no filter for this scanline
        for d in data:
            raw += pack('>H', d)
        compressor = zlib.compressobj()
        DATA = compressor.compress(raw)
        DATA += compressor.flush()
        LENGTH = len(DATA)              # LENGTH
        CRC = binascii.crc32(
            TYPE + DATA
        )
        return pack('>I4s', LENGTH, TYPE) + DATA + pack('>i', CRC)
    def get_iend(self):
        TYPE = b'IEND'                  # CHUNK TYPE
        LENGTH = 0                      # LENGTH
        return pack('>I4si', LENGTH, TYPE, binascii.crc32(TYPE))
def main():
    x = 10
    y = 10
    test = PNG(x, y, 16, PNGColorType.RGB, PNGCompression.NO_COMPRESSION, PNGFilter.NO_FILTER, PNGInterlace.NO_INTERLACE)
    RED = 0xF000
    GREEN = 0x0F00
    BLUE = 0x000F
    WHITE = 0xFFFF
    BLACK = 0x000F
    with open('toast.png', 'wb') as f:
        f.write( test.get_signature() )
        f.write( test.get_ihdr() )
        for i in range(10):
            f.write( test.create_idat( (RED, BLUE) ) )
        f.write( test.get_iend() )
main()

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.