123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include <draw.h>
- #include "imagefile.h"
- typedef struct Entry Entry;
- typedef struct Header Header;
- struct Entry{
- int prefix;
- int exten;
- };
- struct Header{
- Biobuf *fd;
- char err[256];
- jmp_buf errlab;
- uchar buf[3*256];
- char vers[8];
- uchar *globalcmap;
- int screenw;
- int screenh;
- int fields;
- int bgrnd;
- int aspect;
- int flags;
- int delay;
- int trindex;
- int loopcount;
- Entry tbl[4096];
- Rawimage **array;
- Rawimage *new;
- uchar *pic;
- };
- static char readerr[] = "ReadGIF: read error: %r";
- static char extreaderr[] = "ReadGIF: can't read extension: %r";
- static char memerr[] = "ReadGIF: malloc failed: %r";
- static Rawimage** readarray(Header*);
- static Rawimage* readone(Header*);
- static void readheader(Header*);
- static void skipextension(Header*);
- static uchar* readcmap(Header*, int);
- static uchar* decode(Header*, Rawimage*, Entry*);
- static void interlace(Header*, Rawimage*);
- static
- void
- clear(void **p)
- {
- if(*p){
- free(*p);
- *p = nil;
- }
- }
- static
- void
- giffreeall(Header *h, int freeimage)
- {
- int i;
- if(h->fd){
- Bterm(h->fd);
- h->fd = nil;
- }
- clear(&h->pic);
- if(h->new){
- clear(&h->new->cmap);
- clear(&h->new->chans[0]);
- clear(&h->new);
- }
- clear(&h->globalcmap);
- if(freeimage && h->array!=nil){
- for(i=0; h->array[i]; i++){
- clear(&h->array[i]->cmap);
- clear(&h->array[i]->chans[0]);
- }
- clear(&h->array);
- }
- }
- static
- void
- giferror(Header *h, char *fmt, ...)
- {
- va_list arg;
- va_start(arg, fmt);
- vseprint(h->err, h->err+sizeof h->err, fmt, arg);
- va_end(arg);
- werrstr(h->err);
- giffreeall(h, 1);
- longjmp(h->errlab, 1);
- }
- Rawimage**
- readgif(int fd, int colorspace)
- {
- Rawimage **a;
- Biobuf b;
- Header *h;
- char buf[ERRMAX];
- buf[0] = '\0';
- USED(colorspace);
- if(Binit(&b, fd, OREAD) < 0)
- return nil;
- h = malloc(sizeof(Header));
- if(h == nil){
- Bterm(&b);
- return nil;
- }
- memset(h, 0, sizeof(Header));
- h->fd = &b;
- errstr(buf, sizeof buf); /* throw it away */
- if(setjmp(h->errlab))
- a = nil;
- else
- a = readarray(h);
- giffreeall(h, 0);
- free(h);
- return a;
- }
- static
- void
- inittbl(Header *h)
- {
- int i;
- Entry *tbl;
- tbl = h->tbl;
- for(i=0; i<258; i++) {
- tbl[i].prefix = -1;
- tbl[i].exten = i;
- }
- }
- static
- Rawimage**
- readarray(Header *h)
- {
- Entry *tbl;
- Rawimage *new, **array;
- int c, nimages;
- tbl = h->tbl;
- readheader(h);
- if(h->fields & 0x80)
- h->globalcmap = readcmap(h, (h->fields&7)+1);
- array = malloc(sizeof(Rawimage**));
- if(array == nil)
- giferror(h, memerr);
- nimages = 0;
- array[0] = nil;
- h->array = array;
-
- for(;;){
- switch(c = Bgetc(h->fd)){
- case Beof:
- goto Return;
- case 0x21: /* Extension (ignored) */
- skipextension(h);
- break;
- case 0x2C: /* Image Descriptor */
- inittbl(h);
- new = readone(h);
- if(new->fields & 0x80){
- new->cmaplen = 3*(1<<((new->fields&7)+1));
- new->cmap = readcmap(h, (new->fields&7)+1);
- }else{
- new->cmaplen = 3*(1<<((h->fields&7)+1));
- new->cmap = malloc(new->cmaplen);
- memmove(new->cmap, h->globalcmap, new->cmaplen);
- }
- h->new = new;
- new->chans[0] = decode(h, new, tbl);
- if(new->fields & 0x40)
- interlace(h, new);
- new->gifflags = h->flags;
- new->gifdelay = h->delay;
- new->giftrindex = h->trindex;
- new->gifloopcount = h->loopcount;
- array = realloc(h->array, (nimages+2)*sizeof(Rawimage*));
- if(array == nil)
- giferror(h, memerr);
- array[nimages++] = new;
- array[nimages] = nil;
- h->array = array;
- h->new = nil;
- break;
- case 0x3B: /* Trailer */
- goto Return;
- default:
- fprint(2, "ReadGIF: unknown block type: 0x%.2x\n", c);
- goto Return;
- }
- }
- Return:
- if(array[0]==nil || array[0]->chans[0] == nil)
- giferror(h, "ReadGIF: no picture in file");
- return array;
- }
- static
- void
- readheader(Header *h)
- {
- if(Bread(h->fd, h->buf, 13) != 13)
- giferror(h, "ReadGIF: can't read header: %r");
- memmove(h->vers, h->buf, 6);
- if(strcmp(h->vers, "GIF87a")!=0 && strcmp(h->vers, "GIF89a")!=0)
- giferror(h, "ReadGIF: can't recognize format %s", h->vers);
- h->screenw = h->buf[6]+(h->buf[7]<<8);
- h->screenh = h->buf[8]+(h->buf[9]<<8);
- h->fields = h->buf[10];
- h->bgrnd = h->buf[11];
- h->aspect = h->buf[12];
- h->flags = 0;
- h->delay = 0;
- h->trindex = 0;
- h->loopcount = -1;
- }
- static
- uchar*
- readcmap(Header *h, int size)
- {
- uchar *map;
- if(size > 8)
- giferror(h, "ReadGIF: can't handles %d bits per pixel", size);
- size = 3*(1<<size);
- if(Bread(h->fd, h->buf, size) != size)
- giferror(h, "ReadGIF: short read on color map");
- map = malloc(size);
- if(map == nil)
- giferror(h, memerr);
- memmove(map, h->buf, size);
- return map;
- }
- static
- Rawimage*
- readone(Header *h)
- {
- Rawimage *i;
- int left, top, width, height;
- if(Bread(h->fd, h->buf, 9) != 9)
- giferror(h, "ReadGIF: can't read image descriptor: %r");
- i = malloc(sizeof(Rawimage));
- if(i == nil)
- giferror(h, memerr);
- left = h->buf[0]+(h->buf[1]<<8);
- top = h->buf[2]+(h->buf[3]<<8);
- width = h->buf[4]+(h->buf[5]<<8);
- height = h->buf[6]+(h->buf[7]<<8);
- i->fields = h->buf[8];
- i->r.min.x = left;
- i->r.min.y = top;
- i->r.max.x = left+width;
- i->r.max.y = top+height;
- i->nchans = 1;
- i->chandesc = CRGB1;
- return i;
- }
- static
- int
- readdata(Header *h, uchar *data)
- {
- int nbytes, n;
- nbytes = Bgetc(h->fd);
- if(nbytes < 0)
- giferror(h, "ReadGIF: can't read data: %r");
- if(nbytes == 0)
- return 0;
- n = Bread(h->fd, data, nbytes);
- if(n < 0)
- giferror(h, "ReadGIF: can't read data: %r");
- if(n != nbytes)
- fprint(2, "ReadGIF: short data subblock\n");
- return n;
- }
- static
- void
- graphiccontrol(Header *h)
- {
- if(Bread(h->fd, h->buf, 5+1) != 5+1)
- giferror(h, readerr);
- h->flags = h->buf[1];
- h->delay = h->buf[2]+(h->buf[3]<<8);
- h->trindex = h->buf[4];
- }
- static
- void
- skipextension(Header *h)
- {
- int type, hsize, hasdata, n;
- uchar data[256];
- hsize = 0;
- hasdata = 0;
- type = Bgetc(h->fd);
- switch(type){
- case Beof:
- giferror(h, extreaderr);
- break;
- case 0x01: /* Plain Text Extension */
- hsize = 13;
- hasdata = 1;
- break;
- case 0xF9: /* Graphic Control Extension */
- graphiccontrol(h);
- return;
- case 0xFE: /* Comment Extension */
- hasdata = 1;
- break;
- case 0xFF: /* Application Extension */
- hsize = Bgetc(h->fd);
- /* standard says this must be 11, but Adobe likes to put out 10-byte ones,
- * so we pay attention to the field. */
- hasdata = 1;
- break;
- default:
- giferror(h, "ReadGIF: unknown extension");
- }
- if(hsize>0 && Bread(h->fd, h->buf, hsize) != hsize)
- giferror(h, extreaderr);
- if(!hasdata){
- if(h->buf[hsize-1] != 0)
- giferror(h, "ReadGIF: bad extension format");
- return;
- }
- /* loop counter: Application Extension with NETSCAPE2.0 as string and 1 <loop.count> in data */
- if(type == 0xFF && hsize==11 && memcmp(h->buf, "NETSCAPE2.0", 11)==0){
- n = readdata(h, data);
- if(n == 0)
- return;
- if(n==3 && data[0]==1)
- h->loopcount = data[1] | (data[2]<<8);
- }
- while(readdata(h, data) != 0)
- ;
- }
- static
- uchar*
- decode(Header *h, Rawimage *i, Entry *tbl)
- {
- int c, incode, codesize, CTM, EOD, pici, datai, stacki, nbits, sreg, fc, code, piclen;
- int csize, nentry, maxentry, first, ocode, ndata, nb;
- uchar *pic;
- uchar stack[4096], data[256];
- if(Bread(h->fd, h->buf, 1) != 1)
- giferror(h, "ReadGIF: can't read data: %r");
- codesize = h->buf[0];
- if(codesize>8 || 0>codesize)
- giferror(h, "ReadGIF: can't handle codesize %d", codesize);
- if(i->cmap!=nil && i->cmaplen!=3*(1<<codesize)
- && (codesize!=2 || i->cmaplen!=3*2)) /* peculiar GIF bitmap files... */
- giferror(h, "ReadGIF: codesize %d doesn't match color map 3*%d", codesize, i->cmaplen/3);
- CTM =1<<codesize;
- EOD = CTM+1;
- piclen = (i->r.max.x-i->r.min.x)*(i->r.max.y-i->r.min.y);
- i->chanlen = piclen;
- pic = malloc(piclen);
- if(pic == nil)
- giferror(h, memerr);
- h->pic = pic;
- pici = 0;
- ndata = 0;
- datai = 0;
- nbits = 0;
- sreg = 0;
- fc = 0;
- Loop:
- for(;;){
- csize = codesize+1;
- nentry = EOD+1;
- maxentry = (1<<csize)-1;
- first = 1;
- ocode = -1;
- for(;; ocode = incode) {
- while(nbits < csize) {
- if(datai == ndata){
- ndata = readdata(h, data);
- if(ndata == 0)
- goto Return;
- datai = 0;
- }
- c = data[datai++];
- sreg |= c<<nbits;
- nbits += 8;
- }
- code = sreg & ((1<<csize) - 1);
- sreg >>= csize;
- nbits -= csize;
- if(code == EOD){
- ndata = readdata(h, data);
- if(ndata != 0)
- fprint(2, "ReadGIF: unexpected data past EOD");
- goto Return;
- }
- if(code == CTM)
- goto Loop;
- stacki = (sizeof stack)-1;
- incode = code;
- /* special case for KwKwK */
- if(code == nentry) {
- stack[stacki--] = fc;
- code = ocode;
- }
- if(code > nentry)
- giferror(h, "ReadGIF: bad code %x %x", code, nentry);
- for(c=code; c>=0; c=tbl[c].prefix)
- stack[stacki--] = tbl[c].exten;
- nb = (sizeof stack)-(stacki+1);
- if(pici+nb > piclen){
- /* this common error is harmless
- * we have to keep reading to keep the blocks in sync */
- ;
- }else{
- memmove(pic+pici, stack+stacki+1, sizeof stack - (stacki+1));
- pici += nb;
- }
- fc = stack[stacki+1];
- if(first){
- first = 0;
- continue;
- }
- #define early 0 /* peculiar tiff feature here for reference */
- if(nentry == maxentry-early) {
- if(csize >= 12)
- continue;
- csize++;
- maxentry = (1<<csize);
- if(csize < 12)
- maxentry--;
- }
- tbl[nentry].prefix = ocode;
- tbl[nentry].exten = fc;
- nentry++;
- }
- }
- Return:
- h->pic = nil;
- return pic;
- }
- static
- void
- interlace(Header *h, Rawimage *image)
- {
- uchar *pic;
- Rectangle r;
- int dx, yy, y;
- uchar *ipic;
- pic = image->chans[0];
- r = image->r;
- dx = r.max.x-r.min.x;
- ipic = malloc(dx*(r.max.y-r.min.y));
- if(ipic == nil)
- giferror(h, nil);
- /* Group 1: every 8th row, starting with row 0 */
- yy = 0;
- for(y=r.min.y; y<r.max.y; y+=8){
- memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
- yy++;
- }
- /* Group 2: every 8th row, starting with row 4 */
- for(y=r.min.y+4; y<r.max.y; y+=8){
- memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
- yy++;
- }
- /* Group 3: every 4th row, starting with row 2 */
- for(y=r.min.y+2; y<r.max.y; y+=4){
- memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
- yy++;
- }
- /* Group 4: every 2nd row, starting with row 1 */
- for(y=r.min.y+1; y<r.max.y; y+=2){
- memmove(&ipic[(y-r.min.y)*dx], &pic[yy*dx], dx);
- yy++;
- }
- free(image->chans[0]);
- image->chans[0] = ipic;
- }
|