| #include "stdinc.h" | 
 | #include "dat.h" | 
 | #include "fns.h" | 
 |  | 
 | enum | 
 | { | 
 | 	IDATSIZE	= 20000, | 
 | 	FilterNone = 0 | 
 | }; | 
 |  | 
 | typedef struct ZlibR ZlibR; | 
 | typedef struct ZlibW ZlibW; | 
 |  | 
 | struct ZlibR | 
 | { | 
 | 	uchar *data; | 
 | 	int width; | 
 | 	int dx; | 
 | 	int dy; | 
 | 	int x; | 
 | 	int y; | 
 | 	int pixwid; | 
 | }; | 
 |  | 
 | struct ZlibW | 
 | { | 
 | 	Hio *io; | 
 | 	uchar *buf; | 
 | 	uchar *b; | 
 | 	uchar *e; | 
 | }; | 
 |  | 
 | static u32int *crctab; | 
 | static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'}; | 
 |  | 
 | static void | 
 | put4(uchar *a, ulong v) | 
 | { | 
 | 	a[0] = v>>24; | 
 | 	a[1] = v>>16; | 
 | 	a[2] = v>>8; | 
 | 	a[3] = v; | 
 | } | 
 |  | 
 | static void | 
 | chunk(Hio *io, char *type, uchar *d, int n) | 
 | { | 
 | 	uchar buf[4]; | 
 | 	ulong crc = 0; | 
 |  | 
 | 	if(strlen(type) != 4) | 
 | 		return; | 
 | 	put4(buf, n); | 
 | 	hwrite(io, buf, 4); | 
 | 	hwrite(io, type, 4); | 
 | 	hwrite(io, d, n); | 
 | 	crc = blockcrc(crctab, crc, type, 4); | 
 | 	crc = blockcrc(crctab, crc, d, n); | 
 | 	put4(buf, crc); | 
 | 	hwrite(io, buf, 4); | 
 | } | 
 |  | 
 | static int | 
 | zread(void *va, void *buf, int n) | 
 | { | 
 | 	int a, i, pixels, pixwid; | 
 | 	uchar *b, *e, *img; | 
 | 	ZlibR *z; | 
 |  | 
 | 	z = va; | 
 | 	pixwid = z->pixwid; | 
 | 	b = buf; | 
 | 	e = b+n; | 
 | 	while(b+pixwid <= e){ | 
 | 		if(z->y >= z->dy) | 
 | 			break; | 
 | 		if(z->x == 0) | 
 | 			*b++ = FilterNone; | 
 | 		pixels = (e-b)/pixwid; | 
 | 		if(pixels > z->dx - z->x) | 
 | 			pixels = z->dx - z->x; | 
 | 		img = z->data + z->width*z->y + pixwid*z->x; | 
 | 		memmove(b, img, pixwid*pixels); | 
 | 		if(pixwid == 4){ | 
 | 			/* | 
 | 			 * Convert to non-premultiplied alpha. | 
 | 			 */ | 
 | 			for(i=0; i<pixels; i++, b+=4){ | 
 | 				a = b[3]; | 
 | 				if(a != 0 && a != 255){ | 
 | 					if(b[0] >= a) | 
 | 						b[0] = a; | 
 | 					b[0] = (b[0]*255)/a; | 
 | 					if(b[1] >= a) | 
 | 						b[1] = a; | 
 | 					b[1] = (b[1]*255)/a; | 
 | 					if(b[2] >= a) | 
 | 						b[2] = a; | 
 | 					b[2] = (b[2]*255)/a; | 
 | 				} | 
 | 			} | 
 | 		}else	 | 
 | 			b += pixwid*pixels; | 
 |  | 
 | 		z->x += pixels; | 
 | 		if(z->x >= z->dx){ | 
 | 			z->x = 0; | 
 | 			z->y++; | 
 | 		} | 
 | 	} | 
 | 	return b - (uchar*)buf; | 
 | } | 
 |  | 
 | static void | 
 | IDAT(ZlibW *z) | 
 | { | 
 | 	chunk(z->io, "IDAT", z->buf, z->b - z->buf); | 
 | 	z->b = z->buf; | 
 | } | 
 |  | 
 | static int | 
 | zwrite(void *va, void *buf, int n) | 
 | { | 
 | 	int m; | 
 | 	uchar *b, *e; | 
 | 	ZlibW *z; | 
 |  | 
 | 	z = va; | 
 | 	b = buf; | 
 | 	e = b+n; | 
 |  | 
 | 	while(b < e){ | 
 | 		m = z->e - z->b; | 
 | 		if(m > e - b) | 
 | 			m = e - b; | 
 | 		memmove(z->b, b, m); | 
 | 		z->b += m; | 
 | 		b += m; | 
 | 		if(z->b >= z->e) | 
 | 			IDAT(z); | 
 | 	} | 
 | 	return n; | 
 | } | 
 |  | 
 | static Memimage* | 
 | memRGBA(Memimage *i) | 
 | { | 
 | 	Memimage *ni; | 
 | 	char buf[32]; | 
 | 	ulong dst; | 
 | 	 | 
 | 	/* | 
 | 	 * [A]BGR because we want R,G,B,[A] in big-endian order.  Sigh. | 
 | 	 */ | 
 | 	chantostr(buf, i->chan); | 
 | 	if(strchr(buf, 'a')) | 
 | 		dst = ABGR32; | 
 | 	else | 
 | 		dst = BGR24; | 
 | 		 | 
 | 	if(i->chan == dst) | 
 | 		return i; | 
 |  | 
 | 	qlock(&memdrawlock); | 
 | 	ni = allocmemimage(i->r, dst); | 
 | 	if(ni) | 
 | 		memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S); | 
 | 	qunlock(&memdrawlock); | 
 | 	return ni; | 
 | } | 
 |  | 
 | int | 
 | writepng(Hio *io, Memimage *m) | 
 | { | 
 | 	static int first = 1; | 
 | 	static QLock lk; | 
 | 	uchar buf[200], *h; | 
 | 	Memimage *rgb; | 
 | 	ZlibR zr; | 
 | 	ZlibW zw; | 
 |  | 
 | 	if(first){ | 
 | 		qlock(&lk); | 
 | 		if(first){ | 
 | 			deflateinit(); | 
 | 			crctab = mkcrctab(0xedb88320); | 
 | 			first = 0; | 
 | 		} | 
 | 		qunlock(&lk); | 
 | 	} | 
 |  | 
 | 	rgb = memRGBA(m); | 
 | 	if(rgb == nil) | 
 | 		return -1; | 
 |  | 
 | 	hwrite(io, PNGmagic, sizeof PNGmagic); | 
 | 	 | 
 | 	/* IHDR chunk */ | 
 | 	h = buf; | 
 | 	put4(h, Dx(m->r)); h += 4; | 
 | 	put4(h, Dy(m->r)); h += 4; | 
 | 	*h++ = 8;	/* 8 bits per channel */ | 
 | 	if(rgb->chan == BGR24) | 
 | 		*h++ = 2;		/* RGB */ | 
 | 	else | 
 | 		*h++ = 6;		/* RGBA */ | 
 | 	*h++ = 0;	/* compression - deflate */ | 
 | 	*h++ = 0;	/* filter - none */ | 
 | 	*h++ = 0;	/* interlace - none */ | 
 | 	chunk(io, "IHDR", buf, h-buf); | 
 |  | 
 | 	/* image data */ | 
 | 	zr.dx = Dx(m->r); | 
 | 	zr.dy = Dy(m->r); | 
 | 	zr.width = rgb->width * sizeof(ulong); | 
 | 	zr.data = rgb->data->bdata; | 
 | 	zr.x = 0; | 
 | 	zr.y = 0; | 
 | 	zr.pixwid = chantodepth(rgb->chan)/8; | 
 | 	zw.io = io; | 
 | 	zw.buf = vtmalloc(IDATSIZE); | 
 | 	zw.b = zw.buf; | 
 | 	zw.e = zw.b + IDATSIZE; | 
 | 	if(deflatezlib(&zw, zwrite, &zr, zread, 6, 0) < 0){ | 
 | 		free(zw.buf); | 
 | 		return -1; | 
 | 	} | 
 | 	if(zw.b > zw.buf) | 
 | 		IDAT(&zw); | 
 | 	free(zw.buf); | 
 | 	chunk(io, "IEND", nil, 0); | 
 |  | 
 | 	if(m != rgb){ | 
 | 		qlock(&memdrawlock); | 
 | 		freememimage(rgb); | 
 | 		qunlock(&memdrawlock); | 
 | 	} | 
 | 	return 0; | 
 | } |