|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <ctype.h> | 
|  | #include <bio.h> | 
|  | #include <flate.h> | 
|  | #include <draw.h> | 
|  | #include "imagefile.h" | 
|  |  | 
|  | int debug; | 
|  |  | 
|  | enum{  IDATSIZE=1000000, | 
|  | /* filtering algorithms, supposedly increase compression */ | 
|  | FilterNone =	0,	/* new[x][y] = buf[x][y] */ | 
|  | FilterSub	=	1,	/* new[x][y] = buf[x][y] + new[x-1][y] */ | 
|  | FilterUp	=	2,	/* new[x][y] = buf[x][y] + new[x][y-1] */ | 
|  | FilterAvg	=	3,	/* new[x][y] = buf[x][y] + (new[x-1][y]+new[x][y-1])/2 */ | 
|  | FilterPaeth=	4,	/* new[x][y] = buf[x][y] + paeth(new[x-1][y],new[x][y-1],new[x-1][y-1]) */ | 
|  | FilterLast	=	5, | 
|  | PropertyBit =	1<<5 | 
|  | }; | 
|  |  | 
|  |  | 
|  | typedef struct ZlibW{ | 
|  | uchar *chan[4]; /* Rawimage channels */ | 
|  | uchar *scan;	/* new scanline */ | 
|  | uchar *pscan;	/* previous scanline */ | 
|  | int scanl;		/* scan len */ | 
|  | int scanp;		/* scan pos */ | 
|  | int nchan;		/* number of input chans */ | 
|  | int npix;		/* pixels read so far */ | 
|  | int	chanl;		/* number of bytes allocated to chan[x] */ | 
|  | int scanpix; | 
|  | int bpp;		/* bits per sample */ | 
|  | int palsize; | 
|  | int row;		/* current scanline number */ | 
|  | uchar palette[3*256]; | 
|  | } ZlibW; | 
|  |  | 
|  | typedef struct ZlibR{ | 
|  | Biobuf *bi; | 
|  | uchar *buf; | 
|  | uchar *b;	/* next byte to decompress */ | 
|  | uchar *e;	/* past end of buf */ | 
|  | ZlibW *w; | 
|  | } ZlibR; | 
|  |  | 
|  | static uint32 *crctab; | 
|  | static uchar PNGmagic[] = {137,80,78,71,13,10,26,10}; | 
|  | static char memerr[] = "ReadPNG: malloc failed: %r"; | 
|  |  | 
|  | static uint32 | 
|  | get4(uchar *a) | 
|  | { | 
|  | return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; | 
|  | } | 
|  |  | 
|  | static | 
|  | void | 
|  | pnginit(void) | 
|  | { | 
|  | static int inited; | 
|  |  | 
|  | if(inited) | 
|  | return; | 
|  | inited = 1; | 
|  | crctab = mkcrctab(0xedb88320); | 
|  | if(crctab == nil) | 
|  | sysfatal("mkcrctab error"); | 
|  | inflateinit(); | 
|  | } | 
|  |  | 
|  | static | 
|  | void* | 
|  | pngmalloc(ulong n, int clear) | 
|  | { | 
|  | void *p; | 
|  |  | 
|  | p = malloc(n); | 
|  | if(p == nil) | 
|  | sysfatal(memerr); | 
|  | if(clear) | 
|  | memset(p, 0, n); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | getchunk(Biobuf *b, char *type, uchar *d, int m) | 
|  | { | 
|  | uchar buf[8]; | 
|  | uint32 crc = 0, crc2; | 
|  | int n, nr; | 
|  |  | 
|  | if(Bread(b, buf, 8) != 8) | 
|  | return -1; | 
|  | n = get4(buf); | 
|  | memmove(type, buf+4, 4); | 
|  | type[4] = 0; | 
|  | if(n > m) | 
|  | sysfatal("getchunk needed %d, had %d", n, m); | 
|  | nr = Bread(b, d, n); | 
|  | if(nr != n) | 
|  | sysfatal("getchunk read %d, expected %d", nr, n); | 
|  | crc = blockcrc(crctab, crc, type, 4); | 
|  | crc = blockcrc(crctab, crc, d, n); | 
|  | if(Bread(b, buf, 4) != 4) | 
|  | sysfatal("getchunk tlr failed"); | 
|  | crc2 = get4(buf); | 
|  | if(crc != crc2) | 
|  | sysfatal("getchunk crc failed"); | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | zread(void *va) | 
|  | { | 
|  | ZlibR *z = va; | 
|  | char type[5]; | 
|  | int n; | 
|  |  | 
|  | if(z->b >= z->e){ | 
|  | refill_buffer: | 
|  | z->b = z->buf; | 
|  | n = getchunk(z->bi, type, z->b, IDATSIZE); | 
|  | if(n < 0 || strcmp(type, "IEND") == 0) | 
|  | return -1; | 
|  | z->e = z->b + n; | 
|  | if(!strcmp(type,"PLTE")) { | 
|  | if (n < 3 || n > 3*256 || n%3) | 
|  | sysfatal("invalid PLTE chunk len %d", n); | 
|  | memcpy(z->w->palette, z->b, n); | 
|  | z->w->palsize = n/3; | 
|  | goto refill_buffer; | 
|  | } | 
|  | if(type[0] & PropertyBit) | 
|  | goto refill_buffer;  /* skip auxiliary chunks for now */ | 
|  | if(strcmp(type,"IDAT")) { | 
|  | sysfatal("unrecognized mandatory chunk %s", type); | 
|  | goto refill_buffer; | 
|  | } | 
|  | } | 
|  | return *z->b++; | 
|  | } | 
|  |  | 
|  | static uchar | 
|  | paeth(uchar a, uchar b, uchar c) | 
|  | { | 
|  | int p, pa, pb, pc; | 
|  |  | 
|  | p = (int)a + (int)b - (int)c; | 
|  | pa = abs(p - (int)a); | 
|  | pb = abs(p - (int)b); | 
|  | pc = abs(p - (int)c); | 
|  |  | 
|  | if(pa <= pb && pa <= pc) | 
|  | return a; | 
|  | else if(pb <= pc) | 
|  | return b; | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static void | 
|  | unfilter(int alg, uchar *buf, uchar *up, int len, int bypp) | 
|  | { | 
|  | int i; | 
|  | switch(alg){ | 
|  | case FilterNone: | 
|  | break; | 
|  |  | 
|  | case FilterSub: | 
|  | for (i = bypp; i < len; ++i) | 
|  | buf[i] += buf[i-bypp]; | 
|  | break; | 
|  |  | 
|  | case FilterUp: | 
|  | for (i = 0; i < len; ++i) | 
|  | buf[i] += up[i]; | 
|  | break; | 
|  |  | 
|  | case FilterAvg: | 
|  | for (i = 0; i < bypp; ++i) | 
|  | buf[i] += (0+up[i])/2; | 
|  | for (; i < len; ++i) | 
|  | buf[i] += (buf[i-bypp]+up[i])/2; | 
|  | break; | 
|  |  | 
|  | case FilterPaeth: | 
|  | for (i = 0; i < bypp; ++i) | 
|  | buf[i] += paeth(0, up[i], 0); | 
|  | for (; i < len; ++i) | 
|  | buf[i] += paeth(buf[i-bypp], up[i], up[i-bypp]); | 
|  | break; | 
|  | default: | 
|  | sysfatal("unknown filtering scheme %d\n", alg); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | convertpix(ZlibW *z, uchar *pixel, uchar *r, uchar *g, uchar *b) | 
|  | { | 
|  | int off; | 
|  | switch (z->nchan) { | 
|  | case 1:	/* gray or indexed */ | 
|  | case 2:	/* gray+alpha */ | 
|  | if (z->bpp < 8) | 
|  | pixel[0] >>= 8-z->bpp; | 
|  | if (pixel[0] > z->palsize) | 
|  | sysfatal("index %d out of bounds %d", pixel[0], z->palsize); | 
|  | off = 3*pixel[0]; | 
|  | *r = z->palette[off]; | 
|  | *g = z->palette[off+1]; | 
|  | *b = z->palette[off+2]; | 
|  | break; | 
|  | case 3:	/* rgb */ | 
|  | case 4:	/* rgb+alpha */ | 
|  | *r = pixel[0]; | 
|  | *g = pixel[1]; | 
|  | *b = pixel[2]; | 
|  | break; | 
|  | default: | 
|  | sysfatal("bad number of channels: %d", z->nchan); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void | 
|  | scan(ZlibW *z) | 
|  | { | 
|  | uchar *p; | 
|  | int i, bit, n, ch, nch, pd; | 
|  | uchar cb; | 
|  | uchar pixel[4]; | 
|  |  | 
|  | p = z->scan; | 
|  | nch = z->nchan; | 
|  |  | 
|  | unfilter(p[0], p+1, z->pscan+1, z->scanl-1, (nch*z->bpp+7)/8); | 
|  | /* | 
|  | *	Adam7 interlace order. | 
|  | *	1 6 4 6 2 6 4 6 | 
|  | *	7 7 7 7 7 7 7 7 | 
|  | *	5 6 5 6 5 6 5 6 | 
|  | *	7 7 7 7 7 7 7 7 | 
|  | *	3 6 4 6 3 6 4 6 | 
|  | *	7 7 7 7 7 7 7 7 | 
|  | *	5 6 5 6 5 6 5 6 | 
|  | *	7 7 7 7 7 7 7 7 | 
|  | */ | 
|  | ch = 0; | 
|  | n = 0; | 
|  | cb = 128; | 
|  | pd = z->row * z->scanpix; | 
|  | for (i = 1; i < z->scanl; ++i) | 
|  | for (bit = 128; bit > 0; bit /= 2) { | 
|  |  | 
|  | pixel[ch] &= ~cb; | 
|  | if (p[i] & bit) | 
|  | pixel[ch] |= cb; | 
|  |  | 
|  | cb >>= 1; | 
|  |  | 
|  | if (++n == z->bpp) { | 
|  | cb = 128; | 
|  | n = 0; | 
|  | ch++; | 
|  | } | 
|  | if (ch == nch) { | 
|  | if (z->npix++ < z->chanl) | 
|  | convertpix(z,pixel,z->chan[0]+pd,z->chan[1]+pd,z->chan[2]+pd); | 
|  | pd++; | 
|  | if (pd % z->scanpix == 0) | 
|  | goto out; | 
|  | ch = 0; | 
|  | } | 
|  | } | 
|  | out: ; | 
|  | } | 
|  |  | 
|  | static int | 
|  | zwrite(void *va, void *vb, int n) | 
|  | { | 
|  | ZlibW *z = va; | 
|  | uchar *buf = vb; | 
|  | int i, j; | 
|  |  | 
|  | j = z->scanp; | 
|  | for (i = 0; i < n; ++i) { | 
|  | z->scan[j++] = buf[i]; | 
|  | if (j == z->scanl) { | 
|  | uchar *tp; | 
|  | scan(z); | 
|  |  | 
|  | tp = z->scan; | 
|  | z->scan = z->pscan; | 
|  | z->pscan = tp; | 
|  | z->row++; | 
|  | j = 0; | 
|  | } | 
|  | } | 
|  | z->scanp = j; | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static Rawimage* | 
|  | readslave(Biobuf *b) | 
|  | { | 
|  | ZlibR zr; | 
|  | ZlibW zw; | 
|  | Rawimage *image; | 
|  | char type[5]; | 
|  | uchar *buf, *h; | 
|  | int k, n, nrow, ncol, err, bpp, nch; | 
|  |  | 
|  | zr.w = &zw; | 
|  |  | 
|  | buf = pngmalloc(IDATSIZE, 0); | 
|  | Bread(b, buf, sizeof PNGmagic); | 
|  | if(memcmp(PNGmagic, buf, sizeof PNGmagic) != 0) | 
|  | sysfatal("bad PNGmagic"); | 
|  |  | 
|  | n = getchunk(b, type, buf, IDATSIZE); | 
|  | if(n < 13 || strcmp(type,"IHDR") != 0) | 
|  | sysfatal("missing IHDR chunk"); | 
|  | h = buf; | 
|  | ncol = get4(h);  h += 4; | 
|  | nrow = get4(h);  h += 4; | 
|  | if(ncol <= 0 || nrow <= 0) | 
|  | sysfatal("impossible image size nrow=%d ncol=%d", nrow, ncol); | 
|  | if(debug) | 
|  | fprint(2, "readpng nrow=%d ncol=%d\n", nrow, ncol); | 
|  |  | 
|  | bpp = *h++; | 
|  | nch = 0; | 
|  | switch (*h++) { | 
|  | case 0:	/* grey */ | 
|  | nch = 1; | 
|  | break; | 
|  | case 2:	/* rgb */ | 
|  | nch = 3; | 
|  | break; | 
|  | case 3: /* indexed rgb with PLTE */ | 
|  | nch = 1; | 
|  | break; | 
|  | case 4:	/* grey+alpha */ | 
|  | nch = 2; | 
|  | break; | 
|  | case 6:	/* rgb+alpha */ | 
|  | nch = 4; | 
|  | break; | 
|  | default: | 
|  | sysfatal("unsupported color scheme %d", h[-1]); | 
|  | } | 
|  |  | 
|  | /* generate default palette for grayscale */ | 
|  | zw.palsize = 256; | 
|  | if (nch < 3 && bpp < 9) | 
|  | zw.palsize = 1<<bpp; | 
|  | for (k = 0; k < zw.palsize; ++k) { | 
|  | zw.palette[3*k] = (k*255)/(zw.palsize-1); | 
|  | zw.palette[3*k+1] = (k*255)/(zw.palsize-1); | 
|  | zw.palette[3*k+2] = (k*255)/(zw.palsize-1); | 
|  | } | 
|  |  | 
|  | if(*h++ != 0) | 
|  | sysfatal("only deflate supported for now [%d]", h[-1]); | 
|  | if(*h++ != FilterNone) | 
|  | sysfatal("only FilterNone supported for now [%d]", h[-1]); | 
|  | if(*h != 0) | 
|  | sysfatal("only non-interlaced supported for now [%d]", h[-1]); | 
|  |  | 
|  | image = pngmalloc(sizeof(Rawimage), 1); | 
|  | image->r = Rect(0, 0, ncol, nrow); | 
|  | image->cmap = nil; | 
|  | image->cmaplen = 0; | 
|  | image->chanlen = ncol*nrow; | 
|  | image->fields = 0; | 
|  | image->gifflags = 0; | 
|  | image->gifdelay = 0; | 
|  | image->giftrindex = 0; | 
|  | image->chandesc = CRGB; | 
|  | image->nchans = 3; | 
|  |  | 
|  | zw.chanl = ncol*nrow; | 
|  | zw.npix = 0; | 
|  | for(k=0; k<4; k++) | 
|  | image->chans[k] = zw.chan[k] = pngmalloc(ncol*nrow, 1); | 
|  |  | 
|  | zr.bi = b; | 
|  | zr.buf = buf; | 
|  | zr.b = zr.e = buf + IDATSIZE; | 
|  |  | 
|  | zw.scanp = 0; | 
|  | zw.row = 0; | 
|  | zw.scanpix = ncol; | 
|  | zw.scanl = (nch*ncol*bpp+7)/8+1; | 
|  | zw.scan = pngmalloc(zw.scanl, 1); | 
|  | zw.pscan = pngmalloc(zw.scanl, 1); | 
|  | zw.nchan = nch; | 
|  | zw.bpp = bpp; | 
|  |  | 
|  | err = inflatezlib(&zw, zwrite, &zr, zread); | 
|  |  | 
|  | if (zw.npix > zw.chanl) | 
|  | fprint(2, "tried to overflow by %d pix\n", zw.npix - zw.chanl); | 
|  |  | 
|  |  | 
|  | if(err) | 
|  | sysfatal("inflatezlib %s\n", flateerr(err)); | 
|  |  | 
|  | free(image->chans[3]); | 
|  | image->chans[3] = nil; | 
|  | free(buf); | 
|  | free(zw.scan); | 
|  | free(zw.pscan); | 
|  | return image; | 
|  | } | 
|  |  | 
|  | Rawimage** | 
|  | Breadpng(Biobuf *b, int colorspace) | 
|  | { | 
|  | Rawimage *r, **array; | 
|  | char buf[ERRMAX]; | 
|  |  | 
|  | buf[0] = '\0'; | 
|  | if(colorspace != CRGB){ | 
|  | errstr(buf, sizeof buf);	/* throw it away */ | 
|  | werrstr("ReadPNG: unknown color space %d", colorspace); | 
|  | return nil; | 
|  | } | 
|  | pnginit(); | 
|  | array = malloc(2*sizeof(*array)); | 
|  | if(array==nil) | 
|  | return nil; | 
|  | errstr(buf, sizeof buf);	/* throw it away */ | 
|  | r = readslave(b); | 
|  | array[0] = r; | 
|  | array[1] = nil; | 
|  | return array; | 
|  | } | 
|  |  | 
|  | Rawimage** | 
|  | readpng(int fd, int colorspace) | 
|  | { | 
|  | Rawimage** a; | 
|  | Biobuf b; | 
|  |  | 
|  | if(Binit(&b, fd, OREAD) < 0) | 
|  | return nil; | 
|  | a = Breadpng(&b, colorspace); | 
|  | Bterm(&b); | 
|  | return a; | 
|  | } |