xref: /plan9/sys/src/cmd/jpg/readpng.c (revision 9b7bf7df4595c26f1e9b67beb0c6e44c9876fb05)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <bio.h>
5 #include <flate.h>
6 #include <draw.h>
7 #include "imagefile.h"
8 
9 int debug;
10 
11 enum
12 {
13 	IDATSIZE = 1000000,
14 
15 	/* filtering algorithms */
16 	FilterNone =	0,	/* new[x][y] = buf[x][y] */
17 	FilterSub =	1,	/* new[x][y] = buf[x][y] + new[x-1][y] */
18 	FilterUp =	2,	/* new[x][y] = buf[x][y] + new[x][y-1] */
19 	FilterAvg =	3,	/* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */
20 	FilterPaeth =	4,	/* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
21 	FilterLast =	5,
22 
23 	PropertyBit = 1<<5,
24 };
25 
26 typedef struct ZlibR ZlibR;
27 typedef struct ZlibW ZlibW;
28 
29 struct ZlibW
30 {
31 	uchar *data;		/* Rawimage data */
32 	int ndata;
33 	int noutchan;
34 	int chandesc;
35 	int nchan;
36 
37 	uchar *scan;		/* new scanline */
38 	uchar *lastscan;	/* previous scan line */
39 	int scanlen;		/* scan line length */
40 	int scanpos;		/* scan position */
41 
42 	int dx;			/* width of image */
43 	int dy;			/* height of image */
44 	int bpc;			/* bits per channel (per pixel) */
45 	int y;				/* current scan line */
46 	int pass;			/* adam7 pass#; 0 means no adam7 */
47 	uchar palette[3*256];	/* color palette */
48 	int palsize;		/* number of palette entries */
49 };
50 
51 struct ZlibR
52 {
53 	Biobuf *io;		/* input buffer */
54 	uchar *buf;		/* malloc'ed staging buffer */
55 	uchar *p;			/* next byte to decompress */
56 	uchar *e;			/* end of buffer */
57 	ZlibW *w;
58 };
59 
60 static ulong *crctab;
61 static uchar PNGmagic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n'};
62 
63 static ulong
get4(uchar * a)64 get4(uchar *a)
65 {
66 	return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
67 }
68 
69 static
70 void
pnginit(void)71 pnginit(void)
72 {
73 	static int inited;
74 
75 	if(inited)
76 		return;
77 	inited = 1;
78 	crctab = mkcrctab(0xedb88320);
79 	if(crctab == nil)
80 		sysfatal("mkcrctab error");
81 	inflateinit();
82 }
83 
84 static
85 void*
pngmalloc(ulong n,int clear)86 pngmalloc(ulong n, int clear)
87 {
88 	void *p;
89 
90 	p = mallocz(n, clear);
91 	if(p == nil)
92 		sysfatal("malloc: %r");
93 	return p;
94 }
95 
96 static int
getchunk(Biobuf * b,char * type,uchar * d,int m)97 getchunk(Biobuf *b, char *type, uchar *d, int m)
98 {
99 	uchar buf[8];
100 	ulong crc = 0, crc2;
101 	int n, nr;
102 
103 	if(Bread(b, buf, 8) != 8)
104 		return -1;
105 	n = get4(buf);
106 	memmove(type, buf+4, 4);
107 	type[4] = 0;
108 	if(n > m)
109 		sysfatal("getchunk needed %d, had %d", n, m);
110 	nr = Bread(b, d, n);
111 	if(nr != n)
112 		sysfatal("getchunk read %d, expected %d", nr, n);
113 	crc = blockcrc(crctab, crc, type, 4);
114 	crc = blockcrc(crctab, crc, d, n);
115 	if(Bread(b, buf, 4) != 4)
116 		sysfatal("getchunk tlr failed");
117 	crc2 = get4(buf);
118 	if(crc != crc2)
119 		sysfatal("getchunk crc failed");
120 	return n;
121 }
122 
123 static int
zread(void * va)124 zread(void *va)
125 {
126 	ZlibR *z = va;
127 	char type[5];
128 	int n;
129 
130 	if(z->p >= z->e){
131 	Again:
132 		z->p = z->buf;
133 		z->e = z->p;
134 		n = getchunk(z->io, type, z->p, IDATSIZE);
135 		if(n < 0 || strcmp(type, "IEND") == 0)
136 			return -1;
137 		z->e = z->p + n;
138 		if(!strcmp(type,"PLTE")){
139 			if(n < 3 || n > 3*256 || n%3)
140 				sysfatal("invalid PLTE chunk len %d", n);
141 			memcpy(z->w->palette, z->p, n);
142 			z->w->palsize = 256;
143 			goto Again;
144 		}
145 		if(type[0] & PropertyBit)
146 			goto Again;  /* skip auxiliary chunks fornow */
147 		if(strcmp(type,"IDAT")){
148 			sysfatal("unrecognized mandatory chunk %s", type);
149 			goto Again;
150 		}
151 	}
152 	return *z->p++;
153 }
154 
155 static uchar
paeth(uchar a,uchar b,uchar c)156 paeth(uchar a, uchar b, uchar c)
157 {
158 	int p, pa, pb, pc;
159 
160 	p = a + b - c;
161 	pa = abs(p - a);
162 	pb = abs(p - b);
163 	pc = abs(p - c);
164 
165 	if(pa <= pb && pa <= pc)
166 		return a;
167 	else if(pb <= pc)
168 		return b;
169 	return c;
170 }
171 
172 static void
unfilter(int alg,uchar * buf,uchar * up,int len,int bypp)173 unfilter(int alg, uchar *buf, uchar *up, int len, int bypp)
174 {
175 	int i;
176 
177 	switch(alg){
178 	case FilterNone:
179 		break;
180 
181 	case FilterSub:
182 		for(i = bypp; i < len; ++i)
183 			buf[i] += buf[i-bypp];
184 		break;
185 
186 	case FilterUp:
187 		for(i = 0; i < len; ++i)
188 			buf[i] += up[i];
189 		break;
190 
191 	case FilterAvg:
192 		for(i = 0; i < bypp; ++i)
193 			buf[i] += (0+up[i])/2;
194 		for(; i < len; ++i)
195 			buf[i] += (buf[i-bypp]+up[i])/2;
196 		break;
197 
198 	case FilterPaeth:
199 		for(i = 0; i < bypp; ++i)
200 			buf[i] += paeth(0, up[i], 0);
201 		for(; i < len; ++i)
202 			buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]);
203 		break;
204 
205 	default:
206 		sysfatal("unknown filtering scheme %d\n", alg);
207 	}
208 }
209 
210 struct {
211 	int x;
212 	int y;
213 	int dx;
214 	int dy;
215 } adam7[] = {
216 	{0,0,1,1},	/* eve alone */
217 	{0,0,8,8},	/* pass 1 */
218 	{4,0,8,8},	/* pass 2 */
219 	{0,4,4,8},	/* pass 3 */
220 	{2,0,4,4},	/* pass 4 */
221 	{0,2,2,4},	/* pass 5 */
222 	{1,0,2,2},	/* pass 6 */
223 	{0,1,1,2},	/* pass 7 */
224 };
225 
226 static void
scan(int len,ZlibW * z)227 scan(int len, ZlibW *z)
228 {
229 	int chan, i, j, nbit, off, val;
230 	uchar pixel[4], *p, *w;
231 
232 	unfilter(z->scan[0], z->scan+1, z->lastscan+1, len-1, (z->nchan*z->bpc+7)/8);
233 
234 	/*
235 	 * loop over raw bits extracting pixel values and converting to 8-bit
236 	 */
237 	nbit = 0;
238 	chan = 0;
239 	val = 0;
240 	off = z->y*z->dx + adam7[z->pass].x;
241 	w = z->data + z->noutchan*off;
242 	p = z->scan+1;	/* skip alg byte */
243 	len--;
244 	for(i=0; i<len*8; i++){
245 		val <<= 1;
246 		if(p[i>>3] & (1<<(7-(i&7))))
247 			val++;
248 		if(++nbit == z->bpc){
249 			/* finished the value */
250 			pixel[chan++] = (val*255)/((1<<z->bpc)-1);
251 			val = 0;
252 			nbit = 0;
253 			if(chan == z->nchan){
254 				/* finished the pixel */
255 				if(off < z->dx*z->dy){
256 					if(z->nchan < 3 && z->palsize){
257 						j = pixel[0];
258 						if(z->bpc < 8)
259 							j >>= 8-z->bpc;
260 						if(j >= z->palsize)
261 							sysfatal("index %d >= palette size %d", j, z->palsize);
262 						pixel[3] = pixel[1];	/* alpha */
263 						pixel[0] = z->palette[3*j];
264 						pixel[1] = z->palette[3*j+1];
265 						pixel[2] = z->palette[3*j+2];
266 					}
267 					switch(z->chandesc){
268 					case CYA16:
269 					//	print("%.2x%.2x ", pixel[0], pixel[1]);
270 						*w++ = pixel[1];
271 						*w++ += (pixel[0]*pixel[1])/255;
272 						break;
273 					case CRGBA32:
274 					//	print("%.2x%.2x%.2x%.2x ", pixel[0], pixel[1], pixel[2], pixel[3]);
275 						*w++ += pixel[3];
276 						*w++ += (pixel[2]*pixel[3])/255;
277 						*w++ += (pixel[1]*pixel[3])/255;
278 						*w++ += (pixel[0]*pixel[3])/255;
279 						break;
280 					case CRGB24:
281 						*w++ = pixel[2];
282 						*w++ = pixel[1];
283 					case CY:
284 						*w++ = pixel[0];
285 						break;
286 					}
287 					w += (adam7[z->pass].dx-1)*z->noutchan;
288 				}
289 				off += adam7[z->pass].dx;
290 				if(off >= (z->y+1)*z->dx){
291 					/* finished the line */
292 					return;
293 				}
294 				chan = 0;
295 			}
296 		}
297 	}
298 	sysfatal("scan line too short");
299 }
300 
301 static int
scanbytes(ZlibW * z)302 scanbytes(ZlibW *z)
303 {
304 	int bits, n, adx, dx;
305 
306 	if(adam7[z->pass].y >= z->dy || adam7[z->pass].x >= z->dx)
307 		return 0;
308 	adx = adam7[z->pass].dx;
309 	dx = z->dx - adam7[z->pass].x;
310 	if(dx <= 0)
311 		n = 1;
312 	else
313 		n = (dx+adx-1)/adx;
314 	if(n != 1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx){
315 		print("%d/%d != 1+(%d-1)/%d = %d\n",
316 			z->dx - adam7[z->pass].x - 1 + adx, adx,
317 			z->dx - (adam7[z->pass].x+1), adam7[z->pass].dx,
318 			1 + (z->dx - (adam7[z->pass].x+1)) / adam7[z->pass].dx);
319 	}
320 	bits = n*z->bpc*z->nchan;
321 	return 1 + (bits+7)/8;
322 }
323 
324 static int
nextpass(ZlibW * z)325 nextpass(ZlibW *z)
326 {
327 	int len;
328 
329 	memset(z->lastscan, 0, z->scanlen);
330 	do{
331 		z->pass = (z->pass+1)%8;
332 		z->y = adam7[z->pass].y;
333 		len = scanbytes(z);
334 	}while(len < 2);
335 	return len;
336 }
337 
338 static int
zwrite(void * vz,void * vbuf,int n)339 zwrite(void *vz, void *vbuf, int n)
340 {
341 	int oldn, m, len;
342 	uchar *buf, *t;
343 	ZlibW *z;
344 
345 	z = vz;
346 	buf = vbuf;
347 	oldn = n;
348 
349 	len = scanbytes(z);
350 	if(len < 2)
351 		len = nextpass(z);
352 
353 	while(n > 0){
354 		m = len - z->scanpos;
355 		if(m > n){
356 			/* save final partial line */
357 			memmove(z->scan+z->scanpos, buf, n);
358 			z->scanpos += n;
359 			break;
360 		}
361 
362 		/* fill line */
363 		memmove(z->scan+z->scanpos, buf, m);
364 		buf += m;
365 		n -= m;
366 
367 		/* process line */
368 		scan(len, z);
369 		t = z->scan;
370 		z->scan = z->lastscan;
371 		z->lastscan = t;
372 
373 		z->scanpos = 0;
374 		z->y += adam7[z->pass].dy;
375 		if(z->y >= z->dy)
376 			len = nextpass(z);
377 	}
378 	return oldn;
379 }
380 
381 static Rawimage*
readslave(Biobuf * b)382 readslave(Biobuf *b)
383 {
384 	char type[5];
385 	int bpc, colorfmt, dx, dy, err, n, nchan, nout, useadam7;
386 	uchar *buf, *h;
387 	Rawimage *image;
388 	ZlibR zr;
389 	ZlibW zw;
390 
391 	buf = pngmalloc(IDATSIZE, 0);
392 	if(Bread(b, buf, sizeof PNGmagic) != sizeof PNGmagic ||
393 	    memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
394 		sysfatal("bad PNGmagic");
395 
396 	n = getchunk(b, type, buf, IDATSIZE);
397 	if(n < 13 || strcmp(type,"IHDR") != 0)
398 		sysfatal("missing IHDR chunk");
399 	h = buf;
400 	dx = get4(h);
401 	h += 4;
402 	dy = get4(h);
403 	h += 4;
404 	if(dx <= 0 || dy <= 0)
405 		sysfatal("impossible image size %dx%d", dx, dy);
406 	if(debug)
407 		fprint(2, "readpng %dx%d\n", dx, dy);
408 
409 	bpc = *h++;
410 	colorfmt = *h++;
411 	nchan = 0;
412 	if(*h++ != 0)
413 		sysfatal("only deflate supported for now [%d]", h[-1]);
414 	if(*h++ != FilterNone)
415 		sysfatal("only FilterNone supported for now [%d]", h[-1]);
416 	useadam7 = *h++;
417 	USED(h);
418 
419 	image = pngmalloc(sizeof(Rawimage), 1);
420 	image->r = Rect(0, 0, dx, dy);
421 	nout = 0;
422 	switch(colorfmt){
423 	case 0:	/* grey */
424 		image->nchans = 1;
425 		image->chandesc = CY;
426 		nout = 1;
427 		nchan = 1;
428 		break;
429 	case 2:	/* rgb */
430 		image->nchans = 1;
431 		image->chandesc = CRGB24;
432 		nout = 3;
433 		nchan = 3;
434 		break;
435 	case 3: /* indexed rgb with PLTE */
436 		image->nchans = 1;
437 		image->chandesc = CRGB24;
438 		nout = 3;
439 		nchan = 1;
440 		break;
441 	case 4:	/* grey+alpha */
442 		image->nchans = 1;
443 		image->chandesc = CYA16;
444 		nout = 2;
445 		nchan = 2;
446 		break;
447 	case 6:	/* rgb+alpha */
448 		image->nchans = 1;
449 		image->chandesc = CRGBA32;
450 		nout = 4;
451 		nchan = 4;
452 		break;
453 	default:
454 		sysfatal("unsupported color scheme %d", h[-1]);
455 	}
456 	image->chanlen = dx*dy*nout;
457 	image->chans[0] = pngmalloc(image->chanlen, 0);
458 	memset(image->chans[0], 0, image->chanlen);
459 
460 	memset(&zr, 0, sizeof zr);
461 	zr.w = &zw;
462 	zr.io = b;
463 	zr.buf = buf;
464 
465 	memset(&zw, 0, sizeof zw);
466 	if(useadam7)
467 		zw.pass = 1;
468 	zw.data = image->chans[0];
469 	zw.ndata = image->chanlen;
470 	zw.chandesc = image->chandesc;
471 	zw.noutchan = nout;
472 
473 	zw.dx = dx;
474 	zw.dy = dy;
475 	zw.scanlen = (nchan*dx*bpc+7)/8+1;
476 	zw.scan = pngmalloc(zw.scanlen, 1);
477 	zw.lastscan = pngmalloc(zw.scanlen, 1);
478 	zw.nchan = nchan;
479 	zw.bpc = bpc;
480 
481 	err = inflatezlib(&zw, zwrite, &zr, zread);
482 
483 	if(err)
484 		sysfatal("inflatezlib %s\n", flateerr(err));
485 
486 	free(buf);
487 	free(zw.scan);
488 	free(zw.lastscan);
489 	return image;
490 }
491 
492 Rawimage**
Breadpng(Biobuf * b,int colorspace)493 Breadpng(Biobuf *b, int colorspace)
494 {
495 	Rawimage **array, *r;
496 
497 	if(colorspace != CRGB){
498 		werrstr("ReadPNG: unknown color space %d", colorspace);
499 		return nil;
500 	}
501 	pnginit();
502 	array = malloc(2*sizeof(*array));
503 	if(array==nil)
504 		return nil;
505 	r = readslave(b);
506 	array[0] = r;
507 	array[1] = nil;
508 	return array;
509 }
510 
511 Rawimage**
readpng(int fd,int colorspace)512 readpng(int fd, int colorspace)
513 {
514 	Biobuf b;
515 	Rawimage **a;
516 
517 	if(Binit(&b, fd, OREAD) < 0)
518 		return nil;
519 	a = Breadpng(&b, colorspace);
520 	Bterm(&b);
521 	return a;
522 }
523