/* 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

#include "links.h"

struct gif_table_entry {
	unsigned char end_char;
	unsigned char garbage; /* This has nothing common to do with code table:
			this is temporarily used for reverting strings :-) */
	short pointer; /* points onto another entry in table, number 0...4095.
			number -1 means it end there, the end_char is the last
			number -2 means that this entry is no filled in yet. */
};

struct gif_decoder {
	unsigned char *color_map; /* NULL if no color map, otherwise a block of 768 bytes, red, green, blue,
				     in sRGB, describing color slots 0...255. */
	int state; /* State of the automatus finitus recognizing the GIF
		    * format.  0 is initial. */
	/* Image width, height, bits per pixel, bytes per line, number of bit planes */
	int im_width;
	int im_height;
	int im_bpp; /* Bits per pixel (in codestream) */
	int code_size;
	int initial_code_size;
	int remains; /* Used to skip unwanted blocks in raster data */
	struct gif_table_entry table[4096]; /* NULL when not present */
	unsigned char *actual_line; /* Points to actual line in goi->buffer */
	unsigned char tbuf[16]; /* For remembering headers and similar things. */
	int tlen; /* 0 in the beginning . tbuf length */
	int xoff, yoff;
	int interl_dist;
	int bits_read; /* How many bits are already read from the symbol
			* Currently being read */
	int last_code; /* This is somehow used in the decompression algorithm */
	int read_code;
	int CC;
	int EOI;
	int table_pos;
	int first_code;
	int transparent;
};

/****************************** 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) */
static void alloc_color_map(int colors)
{
 struct gif_decoder* deco=global_cimg->decoder;

 if (deco->color_map) mem_free(deco->color_map);
 if ((unsigned)colors > MAXINT / 3 / sizeof(*(deco->color_map))) overalloc();
 deco->color_map=mem_alloc(colors*3*sizeof(*(deco->color_map)));
}

/*
   Initialize code table: construct codes 0...(1<<code_size)-1 with values
   0...(1<<code_size)-1 Codes (1<<code_size) and (1<<code_size)+1 are
   left intact -- they are of no use.
   Initializes CC and EOI
   Zeroes out the last_code. In normal data stream the first code must be
   in the table. However, if corrupted stream is to be received, a cause could
   happen that the first code would be out of table and as last code would
   be used something uninitialized and something very strange could happen
   (drawing a pixel from previous image or an infinite loop in outputting
   string)
*/
static void init_table(void)
{
 int i;
 struct gif_decoder *deco;

 deco=global_cimg->decoder;
 deco->code_size=deco->initial_code_size;
 deco->first_code=1;
 for (i=0;i<1<<deco->code_size;i++)
 {
  deco->table[i].pointer=-1;
  deco->table[i].end_char=(unsigned char)i;
 }
 /* Here i=1<<code_size. */
 deco->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<<deco->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;y<deco->im_height
				&&y<deco->yoff+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,short pointer)
{
	if (deco->table_pos>=4096){
		/* ignore overflows because they may happen */
		/*end_callback_hit=1;*/
		return; /* Overflow */
	}
	deco->table[deco->table_pos].end_char=(unsigned char)end_char;
	deco->table[deco->table_pos].pointer=pointer;
	deco->table_pos++;
	if (deco->table_pos==(1<<deco->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;

 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|=(int)((unsigned long)c<<deco->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<<deco->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. */
static void implant_transparent(struct gif_decoder *deco)
{
	if (deco->transparent >= 0 && deco->transparent < (1 << deco->im_bpp)) {
		if (global_cimg->strip_optimized) {
			compute_background_8(global_cimg, deco->color_map + 3 * deco->transparent);
		} else {
			memcpy(deco->color_map + 3 * deco->transparent, deco->actual_line, 3);
	        }
	}
}

/* Dimensions are in deco->im_width and deco->im_height */
static int 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=(float)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 */
	return header_dimensions_known(global_cimg);
}

/* Accepts one byte from GIF codestream
   Caller is responsible for destorying the decoder when
   end_callback_hit is 1.
*/
static inline void
gif_accept_byte(unsigned char 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(cast_const_char deco->tbuf,"GIF87a",6)
				&&strncmp(cast_const_char 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<<deco->im_bpp);
				deco->state=1;
			}
			/*if (deco->tbuf[10] & 8 || deco->tbuf[12]) {
				goto bad_file;
			}*/
		}
		break;

		case 1: /* Reading global color map -- 3*(1<<im_bpp) bytes in GIF*/
		deco->color_map[deco->tlen]=c;
		deco->tlen++;
		if (deco->tlen>=3*(1<<deco->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;
			if (gif_dimensions_known()) {
				end_callback_hit=1;
				return; /* Bad dimensions */
			}
			if (global_cimg->width && (unsigned)global_cimg->width * (unsigned)global_cimg->buffer_bytes_per_pixel / (unsigned)global_cimg->width != (unsigned)global_cimg->buffer_bytes_per_pixel) overalloc();
			if ((unsigned)global_cimg->width * (unsigned)global_cimg->buffer_bytes_per_pixel > MAXINT) overalloc();
			deco->actual_line=global_cimg->strip_optimized
				?mem_alloc((size_t)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<<deco->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<<deco->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;
}

static void gif_restart_internal(unsigned char *data, int length)
{
	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_error("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 */
