/* gif.c * GIF parser (c) 2002 Karel 'Clock' Kulhavy This file is a part of the Links program, released under GPL. */ /* TODO: remove superfluous deco->im_width and deco->im_height */ #include "cfg.h" #ifdef G #ifdef HAVE_MATH_H #include #endif #include "links.h" /****************************** Functions *************************************/ /* Takes the argument from global_cimg. Does not free the gif_decoder * struct itself. */ void gif_destroy_decoder(struct cached_image *cimg) { struct gif_decoder *deco=cimg->decoder; if ((cimg->state==12||cimg->state==14)&&cimg->strip_optimized) mem_free(deco->actual_line); if (deco->color_map) mem_free(deco->color_map); } /* colors: number of triplets (color entries) */ void alloc_color_map(int colors) { struct gif_decoder* deco=global_cimg->decoder; if (deco->color_map) mem_free(deco->color_map); deco->color_map=mem_alloc(colors*3*sizeof(*(deco->color_map))); } /* Initialize code table: construct codes 0...(1<decoder; deco->code_size=deco->initial_code_size; deco->first_code=1; for (i=0;i<1<code_size;i++) { deco->table[i].pointer=-1; deco->table[i].end_char=i; } /* Here i=1<CC=i; deco->EOI=i+1; deco->table_pos=i+2; for (;i<4096;i++) { deco->table[i].pointer=-2; } deco->code_size++; deco->last_code=0; } /* Outputs a single pixel. if end_callback_hit gets set, do not send any more data in. */ static inline void output_pixel(int c) { struct gif_decoder *deco; struct cached_image *cimg=global_cimg; deco=global_cimg->decoder; if (c>=1<im_bpp){ end_callback_hit=1; return; } deco->actual_line[deco->xoff*3]=deco->color_map[c*3]; deco->actual_line[deco->xoff*3+1]=deco->color_map[c*3+1]; deco->actual_line[deco->xoff*3+2]=deco->color_map[c*3+2]; deco->xoff++; if (deco->xoff>=deco->im_width) { deco->xoff=0; global_cimg->rows_added=1; if (deco->interl_dist==1) { if (global_cimg->strip_optimized){ buffer_to_bitmap_incremental(cimg ,deco->actual_line, 1, deco->yoff, cimg->dregs, 1); }else{ deco->actual_line+=deco->im_width*3; } deco->yoff++; }else{ int skip=deco->interl_dist&15; int n=(deco->interl_dist==24) ?8:(deco->interl_dist>>1); unsigned char *ptr; int y; ptr=deco->actual_line+deco->im_width*3; for (y=deco->yoff+1;yim_height &&yyoff+n;y++){ memcpy(ptr,deco ->actual_line,deco->im_width*3); ptr+=deco->im_width*3; } deco->actual_line+=deco->im_width*3*skip; deco->yoff+=skip; } while (deco->yoff>=deco->im_height) { /* The vertical range is complete. */ if (deco->interl_dist<=2) { end_callback_hit=1; return; }else{ deco->interl_dist=(deco->interl_dist==24) ?8:(deco->interl_dist>>1); deco->yoff=deco->interl_dist>>1; deco->actual_line=global_cimg ->buffer+deco->yoff*3*deco->im_width; } } } } /* Finds the first char of output string for given codeword. */ static inline int find_first(int c) { struct gif_decoder *deco; int p; int i; deco=global_cimg->decoder; for (i=0;i<4096;i++){ p=deco->table[c].pointer; if (p==-1) break; if (p<-1||p>=4096) return 0; c=p; } return deco->table[c].end_char; } /* GIF code Supply a code and it outputs the string for c. if end_callback_hit is set then it should not be called anymore. */ static inline void output_string(int c) { int pos=0; struct gif_decoder *deco; deco=global_cimg->decoder; while(1){ if (pos==4096){ /* Cycle in string */ end_callback_hit=1; return; } deco->table[pos].garbage=deco->table[c].end_char; pos++; c=deco->table[c].pointer; if (c<0) break; /* We are at the end */ } for (pos--;pos>=0;pos--) { output_pixel(deco->table[pos].garbage); if (end_callback_hit) return; } } /* Adds to the code table return value: 0 ok 1 fatal error 2 stop sending data */ static inline void add_table(struct gif_decoder *deco,int end_char,int pointer) { if (deco->table_pos>=4096){ end_callback_hit=1; return; /* Overflow */ } deco->table[deco->table_pos].end_char=end_char; deco->table[deco->table_pos].pointer=pointer; deco->table_pos++; if (deco->table_pos==(1<code_size)&&deco->code_size!=12) { /* Table pos is a power of 2 */ deco->code_size++; } } /* Yout throw inside a codeword and it processes it farther If the code==256, it means that end-of-file came. This is part of GIF code. If sets up end_callback_hit, no more codes should be sent into accept_codee. */ static inline void accept_code(struct gif_decoder *deco,int c) { int k; k=k; /* To suppress warning */ if (c>4096||c<0) return; /* Erroneous code word will be ignored */ if (c==deco->CC) { init_table(); return; } if (c==deco->EOI) { end_callback_hit=1; return; } if (deco->first_code) { deco->first_code=0; /* First code after init_table */ /* Action: output the string for code */ output_string(c); if (end_callback_hit) return; deco->last_code=c; return; } if (c>=deco->table_pos) { /* The code is not in the table */ k=find_first(deco->last_code); } else { /* The code is in code table */ k=find_first(c); } add_table(deco,k,deco->last_code); if (end_callback_hit) return; /* Output the string for code */ output_string(c); if (end_callback_hit) return; deco->last_code=c; /* Update last code. */ } /* You throw inside a byte, it depacks the bits out and makes code and then passes to the decoder (no headers, palettes etc. go through this.) sets end_callback_hit to 1 when no more data should be put in. */ static inline void accept_byte(unsigned char c) { int original_code_size; struct gif_decoder *deco; deco=global_cimg->decoder; deco->read_code|=(unsigned long)c<bits_read; deco->bits_read+=8; while (deco->bits_read>=deco->code_size) { /* We have collected up a whole code word. */ original_code_size=deco->code_size; accept_code(deco,deco->read_code&((1<code_size)-1)); if (end_callback_hit) return; deco->read_code>>=original_code_size; deco->bits_read-=original_code_size; } } /* if deco->transparent >=0, then fill it with transparent colour. * actual line must exist, must be set to the beginning of the image, * and the buffer must be formatted. */ void implant_transparent(struct gif_decoder *deco) { if (deco->transparent>=0&&deco->transparent<(1<im_bpp)){ if (global_cimg->strip_optimized){ compute_background_8(deco->color_map+3*deco->transparent, global_cimg); }else{ memcpy(deco->color_map+3*deco->transparent ,deco->actual_line,3); } } } /* Dimensions are in deco->im_width and deco->im_height */ void gif_dimensions_known(void) { struct gif_decoder *deco; deco=global_cimg->decoder; global_cimg->buffer_bytes_per_pixel=3; global_cimg->width=deco->im_width; global_cimg->height=deco->im_height; global_cimg->red_gamma=global_cimg->green_gamma =global_cimg->blue_gamma=sRGB_gamma; global_cimg->strip_optimized=(deco->interl_dist==1 &&deco->im_width*deco->im_height>=65536); /* Run strip_optimized only from 65536 pixels up */ header_dimensions_known(global_cimg); } /* Accepts one byte from GIF codestream Caller is responsible for destorying the decoder when end_callback_hit is 1. */ void inline gif_accept_byte(int c) { struct gif_decoder *deco; deco=global_cimg->decoder; switch(deco->state) { case 0: /* Reading signature and screen descriptor -- 13 bytes */ deco->tbuf[deco->tlen]=c; deco->tlen++; if (deco->tlen>=13){ if (strncmp(deco->tbuf,"GIF87a",6) &&strncmp(deco->tbuf,"GIF89a",6)){ end_callback_hit=1; return; /* Invalid GIF header */ } deco->im_bpp=(deco->tbuf[10]&7)+1; deco->tlen=0; if (deco->tbuf[10]<128){ /* No global color map follows */ deco->state=2; /* Reading image data */ }else{ /* Read global color map */ alloc_color_map(1<im_bpp); deco->state=1; } } break; case 1: /* Reading global color map -- 3*(1<color_map[deco->tlen]=c; deco->tlen++; if (deco->tlen>=3*(1<im_bpp)){ deco->state=2; deco->tlen=0; } break; case 2: /* Reading garbage before and one ',' or '!' in GIF */ switch (c){ case ',': if (deco->im_width){ /* Double header encountered */ end_callback_hit=1; return; } deco->state=3; deco->tlen=0; break; case '!': deco->state=7; break; } break; case 3: /* Reading image descriptor -- 9 bytes in GIF */ deco->tbuf[deco->tlen]=c; deco->tlen++; if (deco->tlen>=9){ deco->im_width=deco->tbuf[4]+(deco->tbuf[5]<<8); deco->im_height=deco->tbuf[6]+(deco->tbuf[7]<<8); if (deco->im_width<=0||deco->im_height<=0){ end_callback_hit=1; return; /* Bad dimensions */ } if (deco->tbuf[8]&64){ /* Interlaced order */ deco->interl_dist=24; /* Actually 8, the 16 indicates * it is the first pass. */ }else deco->interl_dist=1; gif_dimensions_known(); deco->actual_line=global_cimg->strip_optimized ?mem_alloc(global_cimg->width*global_cimg ->buffer_bytes_per_pixel) :global_cimg->buffer; if (deco->tbuf[8]&128){ deco->im_bpp=1+(deco->tbuf[8]&7); deco->tlen=0; alloc_color_map(1<im_bpp); deco->state=4; }else{ deco->state=5; deco->tlen=0; deco->xoff=0; deco->yoff=0; } } break; case 4: /* Reading local colormap in GIF */ deco->color_map[deco->tlen]=c; deco->tlen++; if (deco->tlen>=3*(1<im_bpp)){ deco->state=5; deco->xoff=0; deco->yoff=0; } break; case 5: /* Reading code size block in GIF */ deco->initial_code_size=c; if (deco->initial_code_size<=1||deco->initial_code_size>8){ end_callback_hit=1; return; /* Invalid initial code size */ } if (!deco->color_map){ end_callback_hit=1; return; } deco->bits_read=0; deco->read_code=0; /* Nothing read */ init_table(); /* Decoding is about to begin sets up code_size. */ deco->state=6; deco->tlen=0; deco->remains=0; implant_transparent(deco); break; case 6: /* Reading image data in GIF */ if (!deco->remains){ /* This byte is a count byte. Feed it into remains. */ deco->remains=c; if (!c){ /* 0 count = end of data. */ end_callback_hit=1; /* Don't send any following data */ return; } } else { accept_byte(c); if (end_callback_hit) return; /* No more data wanted */ deco->remains--; } break; case 7: /* Reading a byte after '!' in GIF */ if (c==0xf9) deco->state=9; else deco->state=8; deco->remains=0; deco->tlen=0; break; case 8: /* Skipping ignored blocks in GIF */ case 9: /* Graphics control block block size */ if (!deco->remains) { /* Byte count awaited */ if (!c) { /* End. :-) */ deco->state=2; /* Go and wait for ',' */ deco->tlen=0; break; } deco->remains=c; } else { if (deco->state==9&&deco->tlen==3) deco->transparent=deco->transparent?c:-1; if (deco->state==9&&deco->tlen==0) deco->transparent=c&1; deco->remains--; deco->tlen++; } break; } } void gif_start(struct cached_image *cimg) { struct gif_decoder *deco; deco=mem_calloc(sizeof(*deco)); deco->transparent=-1; cimg->decoder=deco; } void gif_restart_internal(unsigned char *data, int length) { struct gif_decoder *deco; deco=global_cimg->decoder; while(length){ gif_accept_byte(*data); if (end_callback_hit) return; data++; length--; } } void gif_restart(unsigned char *data, int length) { #ifdef DEBUG if (!global_cimg->decoder) internal("NULL decoder in gif_restart"); #endif /* #ifdef DEBUG */ end_callback_hit=0; gif_restart_internal(data, length); if (end_callback_hit) img_end(global_cimg); } #endif /* #ifdef G */