#include #include #include #include "gif.h" struct gifreader { const GIFREADER_OPS *ops; void *opsarg; unsigned int detail; unsigned int flags; #define GRF_FAILED 0x00000001 #define GRF_HAVEGLOBAL 0x00000002 #define GRF_UNREAD 0x00000004 unsigned char unreadc; int version; unsigned short int scr_w; unsigned short int scr_h; unsigned char scr_bg; unsigned char scr_par; unsigned char scr_m; unsigned char scr_cr; unsigned char scr_sort; unsigned char scr_pix; unsigned short int img_x; unsigned short int img_y; unsigned short int img_w; unsigned short int img_h; unsigned char img_m; unsigned char img_i; unsigned char img_sort; unsigned char img_pixel; unsigned char img_bpp; unsigned char global_map[256][3]; unsigned char local_map[256][3]; unsigned char codesize; unsigned long int pixels_wanted; unsigned int blockbuf; unsigned int bbbits; unsigned char lzw_sufchar[4096]; unsigned short int lzw_prefcode[4096]; #define LZW_NIL 0xffff unsigned int lzw_clear; unsigned int lzw_end; unsigned int lzw_clearbits; unsigned int lzw_prevcode; unsigned int lzw_next; unsigned int lzw_bits; unsigned int lzw_mask; int lzw_ended; } ; static void gr_err(GIFREADER *gr, int err, ...) { va_list ap; va_start(ap,err); switch (err) { default: abort(); break; case GIFREAD_ERR_READERROR: case GIFREAD_ERR_UNXEOF: (*gr->ops->error)(gr->opsarg,err,va_arg(ap,const char *)); gr->flags |= GRF_FAILED; break; case GIFREAD_ERR_BADSIG: case GIFREAD_ERR_LZW_OVERFLOW: case GIFREAD_ERR_LZW_BAD_KWKWK: case GIFREAD_ERR_SKIPJUNK: case GIFREAD_ERR_TRAILJUNK: (*gr->ops->error)(gr->opsarg,err); gr->flags |= GRF_FAILED; break; case GIFREAD_ERR_87a_RESERVED: case GIFREAD_ERR_IMGDESC_CODESIZE: { int a1; int a2; a1 = va_arg(ap,int); a2 = va_arg(ap,int); (*gr->ops->error)(gr->opsarg,err,a1,a2); } break; case GIFREAD_ERR_TEXTEXT_HDRSIZE: case GIFREAD_ERR_GFXCTLEXT_HDRSIZE: case GIFREAD_ERR_GFXCTLEXT_MBZ: case GIFREAD_ERR_GFXCTLEXT_BADTERM: case GIFREAD_ERR_GFXCTLEXT_BADDISP: case GIFREAD_ERR_APPEXT_HDRSIZE: case GIFREAD_ERR_IMGDESC_RESERVED: case GIFREAD_ERR_LZW_BAD_CODE: (*gr->ops->error)(gr->opsarg,err,va_arg(ap,int)); break; } } static int mustread(GIFREADER *gr, void *buf, int nb, const char *tag) { int nr; if (gr->flags & GRF_UNREAD) { ((unsigned char *)buf)[0] = gr->unreadc; buf = ((char *)buf) + 1; nb --; gr->flags &= ~GRF_UNREAD; } nr = (*gr->ops->read_input)(gr->opsarg,buf,nb); if (nr < 0) { gr_err(gr,GIFREAD_ERR_READERROR,tag); return(1); } if (nr != nb) { gr_err(gr,GIFREAD_ERR_UNXEOF,tag); gr->flags |= GRF_FAILED; return(1); } if (gr->detail & GIFREAD_DETAIL_DATAREAD) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_DATAREAD,(const unsigned char *)buf,nb,tag); return(0); } static void unread1(GIFREADER *gr, unsigned char c) { gr->unreadc = c; gr->flags |= GRF_UNREAD; } static int read_plain_text_extension(GIFREADER *gr) { unsigned char len; unsigned char data[255]; unsigned int grid_x; unsigned int grid_y; unsigned int grid_w; unsigned int grid_h; unsigned int cc_w; unsigned int cc_h; unsigned int fg; unsigned int bg; if (mustread(gr,&len,1,"plain text extension data block length")) return(0); if (len != 12) { gr_err(gr,GIFREAD_ERR_TEXTEXT_HDRSIZE,len); do { if (mustread(gr,&data[0],len,"broken plain text extension data")) return(0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,&data[0],len); if (mustread(gr,&len,1,"broken plain text extension data block length")) return(0); } while (len != 0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,(const unsigned char *)0,0); return(1); } if (mustread(gr,&data[0],12,"plain text extension header data")) return(0); grid_x = (data[1] << 8) | data[0]; grid_y = (data[3] << 8) | data[2]; grid_w = (data[5] << 8) | data[4]; grid_h = (data[7] << 8) | data[6]; cc_w = data[8]; cc_h = data[9]; fg = data[10]; bg = data[11]; if (gr->detail & GIFREAD_DETAIL_TEXTEXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_TEXTEXT,grid_x,grid_y,grid_w,grid_h,cc_w,cc_h,fg,bg); while (1) { if (mustread(gr,&len,1,"plain text data block length")) return(0); if (len == 0) break; if (mustread(gr,&data[0],len,"plain text data")) return(0); if (gr->detail & GIFREAD_DETAIL_TEXTEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_TEXTEXT_DATA,&data[0],len); } if (gr->detail & GIFREAD_DETAIL_TEXTEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_TEXTEXT_DATA,(const unsigned char *)0,0); return(1); } static int read_graphic_control_extension(GIFREADER *gr) { unsigned char len; unsigned char data[255]; int disp; int input; int delay; int transp; if (mustread(gr,&len,1,"graphic control extension data block length")) return(0); if (len != 4) { gr_err(gr,GIFREAD_ERR_GFXCTLEXT_HDRSIZE,len); do { if (mustread(gr,&data[0],len,"broken graphic control extension data")) return(0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,&data[0],len); if (mustread(gr,&len,1,"broken graphic control extension data block length")) return(0); } while (len != 0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,(const unsigned char *)0,0); return(1); } if (mustread(gr,&data[0],4,"graphic control extension data")) return(0); if (data[0] & 0xe0) { gr_err(gr,GIFREAD_ERR_GFXCTLEXT_MBZ,data[0]); data[0] &= ~0xe0; } switch ((data[0] >> 2) & 7) { case 0x00: disp = GIFREAD_GX_DISP_NONE; break; case 0x01: disp = GIFREAD_GX_DISP_LEAVE; break; case 0x02: disp = GIFREAD_GX_DISP_BG; break; case 0x03: disp = GIFREAD_GX_DISP_PREV; break; default: gr_err(gr,GIFREAD_ERR_GFXCTLEXT_BADDISP,(data[0]>>2)&7); disp = GIFREAD_GX_DISP_NONE; break; } input = ((data[0] & 0x02) ? 1 : 0); delay = (data[2] << 8) | data[1]; transp = (data[0] & 0x01) ? data[3] : -1; if (mustread(gr,&len,1,"graphic control extension data block terminator")) return(0); if (len != 0) { gr_err(gr,GIFREAD_ERR_GFXCTLEXT_BADTERM,len); do { if (mustread(gr,&data[0],len,"broken graphic control extension data")) return(0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,&data[0],len); if (mustread(gr,&len,1,"broken graphic control extension data block length")) return(0); } while (len != 0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,(const unsigned char *)0,0); } if (gr->detail & GIFREAD_DETAIL_GFXCTLEXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_GFXCTLEXT,disp,input,delay,transp); return(1); } static int read_comment_extension(GIFREADER *gr) { unsigned char len; unsigned char data[255]; if (gr->detail & GIFREAD_DETAIL_COMMEXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_COMMEXT); while (1) { if (mustread(gr,&len,1,"comment data block length")) return(0); if (len == 0) break; if (mustread(gr,&data[0],len,"comment data block")) return(0); if (gr->detail & GIFREAD_DETAIL_COMMEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_COMMEXT_DATA,&data[0],len); } if (gr->detail & GIFREAD_DETAIL_COMMEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_COMMEXT_DATA,(const unsigned char *)0,0); return(1); } static int read_application_extension(GIFREADER *gr) { unsigned char len; unsigned char hdata[11]; unsigned char data[255]; if (mustread(gr,&len,1,"application extension data block length")) return(0); if (len != 11) { gr_err(gr,GIFREAD_ERR_APPEXT_HDRSIZE,len); do { if (mustread(gr,&data[0],len,"broken application extension data")) return(0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,&data[0],len); if (mustread(gr,&len,1,"broken application extension data block length")) return(0); } while (len != 0); if (gr->detail & GIFREAD_DETAIL_BROKEN_EXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BROKEN_EXT,(const unsigned char *)0,0); return(1); } if (mustread(gr,&hdata[0],11,"application extension header data")) return(0); if (gr->detail & GIFREAD_DETAIL_APPEXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_APPEXT,&hdata[0]); while (1) { if (mustread(gr,&len,1,"application extension data block length")) return(0); if (len == 0) break; if (mustread(gr,&data[0],len,"application extension data block")) return(0); if (gr->detail & GIFREAD_DETAIL_APPEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_APPEXT_DATA,&data[0],len); } if (gr->detail & GIFREAD_DETAIL_APPEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_APPEXT_DATA,(const unsigned char *)0,0); return(1); } static int read_extension_block(GIFREADER *gr) { unsigned char xcode; unsigned char len; unsigned char data[255]; if (mustread(gr,&xcode,1,"extension code")) return(0); switch (gr->version) { case GIFREAD_VERS_GIF87a: break; default: switch (xcode) { case 0x01: return(read_plain_text_extension(gr)); break; case 0xf9: return(read_graphic_control_extension(gr)); break; case 0xfe: return(read_comment_extension(gr)); break; case 0xff: return(read_application_extension(gr)); break; } break; } if (gr->detail & GIFREAD_DETAIL_UNKEXT) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_UNKEXT,xcode); while (1) { if (mustread(gr,&len,1,"extension data block length")) return(0); if (len == 0) break; if (mustread(gr,&data[0],len,"extension data block")) return(0); if (gr->detail & GIFREAD_DETAIL_UNKEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_UNKEXT_DATA,&data[0],len); } if (gr->detail & GIFREAD_DETAIL_UNKEXT_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_UNKEXT_DATA,(const unsigned char *)0,0); return(1); } static void lzw_new_bits(GIFREADER *gr) { gr->lzw_mask = (1U << gr->lzw_bits) - 1; } static void lzw_cleared(GIFREADER *gr) { gr->lzw_next = gr->lzw_end + 1; gr->lzw_bits = gr->lzw_clearbits; lzw_new_bits(gr); gr->lzw_prevcode = LZW_NIL; } static void lzw_init(GIFREADER *gr, int imgbpp) { int i; gr->lzw_clearbits = (imgbpp > 2) ? imgbpp+1 : 3; gr->lzw_clear = 1 << (gr->lzw_clearbits - 1); gr->lzw_end = gr->lzw_clear + 1; lzw_cleared(gr); for (i=gr->lzw_clear-1;i>=0;i--) { gr->lzw_sufchar[i] = i; gr->lzw_prefcode[i] = LZW_NIL; } gr->lzw_ended = 0; } static void init_image(GIFREADER *gr) { gr->pixels_wanted = gr->img_w * gr->img_h; lzw_init(gr,gr->codesize); gr->blockbuf = 0; gr->bbbits = 0; } static unsigned char lzw_codefirst(GIFREADER *gr, unsigned short int code) { while (code > gr->lzw_end) code = gr->lzw_prefcode[code]; if (code >= gr->lzw_clear) abort(); return(code); } static void lzw_enter(GIFREADER *gr, unsigned short int prev, unsigned char k) { if (prev == LZW_NIL) return; gr->lzw_prefcode[gr->lzw_next] = prev; gr->lzw_sufchar[gr->lzw_next] = k; gr->lzw_next ++; if (gr->lzw_next > gr->lzw_mask) { gr->lzw_bits ++; lzw_new_bits(gr); } } static void lzw_output(GIFREADER *gr, unsigned char k) { if (gr->detail & GIFREAD_DETAIL_PIXELS) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_PIXELS,k); } static void lzw_output_code(GIFREADER *gr, unsigned short int code) { if (code < gr->lzw_clear) { lzw_output(gr,code); return; } if (code <= gr->lzw_end) abort(); lzw_output_code(gr,gr->lzw_prefcode[code]); lzw_output(gr,gr->lzw_sufchar[code]); } static void lzw_codeword(GIFREADER *gr, unsigned short int code) { unsigned short int k; if (gr->detail & GIFREAD_DETAIL_LZW_CODES) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_LZW_CODES,code,gr->lzw_bits); if (code == gr->lzw_clear) { lzw_cleared(gr); return; } if (code == gr->lzw_end) { gr->lzw_ended = 1; return; } if (gr->lzw_next >= 4096) { gr_err(gr,GIFREAD_ERR_LZW_OVERFLOW); lzw_cleared(gr); } if (code == gr->lzw_next) { /* KwKwK special case */ if (gr->lzw_prevcode == LZW_NIL) { gr_err(gr,GIFREAD_ERR_LZW_BAD_KWKWK); return; } k = lzw_codefirst(gr,gr->lzw_prevcode); lzw_enter(gr,gr->lzw_prevcode,k); lzw_output_code(gr,gr->lzw_prevcode); lzw_output(gr,k); } else if (code > gr->lzw_next) { gr_err(gr,GIFREAD_ERR_LZW_BAD_CODE,code); return; } else { k = lzw_codefirst(gr,code); lzw_enter(gr,gr->lzw_prevcode,k); lzw_output_code(gr,code); } gr->lzw_prevcode = code; } static int read_image_data_block(GIFREADER *gr) { unsigned char len; unsigned char block[255]; int i; if (mustread(gr,&len,1,"image data block length")) return(0); if (len == 0) { if (gr->detail & GIFREAD_DETAIL_LZW_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_LZW_DATA,(const unsigned char *)0,0); return(0); } if (mustread(gr,&block[0],len,"image data block")) return(0); if (gr->detail & GIFREAD_DETAIL_LZW_DATA) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_LZW_DATA,&block[0],len); if (! gr->lzw_ended) { for (i=0;idetail & GIFREAD_DETAIL_LZW_BYTES) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_LZW_BYTES,block[i]); gr->blockbuf |= block[i] << gr->bbbits; gr->bbbits += 8; while (gr->bbbits >= gr->lzw_bits) { unsigned short int code; code = gr->blockbuf & gr->lzw_mask; gr->blockbuf >>= gr->lzw_bits; gr->bbbits -= gr->lzw_bits; lzw_codeword(gr,code); if (gr->lzw_ended) break; } } } return(1); } static int read_image_block(GIFREADER *gr) { unsigned char imgdesc[9]; unsigned char resmask; int xcs; if (mustread(gr,&imgdesc[0],9,"image descriptor")) return(0); gr->img_x = (imgdesc[1] << 8) | imgdesc[0]; gr->img_y = (imgdesc[3] << 8) | imgdesc[2]; gr->img_w = (imgdesc[5] << 8) | imgdesc[4]; gr->img_h = (imgdesc[7] << 8) | imgdesc[6]; switch (gr->version) { case GIFREAD_VERS_GIF87a: resmask = 0x38; break; case GIFREAD_VERS_GIF89a: resmask = 0x18; break; default: resmask = 0; break; } if (imgdesc[8] & resmask) { gr_err(gr,GIFREAD_ERR_IMGDESC_RESERVED,imgdesc[8]); imgdesc[8] &= ~resmask; } gr->img_m = (imgdesc[8] >> 7) & 1; gr->img_i = (imgdesc[8] >> 6) & 1; gr->img_sort = (imgdesc[8] >> 5) & 1; gr->img_pixel = imgdesc[8] & 7; gr->img_bpp = gr->img_m ? (gr->img_pixel+1) : (gr->scr_pix+1); if (gr->detail & GIFREAD_DETAIL_IMGDESC) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_IMGDESC,gr->img_x,gr->img_y,gr->img_w,gr->img_h,gr->img_m?gr->img_sort?GIFREAD_CMAP_SORTED:GIFREAD_CMAP_UNSORTED:GIFREAD_CMAP_NONE,gr->img_i,gr->img_m?gr->img_bpp:-1); if (gr->img_m) { if (mustread(gr,&gr->local_map[0],3<img_bpp,"local color map")) return(0); if (gr->detail & GIFREAD_DETAIL_CMAP) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_CMAP,0,gr->scr_pix+1,&gr->local_map[0]); } if (mustread(gr,&gr->codesize,1,"image data code size")) return(0); xcs = (gr->img_bpp==1) ? 2 : gr->img_bpp; if (gr->codesize != xcs) gr_err(gr,GIFREAD_ERR_IMGDESC_CODESIZE,gr->codesize,xcs); init_image(gr); while (read_image_data_block(gr)) ; return(1); } static int skipjunk(GIFREADER *gr) { unsigned char c; gr_err(gr,GIFREAD_ERR_SKIPJUNK); while (1) { if (mustread(gr,&c,1,"junk")) return(0); switch (c) { case 0x2c: case 0x3b: case 0x21: unread1(gr,c); return(1); } } } static int read_block(GIFREADER *gr) { unsigned char btype; if (mustread(gr,&btype,1,"block identifying byte")) return(0); switch (btype) { case 0x21: return(read_extension_block(gr)); break; case 0x2c: return(read_image_block(gr)); break; case 0x3b: return(0); break; } return(skipjunk(gr)); } static void read_trailing_junk(GIFREADER *gr) { unsigned char junk[256]; int n; n = (*gr->ops->read_input)(gr->opsarg,&junk[0],sizeof(junk)); if (n < 0) { gr_err(gr,GIFREAD_ERR_READERROR,"possible trailing junk"); return; } if (n == 0) return; gr_err(gr,GIFREAD_ERR_TRAILJUNK); while (1) { n = (*gr->ops->read_input)(gr->opsarg,&junk[0],sizeof(junk)); if (n < 0) { gr_err(gr,GIFREAD_ERR_READERROR,"trailing junk"); return; } if (n == 0) return; } } GIFREADER *gifreader_open(const GIFREADER_OPS *ops, void *arg, unsigned int detail) { GIFREADER *gr; gr = malloc(sizeof(GIFREADER)); gr->ops = ops; gr->opsarg = arg; gr->detail = detail; gr->flags = 0; return(gr); } void gifreader_read(GIFREADER *gr) { unsigned char signature[6]; unsigned char scrdesc[7]; if (mustread(gr,&signature[0],6,"signature")) return; if (bcmp(&signature[0],"GIF",3)) { if (gr->detail & GIFREAD_DETAIL_SIGNATURE) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_SIGNATURE,&signature[0],GIFREAD_VERS_BAD); gr_err(gr,GIFREAD_ERR_BADSIG); return; } if (!bcmp(&signature[3],"87a",3)) { gr->version = GIFREAD_VERS_GIF87a; } else if (!bcmp(&signature[3],"89a",3)) { gr->version = GIFREAD_VERS_GIF89a; } else { gr->version = GIFREAD_VERS_OTHER; } if (gr->detail & GIFREAD_DETAIL_SIGNATURE) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_SIGNATURE,&signature[0],gr->version); if (mustread(gr,&scrdesc[0],7,"screen descriptor")) return; gr->scr_w = (scrdesc[1] << 8) | scrdesc[0]; gr->scr_h = (scrdesc[3] << 8) | scrdesc[2]; gr->scr_bg = scrdesc[5]; gr->scr_par = scrdesc[6]; gr->scr_m = (scrdesc[4] >> 7) & 1; gr->scr_cr = (scrdesc[4] >> 4) & 7; gr->scr_sort = (scrdesc[4] >> 3) & 1; gr->scr_pix = scrdesc[4] & 7; switch (gr->version) { case GIFREAD_VERS_GIF87a: if (gr->scr_sort || gr->scr_par) { (*gr->ops->error)(gr,GIFREAD_ERR_87a_RESERVED,gr->scr_sort,gr->scr_par); gr->scr_sort = 0; gr->scr_par = 0; } break; } if (gr->detail & GIFREAD_DETAIL_SCRDESC) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_SCRDESC,gr->scr_w,gr->scr_h,gr->scr_m?gr->scr_sort?GIFREAD_CMAP_SORTED:GIFREAD_CMAP_UNSORTED:GIFREAD_CMAP_NONE,gr->scr_cr+1,gr->scr_pix+1,gr->scr_bg,gr->scr_par); if (gr->scr_m) { if (mustread(gr,&gr->global_map[0],3<<(gr->scr_pix+1),"global color map")) return; if (gr->detail & GIFREAD_DETAIL_CMAP) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_CMAP,1,gr->scr_pix+1,&gr->global_map[0]); if (gr->detail & GIFREAD_DETAIL_BACKGROUND) (*gr->ops->detail)(gr->opsarg,GIFREAD_DETAIL_BACKGROUND,gr->scr_bg); } while (read_block(gr)) ; if (! (gr->flags & GRF_FAILED)) read_trailing_junk(gr); } void gifreader_done(GIFREADER *gr) { gr->flags |= GRF_FAILED; free(gr); }