Pure Python PNG Generator (PPyPNG)
This snippet introduces a python script for generating PNG (Portable Network Graphics) image files. It is a straight conversion from a public domain C program.
The module has a limited capability for creating PNG files - it can only generate files with a 256-color palette.
The following python program uses it to create this example PNG image file, in this case something like a Sparkline.
demopng.py# demopng.py - tiny file based CMS for static web sites
# Copyright (c) 2008 Niall McCarroll
# Distributed under the MIT/X11 License (http://www.mccarroll.net/snippets/license.txt)
from ppypng import PNGCanvas
palette = [(255,255,255),(255,0,255),(0,0,255)]
width = 64
height = 32
xstep = 4
data = [10,11,25,26,29,30,22,23,20,18,21,16,14,12,4,5]
canvas = PNGCanvas(height,width,palette)
for x in xrange(0,16):
for xs in xrange(0,xstep):
for y in xrange(0,data[x]-3):
canvas.addpixel((x*xstep)+xs,y,1)
for y in xrange(data[x]-3,data[x]):
canvas.addpixel((x*xstep)+xs,y,2)
f = open("demo.png","w")
canvas.write(f)
The code for ppypng.py is shown below.
ppypng.py# pypng.py Converted to Python from Original C-Code by Niall McCarroll, 2008:
#
# This code was written by Martin Hinner <mhi@penguin.cz> in 2000,
# no copyright is claimed. This code is in the public domain;
# do with it what you wish.
#
from zlib import adler32, crc32
from math import log
class PNGCanvas:
def __init__(self,height,width,palette):
self.height = height
self.width = width
self.bits = 8
self.data = [0 for i in xrange(0,width*height)]
self.group_bytes=(self.width / (8 / self.bits) + 1)
self.colors=(1 << self.bits)
self.crc = 0L
self.init_palette(palette)
self.png_magic = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"
def init_palette(self,palette):
self.plt = []
for (r,g,b) in palette:
self.plt.append((r<<16)|(g<<8)|b)
for p in xrange(0,256-len(palette)):
self.plt.append(0)
def addpixel(self,x,y,col):
y = (self.height-1) - y
self.data[x+(y*self.width)] = col
def pixel(self,x,y):
return self.data[x+(y*self.width)]
def write(self,file):
i = 0
j = 0
k = 0
zcrc = 0
zero = 0xF1
filter = 0
self.fd = file
self.resetcrc()
self.fd.write(self.png_magic)
self.beginchunk("IHDR", 0x0d)
self.writelongcrc(self.width) # width
self.writelongcrc(self.height) # height
self.writebytecrc(self.bits) # bit depth
self.writebytecrc(3) # color type
self.writebytecrc(0) # compression
self.writebytecrc(0) # filter
self.writebytecrc(0) # interlace
self.endchunk()
self.beginchunk("PLTE", self.colors * 3);
i = 0
while i < self.colors:
j = 0
while j < 3:
self.writebytecrc((self.plt[i] >> ((2 - j) * 8)) & 0xFF)
j += 1
i += 1
self.endchunk()
self.beginchunk("IDAT", (self.height * (self.group_bytes + 4 + 1)) + 4 + 2)
self.writewordcrc(((0x0800 + 30) / 31) * 31 ) # compression method
zcrc = 1L
i = 0
while i < self.height:
if i == (self.height-1):
self.writebytecrc(0x01)
else:
self.writebytecrc(0)
self.Xwritewordcrc(self.group_bytes)
self.Xwritewordcrc(~self.group_bytes)
# write PNG row filter - 0 = unfiltered
zcrc = adler32(chr(filter),zcrc)
self.writebytecrc(filter)
k = 0
while k < self.width:
c = self.pixel(k,i)
zcrc = adler32(chr(c),zcrc)
self.writebytecrc(c)
k += 1
i += 1
self.writelongcrc(zcrc)
# print "zcrc = " + str(zcrc)
self.endchunk()
self.beginchunk("IEND", 0)
self.endchunk()
def resetcrc(self):
self.crc = 0
def writelong(self,l):
self.fd.write(chr((l>>24)&0xFF))
self.fd.write(chr((l>>16)&0xFF))
self.fd.write(chr((l>>8)&0xFF))
self.fd.write(chr(l&0xFF))
def writelongcrc(self,l):
c3 = chr((l>>24)&0xFF)
c2 = chr((l>>16)&0xFF)
c1 = chr((l>>8)&0xFF)
c0 = chr(l&0xFF)
self.crc = crc32(c3,self.crc)
self.fd.write(c3)
self.crc = crc32(c2,self.crc)
self.fd.write(c2)
self.crc = crc32(c1,self.crc)
self.fd.write(c1)
self.crc = crc32(c0,self.crc)
self.fd.write(c0)
def Xwritelongcrc(self,l):
c3 = chr((l>>24)&0xFF)
c2 = chr((l>>16)&0xFF)
c1 = chr((l>>8)&0xFF)
c0 = chr(l&0xFF)
self.crc = crc32(c0,self.crc)
self.fd.write(c0)
self.crc = crc32(c1,self.crc)
self.fd.write(c1)
self.crc = crc32(c2,self.crc)
self.fd.write(c2)
self.crc = crc32(c3,self.crc)
self.fd.write(c3)
def writewordcrc(self,s):
c1 = chr((s>>8)&0xFF)
c0 = chr(s&0xFF)
self.crc = crc32(c1,self.crc)
self.fd.write(c1)
self.crc = crc32(c0,self.crc)
self.fd.write(c0)
def Xwritewordcrc(self,s):
c1 = chr((s>>8)&0xFF)
c0 = chr(s&0xFF)
self.crc = crc32(c0,self.crc)
self.fd.write(c0)
self.crc = crc32(c1,self.crc)
self.fd.write(c1)
def writebytecrc(self,c):
self.crc = crc32(chr(c),self.crc)
self.fd.write(chr(c))
def beginchunk(self,name,len):
self.writelong(len)
self.resetcrc()
self.crc = crc32(name,self.crc)
self.fd.write(name)
def endchunk(self):
self.writelong(self.crc)