plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

readpng.c (8713B)


      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{  IDATSIZE=1000000,
     12 	/* filtering algorithms, supposedly increase compression */
     13 	FilterNone =	0,	/* new[x][y] = buf[x][y] */
     14 	FilterSub	=	1,	/* new[x][y] = buf[x][y] + new[x-1][y] */
     15 	FilterUp	=	2,	/* new[x][y] = buf[x][y] + new[x][y-1] */
     16 	FilterAvg	=	3,	/* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */
     17 	FilterPaeth=	4,	/* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */
     18 	FilterLast	=	5,
     19 	PropertyBit =	1<<5
     20 };
     21 
     22 
     23 typedef struct ZlibW{
     24 	uchar *chan[4]; /* Rawimage channels */
     25 	uchar *scan;	/* new scanline */
     26 	uchar *pscan;	/* previous scanline */
     27 	int scanl;		/* scan len */
     28 	int scanp;		/* scan pos */
     29 	int nchan;		/* number of input chans */
     30 	int npix;		/* pixels read so far */
     31 	int	chanl;		/* number of bytes allocated to chan[x] */
     32 	int scanpix;
     33 	int bpp;		/* bits per sample */
     34 	int palsize;
     35 	int row;		/* current scanline number */
     36 	uchar palette[3*256];
     37 } ZlibW;
     38 
     39 typedef struct ZlibR{
     40 	Biobuf *bi;
     41 	uchar *buf;
     42 	uchar *b;	/* next byte to decompress */
     43 	uchar *e;	/* past end of buf */
     44 	ZlibW *w;
     45 } ZlibR;
     46 
     47 static uint32 *crctab;
     48 static uchar PNGmagic[] = {137,80,78,71,13,10,26,10};
     49 static char memerr[] = "ReadPNG: malloc failed: %r";
     50 
     51 static uint32
     52 get4(uchar *a)
     53 {
     54 	return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
     55 }
     56 
     57 static
     58 void
     59 pnginit(void)
     60 {
     61 	static int inited;
     62 
     63 	if(inited)
     64 		return;
     65 	inited = 1;
     66 	crctab = mkcrctab(0xedb88320);
     67 	if(crctab == nil)
     68 		sysfatal("mkcrctab error");
     69 	inflateinit();
     70 }
     71 
     72 static
     73 void*
     74 pngmalloc(ulong n, int clear)
     75 {
     76 	void *p;
     77 
     78 	p = malloc(n);
     79 	if(p == nil)
     80 		sysfatal(memerr);
     81 	if(clear)
     82 		memset(p, 0, n);
     83 	return p;
     84 }
     85 
     86 static int
     87 getchunk(Biobuf *b, char *type, uchar *d, int m)
     88 {
     89 	uchar buf[8];
     90 	uint32 crc = 0, crc2;
     91 	int n, nr;
     92 
     93 	if(Bread(b, buf, 8) != 8)
     94 		return -1;
     95 	n = get4(buf);
     96 	memmove(type, buf+4, 4);
     97 	type[4] = 0;
     98 	if(n > m)
     99 		sysfatal("getchunk needed %d, had %d", n, m);
    100 	nr = Bread(b, d, n);
    101 	if(nr != n)
    102 		sysfatal("getchunk read %d, expected %d", nr, n);
    103 	crc = blockcrc(crctab, crc, type, 4);
    104 	crc = blockcrc(crctab, crc, d, n);
    105 	if(Bread(b, buf, 4) != 4)
    106 		sysfatal("getchunk tlr failed");
    107 	crc2 = get4(buf);
    108 	if(crc != crc2)
    109 		sysfatal("getchunk crc failed");
    110 	return n;
    111 }
    112 
    113 static int
    114 zread(void *va)
    115 {
    116 	ZlibR *z = va;
    117 	char type[5];
    118 	int n;
    119 
    120 	if(z->b >= z->e){
    121 refill_buffer:
    122 		z->b = z->buf;
    123 		n = getchunk(z->bi, type, z->b, IDATSIZE);
    124 		if(n < 0 || strcmp(type, "IEND") == 0)
    125 			return -1;
    126 		z->e = z->b + n;
    127 		if(!strcmp(type,"PLTE")) {
    128 			if (n < 3 || n > 3*256 || n%3)
    129 				sysfatal("invalid PLTE chunk len %d", n);
    130 			memcpy(z->w->palette, z->b, n);
    131 			z->w->palsize = n/3;
    132 			goto refill_buffer;
    133 		}
    134 		if(type[0] & PropertyBit)
    135 			goto refill_buffer;  /* skip auxiliary chunks for now */
    136 		if(strcmp(type,"IDAT")) {
    137 			sysfatal("unrecognized mandatory chunk %s", type);
    138 			goto refill_buffer;
    139 		}
    140 	}
    141 	return *z->b++;
    142 }
    143 
    144 static uchar
    145 paeth(uchar a, uchar b, uchar c)
    146 {
    147 	int p, pa, pb, pc;
    148 
    149 	p = (int)a + (int)b - (int)c;
    150 	pa = abs(p - (int)a);
    151 	pb = abs(p - (int)b);
    152 	pc = abs(p - (int)c);
    153 
    154 	if(pa <= pb && pa <= pc)
    155 		return a;
    156 	else if(pb <= pc)
    157 		return b;
    158 	return c;
    159 }
    160 
    161 static void
    162 unfilter(int alg, uchar *buf, uchar *up, int len, int bypp)
    163 {
    164 	int i;
    165 	switch(alg){
    166 	case FilterNone:
    167 		break;
    168 
    169 	case FilterSub:
    170 		for (i = bypp; i < len; ++i)
    171 			buf[i] += buf[i-bypp];
    172 		break;
    173 
    174 	case FilterUp:
    175 		for (i = 0; i < len; ++i)
    176 			buf[i] += up[i];
    177 		break;
    178 
    179 	case FilterAvg:
    180 		for (i = 0; i < bypp; ++i)
    181 			buf[i] += (0+up[i])/2;
    182 		for (; i < len; ++i)
    183 			buf[i] += (buf[i-bypp]+up[i])/2;
    184 		break;
    185 
    186 	case FilterPaeth:
    187 		for (i = 0; i < bypp; ++i)
    188 			buf[i] += paeth(0, up[i], 0);
    189 		for (; i < len; ++i)
    190 			buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]);
    191 		break;
    192 	default:
    193 		sysfatal("unknown filtering scheme %d\n", alg);
    194 	}
    195 }
    196 
    197 static void
    198 convertpix(ZlibW *z, uchar *pixel, uchar *r, uchar *g, uchar *b)
    199 {
    200 	int off;
    201 	switch (z->nchan) {
    202 	case 1:	/* gray or indexed */
    203 	case 2:	/* gray+alpha */
    204 		if (z->bpp < 8)
    205 			pixel[0] >>= 8-z->bpp;
    206 		if (pixel[0] > z->palsize)
    207 			sysfatal("index %d out of bounds %d", pixel[0], z->palsize);
    208 		off = 3*pixel[0];
    209 		*r = z->palette[off];
    210 		*g = z->palette[off+1];
    211 		*b = z->palette[off+2];
    212 		break;
    213 	case 3:	/* rgb */
    214 	case 4:	/* rgb+alpha */
    215 		*r = pixel[0];
    216 		*g = pixel[1];
    217 		*b = pixel[2];
    218 		break;
    219 	default:
    220 		sysfatal("bad number of channels: %d", z->nchan);
    221 	}
    222 }
    223 
    224 static void
    225 scan(ZlibW *z)
    226 {
    227 	uchar *p;
    228 	int i, bit, n, ch, nch, pd;
    229 	uchar cb;
    230 	uchar pixel[4];
    231 
    232 	p = z->scan;
    233 	nch = z->nchan;
    234 
    235 	unfilter(p[0], p+1, z->pscan+1, z->scanl-1, (nch*z->bpp+7)/8);
    236 /*
    237  *	Adam7 interlace order.
    238  *	1 6 4 6 2 6 4 6
    239  *	7 7 7 7 7 7 7 7
    240  *	5 6 5 6 5 6 5 6
    241  *	7 7 7 7 7 7 7 7
    242  *	3 6 4 6 3 6 4 6
    243  *	7 7 7 7 7 7 7 7
    244  *	5 6 5 6 5 6 5 6
    245  *	7 7 7 7 7 7 7 7
    246  */
    247 	ch = 0;
    248 	n = 0;
    249 	cb = 128;
    250 	pd = z->row * z->scanpix;
    251 	for (i = 1; i < z->scanl; ++i)
    252 		for (bit = 128; bit > 0; bit /= 2) {
    253 
    254 			pixel[ch] &= ~cb;
    255 			if (p[i] & bit)
    256 				pixel[ch] |= cb;
    257 
    258 			cb >>= 1;
    259 
    260 			if (++n == z->bpp) {
    261 				cb = 128;
    262 				n = 0;
    263 				ch++;
    264 			}
    265 			if (ch == nch) {
    266 				if (z->npix++ < z->chanl)
    267 					convertpix(z,pixel,z->chan[0]+pd,z->chan[1]+pd,z->chan[2]+pd);
    268 				pd++;
    269 				if (pd % z->scanpix == 0)
    270 					goto out;
    271 				ch = 0;
    272 			}
    273 		}
    274 out: ;
    275 }
    276 
    277 static int
    278 zwrite(void *va, void *vb, int n)
    279 {
    280 	ZlibW *z = va;
    281 	uchar *buf = vb;
    282 	int i, j;
    283 
    284 	j = z->scanp;
    285 	for (i = 0; i < n; ++i) {
    286 		z->scan[j++] = buf[i];
    287 		if (j == z->scanl) {
    288 			uchar *tp;
    289 			scan(z);
    290 
    291 			tp = z->scan;
    292 			z->scan = z->pscan;
    293 			z->pscan = tp;
    294 			z->row++;
    295 			j = 0;
    296 		}
    297 	}
    298 	z->scanp = j;
    299 
    300 	return n;
    301 }
    302 
    303 static Rawimage*
    304 readslave(Biobuf *b)
    305 {
    306 	ZlibR zr;
    307 	ZlibW zw;
    308 	Rawimage *image;
    309 	char type[5];
    310 	uchar *buf, *h;
    311 	int k, n, nrow, ncol, err, bpp, nch;
    312 
    313 	zr.w = &zw;
    314 
    315 	buf = pngmalloc(IDATSIZE, 0);
    316 	Bread(b, buf, sizeof PNGmagic);
    317 	if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0)
    318 		sysfatal("bad PNGmagic");
    319 
    320 	n = getchunk(b, type, buf, IDATSIZE);
    321 	if(n < 13 || strcmp(type,"IHDR") != 0)
    322 		sysfatal("missing IHDR chunk");
    323 	h = buf;
    324 	ncol = get4(h);  h += 4;
    325 	nrow = get4(h);  h += 4;
    326 	if(ncol <= 0 || nrow <= 0)
    327 		sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol);
    328 	if(debug)
    329 		fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol);
    330 
    331 	bpp = *h++;
    332 	nch = 0;
    333 	switch (*h++) {
    334 	case 0:	/* grey */
    335 		nch = 1;
    336 		break;
    337 	case 2:	/* rgb */
    338 		nch = 3;
    339 		break;
    340 	case 3: /* indexed rgb with PLTE */
    341 		nch = 1;
    342 		break;
    343 	case 4:	/* grey+alpha */
    344 		nch = 2;
    345 		break;
    346 	case 6:	/* rgb+alpha */
    347 		nch = 4;
    348 		break;
    349 	default:
    350 		sysfatal("unsupported color scheme %d", h[-1]);
    351 	}
    352 
    353 	/* generate default palette for grayscale */
    354 	zw.palsize = 256;
    355 	if (nch < 3 && bpp < 9)
    356 		zw.palsize = 1<<bpp;
    357 	for (k = 0; k < zw.palsize; ++k) {
    358 		zw.palette[3*k] = (k*255)/(zw.palsize-1);
    359 		zw.palette[3*k+1] = (k*255)/(zw.palsize-1);
    360 		zw.palette[3*k+2] = (k*255)/(zw.palsize-1);
    361 	}
    362 
    363 	if(*h++ != 0)
    364 		sysfatal("only deflate supported for now [%d]", h[-1]);
    365 	if(*h++ != FilterNone)
    366 		sysfatal("only FilterNone supported for now [%d]", h[-1]);
    367 	if(*h != 0)
    368 		sysfatal("only non-interlaced supported for now [%d]", h[-1]);
    369 
    370 	image = pngmalloc(sizeof(Rawimage), 1);
    371 	image->r = Rect(0, 0, ncol, nrow);
    372 	image->cmap = nil;
    373 	image->cmaplen = 0;
    374 	image->chanlen = ncol*nrow;
    375 	image->fields = 0;
    376 	image->gifflags = 0;
    377 	image->gifdelay = 0;
    378 	image->giftrindex = 0;
    379 	image->chandesc = CRGB;
    380 	image->nchans = 3;
    381 
    382 	zw.chanl = ncol*nrow;
    383 	zw.npix = 0;
    384 	for(k=0; k<4; k++)
    385 		image->chans[k] = zw.chan[k] = pngmalloc(ncol*nrow, 1);
    386 
    387 	zr.bi = b;
    388 	zr.buf = buf;
    389 	zr.b = zr.e = buf + IDATSIZE;
    390 
    391 	zw.scanp = 0;
    392 	zw.row = 0;
    393 	zw.scanpix = ncol;
    394 	zw.scanl = (nch*ncol*bpp+7)/8+1;
    395 	zw.scan = pngmalloc(zw.scanl, 1);
    396 	zw.pscan = pngmalloc(zw.scanl, 1);
    397 	zw.nchan = nch;
    398 	zw.bpp = bpp;
    399 
    400 	err = inflatezlib(&zw, zwrite, &zr, zread);
    401 
    402 	if (zw.npix > zw.chanl)
    403 		fprint(2, "tried to overflow by %d pix\n", zw.npix - zw.chanl);
    404 
    405 
    406 	if(err)
    407 		sysfatal("inflatezlib %s\n", flateerr(err));
    408 
    409 	free(image->chans[3]);
    410 	image->chans[3] = nil;
    411 	free(buf);
    412 	free(zw.scan);
    413 	free(zw.pscan);
    414 	return image;
    415 }
    416 
    417 Rawimage**
    418 Breadpng(Biobuf *b, int colorspace)
    419 {
    420 	Rawimage *r, **array;
    421 	char buf[ERRMAX];
    422 
    423 	buf[0] = '\0';
    424 	if(colorspace != CRGB){
    425 		errstr(buf, sizeof buf);	/* throw it away */
    426 		werrstr("ReadPNG: unknown color space %d", colorspace);
    427 		return nil;
    428 	}
    429 	pnginit();
    430 	array = malloc(2*sizeof(*array));
    431 	if(array==nil)
    432 		return nil;
    433 	errstr(buf, sizeof buf);	/* throw it away */
    434 	r = readslave(b);
    435 	array[0] = r;
    436 	array[1] = nil;
    437 	return array;
    438 }
    439 
    440 Rawimage**
    441 readpng(int fd, int colorspace)
    442 {
    443 	Rawimage** a;
    444 	Biobuf b;
    445 
    446 	if(Binit(&b, fd, OREAD) < 0)
    447 		return nil;
    448 	a = Breadpng(&b, colorspace);
    449 	Bterm(&b);
    450 	return a;
    451 }