URI:
       treadgif.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
       ---
       treadgif.c (10249B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <draw.h>
            5 #include "imagefile.h"
            6 
            7 typedef struct Entry Entry;
            8 typedef struct Header Header;
            9 
           10 struct Entry{
           11         int                prefix;
           12         int                exten;
           13 };
           14 
           15 
           16 struct Header{
           17         Biobuf        *fd;
           18         char                err[256];
           19         jmp_buf        errlab;
           20         uchar         buf[3*256];
           21         char                 vers[8];
           22         uchar         *globalcmap;
           23         int                screenw;
           24         int                screenh;
           25         int                fields;
           26         int                bgrnd;
           27         int                aspect;
           28         int                flags;
           29         int                delay;
           30         int                trindex;
           31         int                loopcount;
           32         Entry        tbl[4096];
           33         Rawimage        **array;
           34         Rawimage        *new;
           35 
           36         uchar        *pic;
           37 };
           38 
           39 static char                readerr[] = "ReadGIF: read error: %r";
           40 static char                extreaderr[] = "ReadGIF: can't read extension: %r";
           41 static char                memerr[] = "ReadGIF: malloc failed: %r";
           42 
           43 static Rawimage**        readarray(Header*);
           44 static Rawimage*        readone(Header*);
           45 static void                        readheader(Header*);
           46 static void                        skipextension(Header*);
           47 static uchar*                readcmap(Header*, int);
           48 static uchar*                decode(Header*, Rawimage*, Entry*);
           49 static void                        interlace(Header*, Rawimage*);
           50 
           51 static
           52 void
           53 clear(void *pp)
           54 {
           55         void **p = (void**)pp;
           56 
           57         if(*p){
           58                 free(*p);
           59                 *p = nil;
           60         }
           61 }
           62 
           63 static
           64 void
           65 giffreeall(Header *h, int freeimage)
           66 {
           67         int i;
           68 
           69         if(h->fd){
           70                 Bterm(h->fd);
           71                 h->fd = nil;
           72         }
           73         clear(&h->pic);
           74         if(h->new){
           75                 clear(&h->new->cmap);
           76                 clear(&h->new->chans[0]);
           77                 clear(&h->new);
           78         }
           79         clear(&h->globalcmap);
           80         if(freeimage && h->array!=nil){
           81                 for(i=0; h->array[i]; i++){
           82                         clear(&h->array[i]->cmap);
           83                         clear(&h->array[i]->chans[0]);
           84                 }
           85                 clear(&h->array);
           86         }
           87 }
           88 
           89 static
           90 void
           91 giferror(Header *h, char *fmt, ...)
           92 {
           93         va_list arg;
           94 
           95         va_start(arg, fmt);
           96         vseprint(h->err, h->err+sizeof h->err, fmt, arg);
           97         va_end(arg);
           98 
           99         werrstr(h->err);
          100         giffreeall(h, 1);
          101         longjmp(h->errlab, 1);
          102 }
          103 
          104 
          105 Rawimage**
          106 readgif(int fd, int colorspace)
          107 {
          108         Rawimage **a;
          109         Biobuf b;
          110         Header *h;
          111         char buf[ERRMAX];
          112 
          113         buf[0] = '\0';
          114         USED(colorspace);
          115         if(Binit(&b, fd, OREAD) < 0)
          116                 return nil;
          117         h = malloc(sizeof(Header));
          118         if(h == nil){
          119                 Bterm(&b);
          120                 return nil;
          121         }
          122         memset(h, 0, sizeof(Header));
          123         h->fd = &b;
          124         errstr(buf, sizeof buf);        /* throw it away */
          125         if(setjmp(h->errlab))
          126                 a = nil;
          127         else
          128                 a = readarray(h);
          129         giffreeall(h, 0);
          130         free(h);
          131         return a;
          132 }
          133 
          134 static
          135 void
          136 inittbl(Header *h)
          137 {
          138         int i;
          139         Entry *tbl;
          140 
          141         tbl = h->tbl;
          142         for(i=0; i<258; i++) {
          143                 tbl[i].prefix = -1;
          144                 tbl[i].exten = i;
          145         }
          146 }
          147 
          148 static
          149 Rawimage**
          150 readarray(Header *h)
          151 {
          152         Entry *tbl;
          153         Rawimage *new, **array;
          154         int c, nimages;
          155 
          156         tbl = h->tbl;
          157 
          158         readheader(h);
          159 
          160         if(h->fields & 0x80)
          161                 h->globalcmap = readcmap(h, (h->fields&7)+1);
          162 
          163         array = malloc(sizeof(Rawimage**));
          164         if(array == nil)
          165                 giferror(h, memerr);
          166         nimages = 0;
          167         array[0] = nil;
          168         h->array = array;
          169 
          170         for(;;){
          171                 switch(c = Bgetc(h->fd)){
          172                 case Beof:
          173                         goto Return;
          174 
          175                 case 0x21:        /* Extension (ignored) */
          176                         skipextension(h);
          177                         break;
          178 
          179                 case 0x2C:        /* Image Descriptor */
          180                         inittbl(h);
          181                         new = readone(h);
          182                         if(new->fields & 0x80){
          183                                 new->cmaplen = 3*(1<<((new->fields&7)+1));
          184                                 new->cmap = readcmap(h, (new->fields&7)+1);
          185                         }else{
          186                                 new->cmaplen = 3*(1<<((h->fields&7)+1));
          187                                 new->cmap = malloc(new->cmaplen);
          188                                 memmove(new->cmap, h->globalcmap, new->cmaplen);
          189                         }
          190                         h->new = new;
          191                         new->chans[0] = decode(h, new, tbl);
          192                         if(new->fields & 0x40)
          193                                 interlace(h, new);
          194                         new->gifflags = h->flags;
          195                         new->gifdelay = h->delay;
          196                         new->giftrindex = h->trindex;
          197                         new->gifloopcount = h->loopcount;
          198                         array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
          199                         if(array == nil)
          200                                 giferror(h, memerr);
          201                         array[nimages++] = new;
          202                         array[nimages] = nil;
          203                         h->array = array;
          204                         h->new = nil;
          205                         break;
          206 
          207                 case 0x3B:        /* Trailer */
          208                         goto Return;
          209 
          210                 default:
          211                         fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
          212                         goto Return;
          213                 }
          214         }
          215 
          216    Return:
          217         if(array[0]==nil || array[0]->chans[0] == nil)
          218                 giferror(h, "ReadGIF: no picture in file");
          219 
          220         return array;
          221 }
          222 
          223 static
          224 void
          225 readheader(Header *h)
          226 {
          227         if(Bread(h->fd, h->buf, 13) != 13)
          228                 giferror(h, "ReadGIF: can't read header: %r");
          229         memmove(h->vers, h->buf, 6);
          230         if(strcmp(h->vers, "GIF87a")!=0 &&  strcmp(h->vers, "GIF89a")!=0)
          231                 giferror(h, "ReadGIF: can't recognize format %s", h->vers);
          232         h->screenw = h->buf[6]+(h->buf[7]<<8);
          233         h->screenh = h->buf[8]+(h->buf[9]<<8);
          234         h->fields = h->buf[10];
          235         h->bgrnd = h->buf[11];
          236         h->aspect = h->buf[12];
          237         h->flags = 0;
          238         h->delay = 0;
          239         h->trindex = 0;
          240         h->loopcount = -1;
          241 }
          242 
          243 static
          244 uchar*
          245 readcmap(Header *h, int size)
          246 {
          247         uchar *map;
          248 
          249         if(size > 8)
          250                 giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
          251         size = 3*(1<<size);
          252         if(Bread(h->fd, h->buf, size) != size)
          253                 giferror(h, "ReadGIF: short read on color map");
          254         map = malloc(size);
          255         if(map == nil)
          256                 giferror(h, memerr);
          257         memmove(map, h->buf, size);
          258         return map;
          259 }
          260 
          261 static
          262 Rawimage*
          263 readone(Header *h)
          264 {
          265         Rawimage *i;
          266         int left, top, width, height;
          267 
          268         if(Bread(h->fd, h->buf, 9) != 9)
          269                 giferror(h, "ReadGIF: can't read image descriptor: %r");
          270         i = malloc(sizeof(Rawimage));
          271         if(i == nil)
          272                 giferror(h, memerr);
          273         left = h->buf[0]+(h->buf[1]<<8);
          274         top = h->buf[2]+(h->buf[3]<<8);
          275         width = h->buf[4]+(h->buf[5]<<8);
          276         height = h->buf[6]+(h->buf[7]<<8);
          277         i->fields = h->buf[8];
          278         i->r.min.x = left;
          279         i->r.min.y = top;
          280         i->r.max.x = left+width;
          281         i->r.max.y = top+height;
          282         i->nchans = 1;
          283         i->chandesc = CRGB1;
          284         return i;
          285 }
          286 
          287 
          288 static
          289 int
          290 readdata(Header *h, uchar *data)
          291 {
          292         int nbytes, n;
          293 
          294         nbytes = Bgetc(h->fd);
          295         if(nbytes < 0)
          296                 giferror(h, "ReadGIF: can't read data: %r");
          297         if(nbytes == 0)
          298                 return 0;
          299         n = Bread(h->fd, data, nbytes);
          300         if(n < 0)
          301                 giferror(h, "ReadGIF: can't read data: %r");
          302         if(n != nbytes)
          303                 fprint(2, "ReadGIF: short data subblock\n");
          304         return n;
          305 }
          306 
          307 static
          308 void
          309 graphiccontrol(Header *h)
          310 {
          311         if(Bread(h->fd, h->buf, 5+1) != 5+1)
          312                 giferror(h, readerr);
          313         h->flags = h->buf[1];
          314         h->delay = h->buf[2]+(h->buf[3]<<8);
          315         h->trindex = h->buf[4];
          316 }
          317 
          318 static
          319 void
          320 skipextension(Header *h)
          321 {
          322         int type, hsize, hasdata, n;
          323         uchar data[256];
          324 
          325         hsize = 0;
          326         hasdata = 0;
          327 
          328         type = Bgetc(h->fd);
          329         switch(type){
          330         case Beof:
          331                 giferror(h, extreaderr);
          332                 break;
          333         case 0x01:        /* Plain Text Extension */
          334                 hsize = 13;
          335                 hasdata = 1;
          336                 break;
          337         case 0xF9:        /* Graphic Control Extension */
          338                 graphiccontrol(h);
          339                 return;
          340         case 0xFE:        /* Comment Extension */
          341                 hasdata = 1;
          342                 break;
          343         case 0xFF:        /* Application Extension */
          344                 hsize = Bgetc(h->fd);
          345                 /* standard says this must be 11, but Adobe likes to put out 10-byte ones,
          346                  * so we pay attention to the field. */
          347                 hasdata = 1;
          348                 break;
          349         default:
          350                 giferror(h, "ReadGIF: unknown extension");
          351         }
          352         if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
          353                 giferror(h, extreaderr);
          354         if(!hasdata)
          355                 return;
          356 
          357         /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
          358         if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
          359                 n = readdata(h, data);
          360                 if(n == 0)
          361                         return;
          362                 if(n==3 && data[0]==1)
          363                         h->loopcount = data[1] | (data[2]<<8);
          364         }
          365         while(readdata(h, data) != 0)
          366                 ;
          367 }
          368 
          369 static
          370 uchar*
          371 decode(Header *h, Rawimage *i, Entry *tbl)
          372 {
          373         int c, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
          374         int csize, nentry, maxentry, first, ocode, ndata, nb;
          375         uchar *pic;
          376         uchar stack[4096], data[256];
          377 
          378         if(Bread(h->fd, h->buf, 1) != 1)
          379                 giferror(h, "ReadGIF: can't read data: %r");
          380         codesize = h->buf[0];
          381         if(codesize>8 || 0>codesize)
          382                 giferror(h, "ReadGIF: can't handle codesize %d", codesize);
          383         if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
          384           && (codesize!=2 || i->cmaplen!=3*2)) /* peculiar GIF bitmap files... */
          385                 giferror(h, "ReadGIF: codesize %d doesn't match color map 3*%d", codesize, i->cmaplen/3);
          386 
          387         CTM =1<<codesize;
          388         EOD = CTM+1;
          389 
          390         piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
          391         i->chanlen = piclen;
          392         pic = malloc(piclen);
          393         if(pic == nil)
          394                 giferror(h, memerr);
          395         h->pic = pic;
          396         pici = 0;
          397         ndata = 0;
          398         datai = 0;
          399         nbits = 0;
          400         sreg = 0;
          401         fc = 0;
          402 
          403     Loop:
          404         for(;;){
          405                 csize = codesize+1;
          406                 nentry = EOD+1;
          407                 maxentry = (1<<csize)-1;
          408                 first = 1;
          409                 ocode = -1;
          410 
          411                 for(;; ocode = incode) {
          412                         while(nbits < csize) {
          413                                 if(datai == ndata){
          414                                         ndata = readdata(h, data);
          415                                         if(ndata == 0)
          416                                                 goto Return;
          417                                         datai = 0;
          418                                 }
          419                                 c = data[datai++];
          420                                 sreg |= c<<nbits;
          421                                 nbits += 8;
          422                         }
          423                         code = sreg & ((1<<csize) - 1);
          424                         sreg >>= csize;
          425                         nbits -= csize;
          426 
          427                         if(code == EOD){
          428                                 ndata = readdata(h, data);
          429                                 if(ndata != 0)
          430                                         fprint(2, "ReadGIF: unexpected data past EOD");
          431                                 goto Return;
          432                         }
          433 
          434                         if(code == CTM)
          435                                 goto Loop;
          436 
          437                         stacki = (sizeof stack)-1;
          438 
          439                         incode = code;
          440 
          441                         /* special case for KwKwK */
          442                         if(code == nentry) {
          443                                 stack[stacki--] = fc;
          444                                 code = ocode;
          445                         }
          446 
          447                         if(code > nentry)
          448                                 giferror(h, "ReadGIF: bad code %x %x", code, nentry);
          449 
          450                         for(c=code; c>=0; c=tbl[c].prefix)
          451                                 stack[stacki--] = tbl[c].exten;
          452 
          453                         nb = (sizeof stack)-(stacki+1);
          454                         if(pici+nb > piclen){
          455                                 /* this common error is harmless
          456                                  * we have to keep reading to keep the blocks in sync */
          457                                 ;
          458                         }else{
          459                                 memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
          460                                 pici += nb;
          461                         }
          462 
          463                         fc = stack[stacki+1];
          464 
          465                         if(first){
          466                                 first = 0;
          467                                 continue;
          468                         }
          469                         #define early 0 /* peculiar tiff feature here for reference */
          470                         if(nentry == maxentry-early) {
          471                                 if(csize >= 12)
          472                                         continue;
          473                                 csize++;
          474                                 maxentry = (1<<csize);
          475                                 if(csize < 12)
          476                                         maxentry--;
          477                         }
          478                         tbl[nentry].prefix = ocode;
          479                         tbl[nentry].exten = fc;
          480                         nentry++;
          481                 }
          482         }
          483 
          484 Return:
          485         h->pic = nil;
          486         return pic;
          487 }
          488 
          489 static
          490 void
          491 interlace(Header *h, Rawimage *image)
          492 {
          493         uchar *pic;
          494         Rectangle r;
          495         int dx, yy, y;
          496         uchar *ipic;
          497 
          498         pic = image->chans[0];
          499         r = image->r;
          500         dx = r.max.x-r.min.x;
          501         ipic = malloc(dx*(r.max.y-r.min.y));
          502         if(ipic == nil)
          503                 giferror(h, nil);
          504 
          505         /* Group 1: every 8th row, starting with row 0 */
          506         yy = 0;
          507         for(y=r.min.y; y<r.max.y; y+=8){
          508                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
          509                 yy++;
          510         }
          511 
          512         /* Group 2: every 8th row, starting with row 4 */
          513         for(y=r.min.y+4; y<r.max.y; y+=8){
          514                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
          515                 yy++;
          516         }
          517 
          518         /* Group 3: every 4th row, starting with row 2 */
          519         for(y=r.min.y+2; y<r.max.y; y+=4){
          520                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
          521                 yy++;
          522         }
          523 
          524         /* Group 4: every 2nd row, starting with row 1 */
          525         for(y=r.min.y+1; y<r.max.y; y+=2){
          526                 memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
          527                 yy++;
          528         }
          529 
          530         free(image->chans[0]);
          531         image->chans[0] = ipic;
          532 }