links/gif.c
2021-08-28 18:37:32 +03:00

574 lines
14 KiB
C

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