xref: /plan9/sys/src/cmd/jpg/readpng.c (revision 9b7bf7df4595c26f1e9b67beb0c6e44c9876fb05)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <ctype.h>
49a747e4fSDavid du Colombier #include <bio.h>
59a747e4fSDavid du Colombier #include <flate.h>
69a747e4fSDavid du Colombier #include <draw.h>
79a747e4fSDavid du Colombier #include "imagefile.h"
89a747e4fSDavid du Colombier 
99a747e4fSDavid du Colombier int debug;
109a747e4fSDavid du Colombier 
11417cabcdSDavid du Colombier enum
12417cabcdSDavid du Colombier {
139863c128SDavid du Colombier 	IDATSIZE = 1000000,
149863c128SDavid du Colombier 
15417cabcdSDavid du Colombier 	/* filtering algorithms */
16714fb1e1SDavid du Colombier 	FilterNone =	0,	/* new[x][y] = buf[x][y] */
17714fb1e1SDavid du Colombier 	FilterSub =	1,	/* new[x][y] = buf[x][y] + new[x-1][y] */
18714fb1e1SDavid du Colombier 	FilterUp =	2,	/* new[x][y] = buf[x][y] + new[x][y-1] */
19714fb1e1SDavid du Colombier 	FilterAvg =	3,	/* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */
20714fb1e1SDavid du Colombier 	FilterPaeth =	4,	/* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
21714fb1e1SDavid du Colombier 	FilterLast =	5,
229863c128SDavid du Colombier 
239a747e4fSDavid du Colombier 	PropertyBit = 1<<5,
249a747e4fSDavid du Colombier };
259a747e4fSDavid du Colombier 
26417cabcdSDavid du Colombier typedef struct ZlibR ZlibR;
27417cabcdSDavid du Colombier typedef struct ZlibW ZlibW;
289863c128SDavid du Colombier 
29417cabcdSDavid du Colombier struct ZlibW
30417cabcdSDavid du Colombier {
31417cabcdSDavid du Colombier 	uchar *data;		/* Rawimage data */
32417cabcdSDavid du Colombier 	int ndata;
33417cabcdSDavid du Colombier 	int noutchan;
34417cabcdSDavid du Colombier 	int chandesc;
35417cabcdSDavid du Colombier 	int nchan;
36417cabcdSDavid du Colombier 
37417cabcdSDavid du Colombier 	uchar *scan;		/* new scanline */
38417cabcdSDavid du Colombier 	uchar *lastscan;	/* previous scan line */
39417cabcdSDavid du Colombier 	int scanlen;		/* scan line length */
40417cabcdSDavid du Colombier 	int scanpos;		/* scan position */
41417cabcdSDavid du Colombier 
42417cabcdSDavid du Colombier 	int dx;			/* width of image */
43417cabcdSDavid du Colombier 	int dy;			/* height of image */
44417cabcdSDavid du Colombier 	int bpc;			/* bits per channel (per pixel) */
45417cabcdSDavid du Colombier 	int y;				/* current scan line */
46417cabcdSDavid du Colombier 	int pass;			/* adam7 pass#; 0 means no adam7 */
47417cabcdSDavid du Colombier 	uchar palette[3*256];	/* color palette */
48417cabcdSDavid du Colombier 	int palsize;		/* number of palette entries */
49417cabcdSDavid du Colombier };
50417cabcdSDavid du Colombier 
51417cabcdSDavid du Colombier struct ZlibR
52417cabcdSDavid du Colombier {
53417cabcdSDavid du Colombier 	Biobuf *io;		/* input buffer */
54417cabcdSDavid du Colombier 	uchar *buf;		/* malloc'ed staging buffer */
55417cabcdSDavid du Colombier 	uchar *p;			/* next byte to decompress */
56417cabcdSDavid du Colombier 	uchar *e;			/* end of buffer */
579863c128SDavid du Colombier 	ZlibW *w;
58417cabcdSDavid du Colombier };
599a747e4fSDavid du Colombier 
609a747e4fSDavid du Colombier static ulong *crctab;
619863c128SDavid du Colombier static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
629a747e4fSDavid du Colombier 
639a747e4fSDavid du Colombier static ulong
get4(uchar * a)649a747e4fSDavid du Colombier get4(uchar *a)
659a747e4fSDavid du Colombier {
669a747e4fSDavid du Colombier 	return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
679a747e4fSDavid du Colombier }
689a747e4fSDavid du Colombier 
69417cabcdSDavid du Colombier static
70417cabcdSDavid du Colombier void
pnginit(void)719a747e4fSDavid du Colombier pnginit(void)
729a747e4fSDavid du Colombier {
739a747e4fSDavid du Colombier 	static int inited;
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier 	if(inited)
769a747e4fSDavid du Colombier 		return;
779a747e4fSDavid du Colombier 	inited = 1;
789a747e4fSDavid du Colombier 	crctab = mkcrctab(0xedb88320);
799a747e4fSDavid du Colombier 	if(crctab == nil)
809a747e4fSDavid du Colombier 		sysfatal("mkcrctab error");
819a747e4fSDavid du Colombier 	inflateinit();
829a747e4fSDavid du Colombier }
839a747e4fSDavid du Colombier 
84417cabcdSDavid du Colombier static
85417cabcdSDavid du Colombier void*
pngmalloc(ulong n,int clear)869a747e4fSDavid du Colombier pngmalloc(ulong n, int clear)
879a747e4fSDavid du Colombier {
889a747e4fSDavid du Colombier 	void *p;
899a747e4fSDavid du Colombier 
90417cabcdSDavid du Colombier 	p = mallocz(n, clear);
919a747e4fSDavid du Colombier 	if(p == nil)
92417cabcdSDavid du Colombier 		sysfatal("malloc: %r");
939a747e4fSDavid du Colombier 	return p;
949a747e4fSDavid du Colombier }
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier static int
getchunk(Biobuf * b,char * type,uchar * d,int m)979a747e4fSDavid du Colombier getchunk(Biobuf *b, char *type, uchar *d, int m)
989a747e4fSDavid du Colombier {
999a747e4fSDavid du Colombier 	uchar buf[8];
1009a747e4fSDavid du Colombier 	ulong crc = 0, crc2;
1019a747e4fSDavid du Colombier 	int n, nr;
1029a747e4fSDavid du Colombier 
1039a747e4fSDavid du Colombier 	if(Bread(b, buf, 8) != 8)
1049a747e4fSDavid du Colombier 		return -1;
1059a747e4fSDavid du Colombier 	n = get4(buf);
1069a747e4fSDavid du Colombier 	memmove(type, buf+4, 4);
1079a747e4fSDavid du Colombier 	type[4] = 0;
1089a747e4fSDavid du Colombier 	if(n > m)
1099a747e4fSDavid du Colombier 		sysfatal("getchunk needed %d, had %d", n, m);
1109a747e4fSDavid du Colombier 	nr = Bread(b, d, n);
1119a747e4fSDavid du Colombier 	if(nr != n)
1129a747e4fSDavid du Colombier 		sysfatal("getchunk read %d, expected %d", nr, n);
1139a747e4fSDavid du Colombier 	crc = blockcrc(crctab, crc, type, 4);
1149a747e4fSDavid du Colombier 	crc = blockcrc(crctab, crc, d, n);
1159a747e4fSDavid du Colombier 	if(Bread(b, buf, 4) != 4)
1169a747e4fSDavid du Colombier 		sysfatal("getchunk tlr failed");
1179a747e4fSDavid du Colombier 	crc2 = get4(buf);
1189a747e4fSDavid du Colombier 	if(crc != crc2)
1199a747e4fSDavid du Colombier 		sysfatal("getchunk crc failed");
120be6ffbb4SDavid du Colombier 	return n;
1219a747e4fSDavid du Colombier }
1229a747e4fSDavid du Colombier 
1239a747e4fSDavid du Colombier static int
zread(void * va)1249a747e4fSDavid du Colombier zread(void *va)
1259a747e4fSDavid du Colombier {
1269a747e4fSDavid du Colombier 	ZlibR *z = va;
1279a747e4fSDavid du Colombier 	char type[5];
1289a747e4fSDavid du Colombier 	int n;
1299a747e4fSDavid du Colombier 
130417cabcdSDavid du Colombier 	if(z->p >= z->e){
131417cabcdSDavid du Colombier 	Again:
132417cabcdSDavid du Colombier 		z->p = z->buf;
133417cabcdSDavid du Colombier 		z->e = z->p;
134417cabcdSDavid du Colombier 		n = getchunk(z->io, type, z->p, IDATSIZE);
1359a747e4fSDavid du Colombier 		if(n < 0 || strcmp(type, "IEND") == 0)
1369a747e4fSDavid du Colombier 			return -1;
137417cabcdSDavid du Colombier 		z->e = z->p + n;
138417cabcdSDavid du Colombier 		if(!strcmp(type,"PLTE")){
1399863c128SDavid du Colombier 			if(n < 3 || n > 3*256 || n%3)
1409863c128SDavid du Colombier 				sysfatal("invalid PLTE chunk len %d", n);
141417cabcdSDavid du Colombier 			memcpy(z->w->palette, z->p, n);
142417cabcdSDavid du Colombier 			z->w->palsize = 256;
143417cabcdSDavid du Colombier 			goto Again;
1449a747e4fSDavid du Colombier 		}
145417cabcdSDavid du Colombier 		if(type[0] & PropertyBit)
146417cabcdSDavid du Colombier 			goto Again;  /* skip auxiliary chunks fornow */
147417cabcdSDavid du Colombier 		if(strcmp(type,"IDAT")){
1489863c128SDavid du Colombier 			sysfatal("unrecognized mandatory chunk %s", type);
149417cabcdSDavid du Colombier 			goto Again;
1509863c128SDavid du Colombier 		}
151417cabcdSDavid du Colombier 	}
152417cabcdSDavid du Colombier 	return *z->p++;
1539a747e4fSDavid du Colombier }
1549a747e4fSDavid du Colombier 
155714fb1e1SDavid du Colombier static uchar
paeth(uchar a,uchar b,uchar c)156714fb1e1SDavid du Colombier paeth(uchar a, uchar b, uchar c)
157714fb1e1SDavid du Colombier {
158714fb1e1SDavid du Colombier 	int p, pa, pb, pc;
159714fb1e1SDavid du Colombier 
160417cabcdSDavid du Colombier 	p = a + b - c;
161417cabcdSDavid du Colombier 	pa = abs(p - a);
162417cabcdSDavid du Colombier 	pb = abs(p - b);
163417cabcdSDavid du Colombier 	pc = abs(p - c);
164714fb1e1SDavid du Colombier 
165714fb1e1SDavid du Colombier 	if(pa <= pb && pa <= pc)
166714fb1e1SDavid du Colombier 		return a;
167714fb1e1SDavid du Colombier 	else if(pb <= pc)
168714fb1e1SDavid du Colombier 		return b;
169714fb1e1SDavid du Colombier 	return c;
170714fb1e1SDavid du Colombier }
171714fb1e1SDavid du Colombier 
172714fb1e1SDavid du Colombier static void
unfilter(int alg,uchar * buf,uchar * up,int len,int bypp)1739863c128SDavid du Colombier unfilter(int alg, uchar *buf, uchar *up, int len, int bypp)
174714fb1e1SDavid du Colombier {
1759863c128SDavid du Colombier 	int i;
1769863c128SDavid du Colombier 
177714fb1e1SDavid du Colombier 	switch(alg){
1789863c128SDavid du Colombier 	case FilterNone:
1799863c128SDavid du Colombier 		break;
1809863c128SDavid du Colombier 
181714fb1e1SDavid du Colombier 	case FilterSub:
1829863c128SDavid du Colombier 		for(i = bypp; i < len; ++i)
1839863c128SDavid du Colombier 			buf[i] += buf[i-bypp];
184714fb1e1SDavid du Colombier 		break;
1859863c128SDavid du Colombier 
186714fb1e1SDavid du Colombier 	case FilterUp:
1879863c128SDavid du Colombier 		for(i = 0; i < len; ++i)
1889863c128SDavid du Colombier 			buf[i] += up[i];
189714fb1e1SDavid du Colombier 		break;
1909863c128SDavid du Colombier 
191714fb1e1SDavid du Colombier 	case FilterAvg:
1929863c128SDavid du Colombier 		for(i = 0; i < bypp; ++i)
1939863c128SDavid du Colombier 			buf[i] += (0+up[i])/2;
1949863c128SDavid du Colombier 		for(; i < len; ++i)
1959863c128SDavid du Colombier 			buf[i] += (buf[i-bypp]+up[i])/2;
196714fb1e1SDavid du Colombier 		break;
1979863c128SDavid du Colombier 
198714fb1e1SDavid du Colombier 	case FilterPaeth:
1999863c128SDavid du Colombier 		for(i = 0; i < bypp; ++i)
2009863c128SDavid du Colombier 			buf[i] += paeth(0, up[i], 0);
2019863c128SDavid du Colombier 		for(; i < len; ++i)
2029863c128SDavid du Colombier 			buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]);
203714fb1e1SDavid du Colombier 		break;
204417cabcdSDavid du Colombier 
2059863c128SDavid du Colombier 	default:
206417cabcdSDavid du Colombier 		sysfatal("unknown filtering scheme %d\n", alg);
207714fb1e1SDavid du Colombier 	}
208714fb1e1SDavid du Colombier }
209714fb1e1SDavid du Colombier 
210417cabcdSDavid du Colombier struct {
211417cabcdSDavid du Colombier 	int x;
212417cabcdSDavid du Colombier 	int y;
213417cabcdSDavid du Colombier 	int dx;
214417cabcdSDavid du Colombier 	int dy;
215417cabcdSDavid du Colombier } adam7[] = {
216417cabcdSDavid du Colombier 	{0,0,1,1},	/* eve alone */
217417cabcdSDavid du Colombier 	{0,0,8,8},	/* pass 1 */
218417cabcdSDavid du Colombier 	{4,0,8,8},	/* pass 2 */
219417cabcdSDavid du Colombier 	{0,4,4,8},	/* pass 3 */
220417cabcdSDavid du Colombier 	{2,0,4,4},	/* pass 4 */
221417cabcdSDavid du Colombier 	{0,2,2,4},	/* pass 5 */
222417cabcdSDavid du Colombier 	{1,0,2,2},	/* pass 6 */
223417cabcdSDavid du Colombier 	{0,1,1,2},	/* pass 7 */
224417cabcdSDavid du Colombier };
225417cabcdSDavid du Colombier 
2269863c128SDavid du Colombier static void
scan(int len,ZlibW * z)227417cabcdSDavid du Colombier scan(int len, ZlibW *z)
2289a747e4fSDavid du Colombier {
229417cabcdSDavid du Colombier 	int chan, i, j, nbit, off, val;
230417cabcdSDavid du Colombier 	uchar pixel[4], *p, *w;
2319863c128SDavid du Colombier 
232417cabcdSDavid du Colombier 	unfilter(z->scan[0], z->scan+1, z->lastscan+1, len-1, (z->nchan*z->bpc+7)/8);
2339863c128SDavid du Colombier 
2349863c128SDavid du Colombier 	/*
235417cabcdSDavid du Colombier 	 * loop over raw bits extracting pixel values and converting to 8-bit
2369863c128SDavid du Colombier 	 */
237417cabcdSDavid du Colombier 	nbit = 0;
238417cabcdSDavid du Colombier 	chan = 0;
239417cabcdSDavid du Colombier 	val = 0;
240417cabcdSDavid du Colombier 	off = z->y*z->dx + adam7[z->pass].x;
241417cabcdSDavid du Colombier 	w = z->data + z->noutchan*off;
242417cabcdSDavid du Colombier 	p = z->scan+1;	/* skip alg byte */
243417cabcdSDavid du Colombier 	len--;
244417cabcdSDavid du Colombier 	for(i=0; i<len*8; i++){
245417cabcdSDavid du Colombier 		val <<= 1;
246417cabcdSDavid du Colombier 		if(p[i>>3] & (1<<(7-(i&7))))
247417cabcdSDavid du Colombier 			val++;
248417cabcdSDavid du Colombier 		if(++nbit == z->bpc){
249417cabcdSDavid du Colombier 			/* finished the value */
250417cabcdSDavid du Colombier 			pixel[chan++] = (val*255)/((1<<z->bpc)-1);
251417cabcdSDavid du Colombier 			val = 0;
252417cabcdSDavid du Colombier 			nbit = 0;
253417cabcdSDavid du Colombier 			if(chan == z->nchan){
254417cabcdSDavid du Colombier 				/* finished the pixel */
255417cabcdSDavid du Colombier 				if(off < z->dx*z->dy){
256417cabcdSDavid du Colombier 					if(z->nchan < 3 && z->palsize){
257417cabcdSDavid du Colombier 						j = pixel[0];
258417cabcdSDavid du Colombier 						if(z->bpc < 8)
259417cabcdSDavid du Colombier 							j >>= 8-z->bpc;
260417cabcdSDavid du Colombier 						if(j >= z->palsize)
261417cabcdSDavid du Colombier 							sysfatal("index %d >= palette size %d", j, z->palsize);
262417cabcdSDavid du Colombier 						pixel[3] = pixel[1];	/* alpha */
263417cabcdSDavid du Colombier 						pixel[0] = z->palette[3*j];
264417cabcdSDavid du Colombier 						pixel[1] = z->palette[3*j+1];
265417cabcdSDavid du Colombier 						pixel[2] = z->palette[3*j+2];
2669a747e4fSDavid du Colombier 					}
267417cabcdSDavid du Colombier 					switch(z->chandesc){
268417cabcdSDavid du Colombier 					case CYA16:
269417cabcdSDavid du Colombier 					//	print("%.2x%.2x ", pixel[0], pixel[1]);
270417cabcdSDavid du Colombier 						*w++ = pixel[1];
271417cabcdSDavid du Colombier 						*w++ += (pixel[0]*pixel[1])/255;
272417cabcdSDavid du Colombier 						break;
273417cabcdSDavid du Colombier 					case CRGBA32:
274417cabcdSDavid du Colombier 					//	print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]);
275417cabcdSDavid du Colombier 						*w++ += pixel[3];
276417cabcdSDavid du Colombier 						*w++ += (pixel[2]*pixel[3])/255;
277417cabcdSDavid du Colombier 						*w++ += (pixel[1]*pixel[3])/255;
278417cabcdSDavid du Colombier 						*w++ += (pixel[0]*pixel[3])/255;
279417cabcdSDavid du Colombier 						break;
280417cabcdSDavid du Colombier 					case CRGB24:
281417cabcdSDavid du Colombier 						*w++ = pixel[2];
282417cabcdSDavid du Colombier 						*w++ = pixel[1];
283417cabcdSDavid du Colombier 					case CY:
284417cabcdSDavid du Colombier 						*w++ = pixel[0];
285417cabcdSDavid du Colombier 						break;
286417cabcdSDavid du Colombier 					}
287417cabcdSDavid du Colombier 					w += (adam7[z->pass].dx-1)*z->noutchan;
288417cabcdSDavid du Colombier 				}
289417cabcdSDavid du Colombier 				off += adam7[z->pass].dx;
290417cabcdSDavid du Colombier 				if(off >= (z->y+1)*z->dx){
291417cabcdSDavid du Colombier 					/* finished the line */
292417cabcdSDavid du Colombier 					return;
293417cabcdSDavid du Colombier 				}
294417cabcdSDavid du Colombier 				chan = 0;
2959863c128SDavid du Colombier 			}
2969863c128SDavid du Colombier 		}
297417cabcdSDavid du Colombier 	}
298417cabcdSDavid du Colombier 	sysfatal("scan line too short");
2999863c128SDavid du Colombier }
3009863c128SDavid du Colombier 
3019863c128SDavid du Colombier static int
scanbytes(ZlibW * z)302417cabcdSDavid du Colombier scanbytes(ZlibW *z)
3039863c128SDavid du Colombier {
304417cabcdSDavid du Colombier 	int bits, n, adx, dx;
3059863c128SDavid du Colombier 
306417cabcdSDavid du Colombier 	if(adam7[z->pass].y >= z->dy || adam7[z->pass].x >= z->dx)
307417cabcdSDavid du Colombier 		return 0;
308417cabcdSDavid du Colombier 	adx = adam7[z->pass].dx;
309417cabcdSDavid du Colombier 	dx = z->dx - adam7[z->pass].x;
310417cabcdSDavid du Colombier 	if(dx <= 0)
311417cabcdSDavid du Colombier 		n = 1;
312417cabcdSDavid du Colombier 	else
313417cabcdSDavid du Colombier 		n = (dx+adx-1)/adx;
314417cabcdSDavid du Colombier 	if(n != 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx){
315417cabcdSDavid du Colombier 		print("%d/%d != 1+(%d-1)/%d = %d\n",
316417cabcdSDavid du Colombier 			z->dx - adam7[z->pass].x - 1 + adx, adx,
317417cabcdSDavid du Colombier 			z->dx - (adam7[z->pass].x+1), adam7[z->pass].dx,
318417cabcdSDavid du Colombier 			1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx);
319714fb1e1SDavid du Colombier 	}
320417cabcdSDavid du Colombier 	bits = n*z->bpc*z->nchan;
321417cabcdSDavid du Colombier 	return 1 + (bits+7)/8;
3229a747e4fSDavid du Colombier }
323417cabcdSDavid du Colombier 
324417cabcdSDavid du Colombier static int
nextpass(ZlibW * z)325417cabcdSDavid du Colombier nextpass(ZlibW *z)
326417cabcdSDavid du Colombier {
327417cabcdSDavid du Colombier 	int len;
328417cabcdSDavid du Colombier 
329417cabcdSDavid du Colombier 	memset(z->lastscan, 0, z->scanlen);
330417cabcdSDavid du Colombier 	do{
331417cabcdSDavid du Colombier 		z->pass = (z->pass+1)%8;
332417cabcdSDavid du Colombier 		z->y = adam7[z->pass].y;
333417cabcdSDavid du Colombier 		len = scanbytes(z);
334417cabcdSDavid du Colombier 	}while(len < 2);
335417cabcdSDavid du Colombier 	return len;
336417cabcdSDavid du Colombier }
337417cabcdSDavid du Colombier 
338417cabcdSDavid du Colombier static int
zwrite(void * vz,void * vbuf,int n)339417cabcdSDavid du Colombier zwrite(void *vz, void *vbuf, int n)
340417cabcdSDavid du Colombier {
341417cabcdSDavid du Colombier 	int oldn, m, len;
342417cabcdSDavid du Colombier 	uchar *buf, *t;
343417cabcdSDavid du Colombier 	ZlibW *z;
344417cabcdSDavid du Colombier 
345417cabcdSDavid du Colombier 	z = vz;
346417cabcdSDavid du Colombier 	buf = vbuf;
347417cabcdSDavid du Colombier 	oldn = n;
348417cabcdSDavid du Colombier 
349417cabcdSDavid du Colombier 	len = scanbytes(z);
350417cabcdSDavid du Colombier 	if(len < 2)
351417cabcdSDavid du Colombier 		len = nextpass(z);
352417cabcdSDavid du Colombier 
353417cabcdSDavid du Colombier 	while(n > 0){
354417cabcdSDavid du Colombier 		m = len - z->scanpos;
355417cabcdSDavid du Colombier 		if(m > n){
356417cabcdSDavid du Colombier 			/* save final partial line */
357417cabcdSDavid du Colombier 			memmove(z->scan+z->scanpos, buf, n);
358417cabcdSDavid du Colombier 			z->scanpos += n;
359417cabcdSDavid du Colombier 			break;
360417cabcdSDavid du Colombier 		}
361417cabcdSDavid du Colombier 
362417cabcdSDavid du Colombier 		/* fill line */
363417cabcdSDavid du Colombier 		memmove(z->scan+z->scanpos, buf, m);
364417cabcdSDavid du Colombier 		buf += m;
365417cabcdSDavid du Colombier 		n -= m;
366417cabcdSDavid du Colombier 
367417cabcdSDavid du Colombier 		/* process line */
368417cabcdSDavid du Colombier 		scan(len, z);
369417cabcdSDavid du Colombier 		t = z->scan;
370417cabcdSDavid du Colombier 		z->scan = z->lastscan;
371417cabcdSDavid du Colombier 		z->lastscan = t;
372417cabcdSDavid du Colombier 
373417cabcdSDavid du Colombier 		z->scanpos = 0;
374417cabcdSDavid du Colombier 		z->y += adam7[z->pass].dy;
375417cabcdSDavid du Colombier 		if(z->y >= z->dy)
376417cabcdSDavid du Colombier 			len = nextpass(z);
377417cabcdSDavid du Colombier 	}
378417cabcdSDavid du Colombier 	return oldn;
3799a747e4fSDavid du Colombier }
3809a747e4fSDavid du Colombier 
3819a747e4fSDavid du Colombier static Rawimage*
readslave(Biobuf * b)3829a747e4fSDavid du Colombier readslave(Biobuf *b)
3839a747e4fSDavid du Colombier {
384417cabcdSDavid du Colombier 	char type[5];
385417cabcdSDavid du Colombier 	int bpc, colorfmt, dx, dy, err, n, nchan, nout, useadam7;
386417cabcdSDavid du Colombier 	uchar *buf, *h;
387417cabcdSDavid du Colombier 	Rawimage *image;
3889a747e4fSDavid du Colombier 	ZlibR zr;
3899a747e4fSDavid du Colombier 	ZlibW zw;
3909a747e4fSDavid du Colombier 
3919a747e4fSDavid du Colombier 	buf = pngmalloc(IDATSIZE, 0);
392*9b7bf7dfSDavid du Colombier 	if(Bread(b, buf, sizeof PNGmagic) != sizeof PNGmagic ||
393*9b7bf7dfSDavid du Colombier 	    memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
3949a747e4fSDavid du Colombier 		sysfatal("bad PNGmagic");
3959a747e4fSDavid du Colombier 
3969a747e4fSDavid du Colombier 	n = getchunk(b, type, buf, IDATSIZE);
3979a747e4fSDavid du Colombier 	if(n < 13 || strcmp(type,"IHDR") != 0)
3989a747e4fSDavid du Colombier 		sysfatal("missing IHDR chunk");
3999a747e4fSDavid du Colombier 	h = buf;
400417cabcdSDavid du Colombier 	dx = get4(h);
401417cabcdSDavid du Colombier 	h += 4;
402417cabcdSDavid du Colombier 	dy = get4(h);
403417cabcdSDavid du Colombier 	h += 4;
404417cabcdSDavid du Colombier 	if(dx <= 0 || dy <= 0)
405417cabcdSDavid du Colombier 		sysfatal("impossible image size %dx%d", dx, dy);
4069a747e4fSDavid du Colombier 	if(debug)
407417cabcdSDavid du Colombier 		fprint(2, "readpng %dx%d\n", dx, dy);
4089863c128SDavid du Colombier 
409417cabcdSDavid du Colombier 	bpc = *h++;
410417cabcdSDavid du Colombier 	colorfmt = *h++;
411417cabcdSDavid du Colombier 	nchan = 0;
4127cb50cffSDavid du Colombier 	if(*h++ != 0)
4137cb50cffSDavid du Colombier 		sysfatal("only deflate supported for now [%d]", h[-1]);
4147cb50cffSDavid du Colombier 	if(*h++ != FilterNone)
4157cb50cffSDavid du Colombier 		sysfatal("only FilterNone supported for now [%d]", h[-1]);
416417cabcdSDavid du Colombier 	useadam7 = *h++;
417417cabcdSDavid du Colombier 	USED(h);
4187cb50cffSDavid du Colombier 
4197cb50cffSDavid du Colombier 	image = pngmalloc(sizeof(Rawimage), 1);
420417cabcdSDavid du Colombier 	image->r = Rect(0, 0, dx, dy);
421417cabcdSDavid du Colombier 	nout = 0;
422417cabcdSDavid du Colombier 	switch(colorfmt){
423417cabcdSDavid du Colombier 	case 0:	/* grey */
424417cabcdSDavid du Colombier 		image->nchans = 1;
425417cabcdSDavid du Colombier 		image->chandesc = CY;
426417cabcdSDavid du Colombier 		nout = 1;
427417cabcdSDavid du Colombier 		nchan = 1;
428417cabcdSDavid du Colombier 		break;
429417cabcdSDavid du Colombier 	case 2:	/* rgb */
430417cabcdSDavid du Colombier 		image->nchans = 1;
431417cabcdSDavid du Colombier 		image->chandesc = CRGB24;
432417cabcdSDavid du Colombier 		nout = 3;
433417cabcdSDavid du Colombier 		nchan = 3;
434417cabcdSDavid du Colombier 		break;
435417cabcdSDavid du Colombier 	case 3: /* indexed rgb with PLTE */
436417cabcdSDavid du Colombier 		image->nchans = 1;
437417cabcdSDavid du Colombier 		image->chandesc = CRGB24;
438417cabcdSDavid du Colombier 		nout = 3;
439417cabcdSDavid du Colombier 		nchan = 1;
440417cabcdSDavid du Colombier 		break;
441417cabcdSDavid du Colombier 	case 4:	/* grey+alpha */
442417cabcdSDavid du Colombier 		image->nchans = 1;
443417cabcdSDavid du Colombier 		image->chandesc = CYA16;
444417cabcdSDavid du Colombier 		nout = 2;
445417cabcdSDavid du Colombier 		nchan = 2;
446417cabcdSDavid du Colombier 		break;
447417cabcdSDavid du Colombier 	case 6:	/* rgb+alpha */
448417cabcdSDavid du Colombier 		image->nchans = 1;
449417cabcdSDavid du Colombier 		image->chandesc = CRGBA32;
450417cabcdSDavid du Colombier 		nout = 4;
451417cabcdSDavid du Colombier 		nchan = 4;
452417cabcdSDavid du Colombier 		break;
453417cabcdSDavid du Colombier 	default:
454417cabcdSDavid du Colombier 		sysfatal("unsupported color scheme %d", h[-1]);
455417cabcdSDavid du Colombier 	}
456417cabcdSDavid du Colombier 	image->chanlen = dx*dy*nout;
457417cabcdSDavid du Colombier 	image->chans[0] = pngmalloc(image->chanlen, 0);
458417cabcdSDavid du Colombier 	memset(image->chans[0], 0, image->chanlen);
4597cb50cffSDavid du Colombier 
460417cabcdSDavid du Colombier 	memset(&zr, 0, sizeof zr);
461417cabcdSDavid du Colombier 	zr.w = &zw;
462417cabcdSDavid du Colombier 	zr.io = b;
4639a747e4fSDavid du Colombier 	zr.buf = buf;
4649863c128SDavid du Colombier 
465417cabcdSDavid du Colombier 	memset(&zw, 0, sizeof zw);
466417cabcdSDavid du Colombier 	if(useadam7)
467417cabcdSDavid du Colombier 		zw.pass = 1;
468417cabcdSDavid du Colombier 	zw.data = image->chans[0];
469417cabcdSDavid du Colombier 	zw.ndata = image->chanlen;
470417cabcdSDavid du Colombier 	zw.chandesc = image->chandesc;
471417cabcdSDavid du Colombier 	zw.noutchan = nout;
472417cabcdSDavid du Colombier 
473417cabcdSDavid du Colombier 	zw.dx = dx;
474417cabcdSDavid du Colombier 	zw.dy = dy;
475417cabcdSDavid du Colombier 	zw.scanlen = (nchan*dx*bpc+7)/8+1;
476417cabcdSDavid du Colombier 	zw.scan = pngmalloc(zw.scanlen, 1);
477417cabcdSDavid du Colombier 	zw.lastscan = pngmalloc(zw.scanlen, 1);
478417cabcdSDavid du Colombier 	zw.nchan = nchan;
479417cabcdSDavid du Colombier 	zw.bpc = bpc;
4809863c128SDavid du Colombier 
4819a747e4fSDavid du Colombier 	err = inflatezlib(&zw, zwrite, &zr, zread);
4829863c128SDavid du Colombier 
4837cb50cffSDavid du Colombier 	if(err)
484417cabcdSDavid du Colombier 		sysfatal("inflatezlib %s\n", flateerr(err));
4857cb50cffSDavid du Colombier 
4869a747e4fSDavid du Colombier 	free(buf);
4879863c128SDavid du Colombier 	free(zw.scan);
488417cabcdSDavid du Colombier 	free(zw.lastscan);
4899a747e4fSDavid du Colombier 	return image;
4909a747e4fSDavid du Colombier }
4919a747e4fSDavid du Colombier 
4929a747e4fSDavid du Colombier Rawimage**
Breadpng(Biobuf * b,int colorspace)4939a747e4fSDavid du Colombier Breadpng(Biobuf *b, int colorspace)
4949a747e4fSDavid du Colombier {
495417cabcdSDavid du Colombier 	Rawimage **array, *r;
4969a747e4fSDavid du Colombier 
4979a747e4fSDavid du Colombier 	if(colorspace != CRGB){
4989a747e4fSDavid du Colombier 		werrstr("ReadPNG: unknown color space %d", colorspace);
4999a747e4fSDavid du Colombier 		return nil;
5009a747e4fSDavid du Colombier 	}
5019a747e4fSDavid du Colombier 	pnginit();
5029a747e4fSDavid du Colombier 	array = malloc(2*sizeof(*array));
5039a747e4fSDavid du Colombier 	if(array==nil)
5049a747e4fSDavid du Colombier 		return nil;
5059a747e4fSDavid du Colombier 	r = readslave(b);
5069a747e4fSDavid du Colombier 	array[0] = r;
5079a747e4fSDavid du Colombier 	array[1] = nil;
5089a747e4fSDavid du Colombier 	return array;
5099a747e4fSDavid du Colombier }
5109a747e4fSDavid du Colombier 
5119a747e4fSDavid du Colombier Rawimage**
readpng(int fd,int colorspace)5129a747e4fSDavid du Colombier readpng(int fd, int colorspace)
5139a747e4fSDavid du Colombier {
5147cb50cffSDavid du Colombier 	Biobuf b;
515417cabcdSDavid du Colombier 	Rawimage **a;
5169a747e4fSDavid du Colombier 
5179a747e4fSDavid du Colombier 	if(Binit(&b, fd, OREAD) < 0)
5189a747e4fSDavid du Colombier 		return nil;
5199a747e4fSDavid du Colombier 	a = Breadpng(&b, colorspace);
5209a747e4fSDavid du Colombier 	Bterm(&b);
5219a747e4fSDavid du Colombier 	return a;
5229a747e4fSDavid du Colombier }
523