xref: /plan9-contrib/sys/src/cmd/jpg/writepng.c (revision 9b7bf7df4595c26f1e9b67beb0c6e44c9876fb05)
19863c128SDavid du Colombier /*
29863c128SDavid du Colombier  * See PNG 1.2 spec, also RFC 2083.
39863c128SDavid du Colombier  */
480ee5cbfSDavid du Colombier #include <u.h>
580ee5cbfSDavid du Colombier #include <libc.h>
680ee5cbfSDavid du Colombier #include <draw.h>
780ee5cbfSDavid du Colombier #include <memdraw.h>
880ee5cbfSDavid du Colombier #include <ctype.h>
980ee5cbfSDavid du Colombier #include <bio.h>
1080ee5cbfSDavid du Colombier #include <flate.h>
1180ee5cbfSDavid du Colombier #include "imagefile.h"
1280ee5cbfSDavid du Colombier 
139863c128SDavid du Colombier enum
149863c128SDavid du Colombier {
159863c128SDavid du Colombier 	IDATSIZE = 	20000,
1680ee5cbfSDavid du Colombier 	FilterNone =	0,
1780ee5cbfSDavid du Colombier };
1880ee5cbfSDavid du Colombier 
199863c128SDavid du Colombier typedef struct ZlibR ZlibR;
209863c128SDavid du Colombier typedef struct ZlibW ZlibW;
219863c128SDavid du Colombier 
229863c128SDavid du Colombier struct ZlibR
239863c128SDavid du Colombier {
249a747e4fSDavid du Colombier 	uchar *data;
25be6ffbb4SDavid du Colombier 	int width;
269863c128SDavid du Colombier 	int dx;
279863c128SDavid du Colombier 	int dy;
289863c128SDavid du Colombier 	int x;
299863c128SDavid du Colombier 	int y;
309863c128SDavid du Colombier 	int pixwid;
319863c128SDavid du Colombier };
3280ee5cbfSDavid du Colombier 
339863c128SDavid du Colombier struct ZlibW
349863c128SDavid du Colombier {
359863c128SDavid du Colombier 	Biobuf *io;
3680ee5cbfSDavid du Colombier 	uchar *buf;
379863c128SDavid du Colombier 	uchar *b;
389863c128SDavid du Colombier 	uchar *e;
399863c128SDavid du Colombier };
4080ee5cbfSDavid du Colombier 
4180ee5cbfSDavid du Colombier static ulong *crctab;
429863c128SDavid du Colombier static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
4380ee5cbfSDavid du Colombier 
4480ee5cbfSDavid du Colombier static void
put4(uchar * a,ulong v)4580ee5cbfSDavid du Colombier put4(uchar *a, ulong v)
4680ee5cbfSDavid du Colombier {
4780ee5cbfSDavid du Colombier 	a[0] = v>>24;
4880ee5cbfSDavid du Colombier 	a[1] = v>>16;
4980ee5cbfSDavid du Colombier 	a[2] = v>>8;
5080ee5cbfSDavid du Colombier 	a[3] = v;
5180ee5cbfSDavid du Colombier }
5280ee5cbfSDavid du Colombier 
5380ee5cbfSDavid du Colombier static void
chunk(Biobuf * bo,char * type,uchar * d,int n)5480ee5cbfSDavid du Colombier chunk(Biobuf *bo, char *type, uchar *d, int n)
5580ee5cbfSDavid du Colombier {
5680ee5cbfSDavid du Colombier 	uchar buf[4];
5780ee5cbfSDavid du Colombier 	ulong crc = 0;
5880ee5cbfSDavid du Colombier 
5980ee5cbfSDavid du Colombier 	if(strlen(type) != 4)
6080ee5cbfSDavid du Colombier 		return;
6180ee5cbfSDavid du Colombier 	put4(buf, n);
6280ee5cbfSDavid du Colombier 	Bwrite(bo, buf, 4);
6380ee5cbfSDavid du Colombier 	Bwrite(bo, type, 4);
6480ee5cbfSDavid du Colombier 	Bwrite(bo, d, n);
6580ee5cbfSDavid du Colombier 	crc = blockcrc(crctab, crc, type, 4);
6680ee5cbfSDavid du Colombier 	crc = blockcrc(crctab, crc, d, n);
6780ee5cbfSDavid du Colombier 	put4(buf, crc);
6880ee5cbfSDavid du Colombier 	Bwrite(bo, buf, 4);
6980ee5cbfSDavid du Colombier }
7080ee5cbfSDavid du Colombier 
7180ee5cbfSDavid du Colombier static int
zread(void * va,void * buf,int n)7280ee5cbfSDavid du Colombier zread(void *va, void *buf, int n)
7380ee5cbfSDavid du Colombier {
749863c128SDavid du Colombier 	int a, i, pixels, pixwid;
759863c128SDavid du Colombier 	uchar *b, *e, *img;
769863c128SDavid du Colombier 	ZlibR *z;
7780ee5cbfSDavid du Colombier 
789863c128SDavid du Colombier 	z = va;
799863c128SDavid du Colombier 	pixwid = z->pixwid;
809863c128SDavid du Colombier 	b = buf;
819863c128SDavid du Colombier 	e = b+n;
82*9b7bf7dfSDavid du Colombier 	while(b+pixwid < e){	/* one less for filter alg byte */
839863c128SDavid du Colombier 		if(z->y >= z->dy)
8480ee5cbfSDavid du Colombier 			break;
859863c128SDavid du Colombier 		if(z->x == 0)
8680ee5cbfSDavid du Colombier 			*b++ = FilterNone;
879863c128SDavid du Colombier 		pixels = (e-b)/pixwid;
889863c128SDavid du Colombier 		if(pixels > z->dx - z->x)
899863c128SDavid du Colombier 			pixels = z->dx - z->x;
909863c128SDavid du Colombier 		img = z->data + z->width*z->y + pixwid*z->x;
919863c128SDavid du Colombier 		memmove(b, img, pixwid*pixels);
929863c128SDavid du Colombier 		if(pixwid == 4){
939863c128SDavid du Colombier 			/*
949863c128SDavid du Colombier 			 * Convert to non-premultiplied alpha.
959863c128SDavid du Colombier 			 */
969863c128SDavid du Colombier 			for(i=0; i<pixels; i++, b+=4){
979863c128SDavid du Colombier 				a = b[3];
989863c128SDavid du Colombier 				if(a == 0)
999863c128SDavid du Colombier 					b[0] = b[1] = b[2] = 0;
1009863c128SDavid du Colombier 				else if(a != 255){
1019863c128SDavid du Colombier 					if(b[0] >= a)
1029863c128SDavid du Colombier 						b[0] = a;
1039863c128SDavid du Colombier 					b[0] = (b[0]*255)/a;
1049863c128SDavid du Colombier 					if(b[1] >= a)
1059863c128SDavid du Colombier 						b[1] = a;
1069863c128SDavid du Colombier 					b[1] = (b[1]*255)/a;
1079863c128SDavid du Colombier 					if(b[2] >= a)
1089863c128SDavid du Colombier 						b[2] = a;
1099863c128SDavid du Colombier 					b[2] = (b[2]*255)/a;
1109a747e4fSDavid du Colombier 				}
1119863c128SDavid du Colombier 			}
1129863c128SDavid du Colombier 		}else
1139863c128SDavid du Colombier 			b += pixwid*pixels;
1149a747e4fSDavid du Colombier 
1159863c128SDavid du Colombier 		z->x += pixels;
1169863c128SDavid du Colombier 		if(z->x >= z->dx){
1179863c128SDavid du Colombier 			z->x = 0;
1189863c128SDavid du Colombier 			z->y++;
11980ee5cbfSDavid du Colombier 		}
12080ee5cbfSDavid du Colombier 	}
12180ee5cbfSDavid du Colombier 	return b - (uchar*)buf;
12280ee5cbfSDavid du Colombier }
12380ee5cbfSDavid du Colombier 
12480ee5cbfSDavid du Colombier static void
IDAT(ZlibW * z)12580ee5cbfSDavid du Colombier IDAT(ZlibW *z)
12680ee5cbfSDavid du Colombier {
1279863c128SDavid du Colombier 	chunk(z->io, "IDAT", z->buf, z->b - z->buf);
12880ee5cbfSDavid du Colombier 	z->b = z->buf;
12980ee5cbfSDavid du Colombier }
13080ee5cbfSDavid du Colombier 
13180ee5cbfSDavid du Colombier static int
zwrite(void * va,void * buf,int n)13280ee5cbfSDavid du Colombier zwrite(void *va, void *buf, int n)
13380ee5cbfSDavid du Colombier {
13480ee5cbfSDavid du Colombier 	int m;
1359863c128SDavid du Colombier 	uchar *b, *e;
1369863c128SDavid du Colombier 	ZlibW *z;
13780ee5cbfSDavid du Colombier 
1389863c128SDavid du Colombier 	z = va;
1399863c128SDavid du Colombier 	b = buf;
1409863c128SDavid du Colombier 	e = b+n;
1419863c128SDavid du Colombier 
1429863c128SDavid du Colombier 	while(b < e){
14380ee5cbfSDavid du Colombier 		m = z->e - z->b;
14480ee5cbfSDavid du Colombier 		if(m > e - b)
14580ee5cbfSDavid du Colombier 			m = e - b;
14680ee5cbfSDavid du Colombier 		memmove(z->b, b, m);
14780ee5cbfSDavid du Colombier 		z->b += m;
14880ee5cbfSDavid du Colombier 		b += m;
14980ee5cbfSDavid du Colombier 		if(z->b >= z->e)
15080ee5cbfSDavid du Colombier 			IDAT(z);
15180ee5cbfSDavid du Colombier 	}
15280ee5cbfSDavid du Colombier 	return n;
15380ee5cbfSDavid du Colombier }
15480ee5cbfSDavid du Colombier 
15580ee5cbfSDavid du Colombier static Memimage*
memRGBA(Memimage * i)1569863c128SDavid du Colombier memRGBA(Memimage *i)
15780ee5cbfSDavid du Colombier {
15880ee5cbfSDavid du Colombier 	Memimage *ni;
1599863c128SDavid du Colombier 	char buf[32];
1609863c128SDavid du Colombier 	ulong dst;
16180ee5cbfSDavid du Colombier 
1629863c128SDavid du Colombier 	/*
1639863c128SDavid du Colombier 	 * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh.
1649863c128SDavid du Colombier 	 */
1659863c128SDavid du Colombier 	chantostr(buf, i->chan);
1669863c128SDavid du Colombier 	if(strchr(buf, 'a'))
1679863c128SDavid du Colombier 		dst = ABGR32;
1689863c128SDavid du Colombier 	else
1699863c128SDavid du Colombier 		dst = BGR24;
1709863c128SDavid du Colombier 
1719863c128SDavid du Colombier 	if(i->chan == dst)
17280ee5cbfSDavid du Colombier 		return i;
17380ee5cbfSDavid du Colombier 
1749863c128SDavid du Colombier 	ni = allocmemimage(i->r, dst);
17580ee5cbfSDavid du Colombier 	if(ni == nil)
17680ee5cbfSDavid du Colombier 		return ni;
1776a9fc400SDavid du Colombier 	memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
17880ee5cbfSDavid du Colombier 	return ni;
17980ee5cbfSDavid du Colombier }
18080ee5cbfSDavid du Colombier 
18180ee5cbfSDavid du Colombier char*
memwritepng(Biobuf * io,Memimage * m,ImageInfo * II)1829863c128SDavid du Colombier memwritepng(Biobuf *io, Memimage *m, ImageInfo *II)
18380ee5cbfSDavid du Colombier {
1849863c128SDavid du Colombier 	int err, n;
18580ee5cbfSDavid du Colombier 	uchar buf[200], *h;
18680ee5cbfSDavid du Colombier 	ulong vgamma;
18780ee5cbfSDavid du Colombier 	Tm *tm;
18880ee5cbfSDavid du Colombier 	Memimage *rgb;
1899863c128SDavid du Colombier 	ZlibR zr;
1909863c128SDavid du Colombier 	ZlibW zw;
19180ee5cbfSDavid du Colombier 
19280ee5cbfSDavid du Colombier 	crctab = mkcrctab(0xedb88320);
19380ee5cbfSDavid du Colombier 	if(crctab == nil)
19480ee5cbfSDavid du Colombier 		sysfatal("mkcrctab error");
19580ee5cbfSDavid du Colombier 	deflateinit();
19680ee5cbfSDavid du Colombier 
1979863c128SDavid du Colombier 	rgb = memRGBA(m);
1989863c128SDavid du Colombier 	if(rgb == nil)
1999863c128SDavid du Colombier 		return "allocmemimage nil";
20080ee5cbfSDavid du Colombier 
2019863c128SDavid du Colombier 	Bwrite(io, PNGmagic, sizeof PNGmagic);
2029863c128SDavid du Colombier 
2039863c128SDavid du Colombier 	/* IHDR chunk */
2049863c128SDavid du Colombier 	h = buf;
2059863c128SDavid du Colombier 	put4(h, Dx(m->r)); h += 4;
2069863c128SDavid du Colombier 	put4(h, Dy(m->r)); h += 4;
2079863c128SDavid du Colombier 	*h++ = 8;	/* 8 bits per channel */
2089863c128SDavid du Colombier 	if(rgb->chan == BGR24)
2099863c128SDavid du Colombier 		*h++ = 2;		/* RGB */
2109863c128SDavid du Colombier 	else
2119863c128SDavid du Colombier 		*h++ = 6;		/* RGBA */
2129863c128SDavid du Colombier 	*h++ = 0;	/* compression - deflate */
2139863c128SDavid du Colombier 	*h++ = 0;	/* filter - none */
2149863c128SDavid du Colombier 	*h++ = 0;	/* interlace - none */
2159863c128SDavid du Colombier 	chunk(io, "IHDR", buf, h-buf);
2169863c128SDavid du Colombier 
2179863c128SDavid du Colombier 	/* time - using now is suspect */
21880ee5cbfSDavid du Colombier 	tm = gmtime(time(0));
21980ee5cbfSDavid du Colombier 	h = buf;
22080ee5cbfSDavid du Colombier 	*h++ = (tm->year + 1900)>>8;
22180ee5cbfSDavid du Colombier 	*h++ = (tm->year + 1900)&0xff;
22280ee5cbfSDavid du Colombier 	*h++ = tm->mon + 1;
22380ee5cbfSDavid du Colombier 	*h++ = tm->mday;
22480ee5cbfSDavid du Colombier 	*h++ = tm->hour;
22580ee5cbfSDavid du Colombier 	*h++ = tm->min;
22680ee5cbfSDavid du Colombier 	*h++ = tm->sec;
2279863c128SDavid du Colombier 	chunk(io, "tIME", buf, h-buf);
22880ee5cbfSDavid du Colombier 
2299863c128SDavid du Colombier 	/* gamma */
23080ee5cbfSDavid du Colombier 	if(II->fields_set & II_GAMMA){
23180ee5cbfSDavid du Colombier 		vgamma = II->gamma*100000;
23280ee5cbfSDavid du Colombier 		put4(buf, vgamma);
2339863c128SDavid du Colombier 		chunk(io, "gAMA", buf, 4);
23480ee5cbfSDavid du Colombier 	}
23580ee5cbfSDavid du Colombier 
2369863c128SDavid du Colombier 	/* comment */
23780ee5cbfSDavid du Colombier 	if(II->fields_set & II_COMMENT){
23880ee5cbfSDavid du Colombier 		strncpy((char*)buf, "Comment", sizeof buf);
23980ee5cbfSDavid du Colombier 		n = strlen((char*)buf)+1; // leave null between Comment and text
24080ee5cbfSDavid du Colombier 		strncpy((char*)(buf+n), II->comment, sizeof buf - n);
2419863c128SDavid du Colombier 		chunk(io, "tEXt", buf, n+strlen((char*)buf+n));
24280ee5cbfSDavid du Colombier 	}
24380ee5cbfSDavid du Colombier 
2449863c128SDavid du Colombier 	/* image data */
2459863c128SDavid du Colombier 	zr.dx = Dx(m->r);
2469863c128SDavid du Colombier 	zr.dy = Dy(m->r);
247be6ffbb4SDavid du Colombier 	zr.width = rgb->width * sizeof(ulong);
2489a747e4fSDavid du Colombier 	zr.data = rgb->data->bdata;
2499863c128SDavid du Colombier 	zr.x = 0;
2509863c128SDavid du Colombier 	zr.y = 0;
2519863c128SDavid du Colombier 	zr.pixwid = chantodepth(rgb->chan)/8;
2529863c128SDavid du Colombier 	zw.io = io;
25380ee5cbfSDavid du Colombier 	zw.buf = malloc(IDATSIZE);
2549863c128SDavid du Colombier 	if(zw.buf == nil)
2559863c128SDavid du Colombier 		sysfatal("malloc: %r");
25680ee5cbfSDavid du Colombier 	zw.b = zw.buf;
25780ee5cbfSDavid du Colombier 	zw.e = zw.b + IDATSIZE;
2589863c128SDavid du Colombier 	if((err=deflatezlib(&zw, zwrite, &zr, zread, 6, 0)) < 0)
2599863c128SDavid du Colombier 		sysfatal("deflatezlib %s", flateerr(err));
26080ee5cbfSDavid du Colombier 	if(zw.b > zw.buf)
26180ee5cbfSDavid du Colombier 		IDAT(&zw);
26280ee5cbfSDavid du Colombier 	free(zw.buf);
2639863c128SDavid du Colombier 	chunk(io, "IEND", nil, 0);
2649863c128SDavid du Colombier 	if(m != rgb)
26580ee5cbfSDavid du Colombier 		freememimage(rgb);
26680ee5cbfSDavid du Colombier 	return nil;
26780ee5cbfSDavid du Colombier }
268