diff options
Diffstat (limited to 'src/cmd/jpg/readgif.c')
-rw-r--r-- | src/cmd/jpg/readgif.c | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/src/cmd/jpg/readgif.c b/src/cmd/jpg/readgif.c new file mode 100644 index 00000000..22a0b68c --- /dev/null +++ b/src/cmd/jpg/readgif.c @@ -0,0 +1,535 @@ +#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 *pp) +{ + void **p = (void**)pp; + + 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; +} |