URI:
       treadpng.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       treadpng.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 }