741 lines
15 KiB
C
741 lines
15 KiB
C
/*
|
|
* gif.c:
|
|
*
|
|
* Modified to be called so that GIFs can be decoded "on-the-fly".
|
|
* Sort of. Reparses lots of data it doesn't have to.
|
|
* John Kilburg <john@cs.unlv.edu>
|
|
*
|
|
* adapted from code by kirk johnson (tuna@athena.mit.edu). most of this
|
|
* code is unchanged. -- jim frost 12.31.89
|
|
*
|
|
* gifin.c
|
|
* kirk johnson
|
|
* november 1989
|
|
*
|
|
* routines for reading GIF files
|
|
*
|
|
* Copyright 1989 Kirk L. Johnson
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this
|
|
* software and its documentation for any purpose is hereby granted
|
|
* without fee, provided that the above copyright notice appear in
|
|
* all copies and that both that copyright notice and this
|
|
* permission notice appear in supporting documentation. The
|
|
* author makes no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without express
|
|
* or implied warranty.
|
|
*
|
|
* THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
|
|
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
#include "port_before.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include "port_after.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "imagep.h"
|
|
#include "image_endian.h"
|
|
#include "image_format.h"
|
|
#include "gifp.h"
|
|
|
|
/* start line for interlacing */
|
|
static int interlace_start[4] = { 0, 4, 2, 1 };
|
|
|
|
/* rate at which we accelerate vertically */
|
|
static int interlace_rate[4] = { 8, 8, 4, 2 };
|
|
|
|
/* how often to copy the line */
|
|
static int interlace_copy[4] = { 8, 4, 2, 1 };
|
|
|
|
static int gs_read_image_header _ArgProto((gifState *));
|
|
static int gs_read_sig _ArgProto((gifState *));
|
|
static int gs_read_data_block _ArgProto((gifState *));
|
|
static int gs_read _ArgProto((gifState *, byte **, int));
|
|
static int gs_open_file _ArgProto((gifState *));
|
|
static int gs_open_image _ArgProto((gifState *));
|
|
static int gs_get_pixel _ArgProto((gifState *, int *));
|
|
static int gs_init_decoder _ArgProto((gifState *));
|
|
|
|
/*
|
|
* read GIF data
|
|
*/
|
|
static int
|
|
gs_read(gs, b, blen)
|
|
gifState *gs;
|
|
byte **b;
|
|
int blen;
|
|
{
|
|
if (gs->datalen < blen + gs->pos) return(0);
|
|
|
|
*b = gs->data + gs->pos;
|
|
gs->pos += blen;
|
|
|
|
return(blen);
|
|
}
|
|
|
|
/*
|
|
* gs_read_sig
|
|
*
|
|
*/
|
|
static int
|
|
gs_read_sig(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
|
|
/* check GIF signature */
|
|
if (gs_read(gs, &b, GIF_SIG_LEN) != GIF_SIG_LEN) return(GS_NEED_DATA);
|
|
|
|
if ((strncmp((char *)b, GIF_SIG, strlen(GIF_SIG)) != 0) &&
|
|
(strncmp((char *)b, GIF_SIG_89, strlen(GIF_SIG_89)) != 0))
|
|
{
|
|
return(GS_ERR_BAD_SIG);
|
|
}
|
|
|
|
gs->state = GS_OPEN_FILE;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_open_file
|
|
*
|
|
* open a GIF file
|
|
*/
|
|
static int
|
|
gs_open_file(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
|
|
/* read screen descriptor */
|
|
if (gs_read(gs, &b, GIF_SD_SIZE) != GIF_SD_SIZE) return(GS_NEED_DATA);
|
|
|
|
/* decode screen descriptor */
|
|
gs->rast_width = (b[1] << 8) + b[0];
|
|
gs->rast_height = (b[3] << 8) + b[2];
|
|
gs->g_cmap_flag = (b[4] & 0x80) ? 1 : 0;
|
|
gs->color_bits = ((b[4] & 0x70) >> 4) + 1;
|
|
gs->g_pixel_bits = (b[4] & 0x07) + 1;
|
|
gs->bg_color = b[5];
|
|
|
|
/* load global colormap */
|
|
if (gs->g_cmap_flag)
|
|
{
|
|
gs->g_ncolors = (1 << gs->g_pixel_bits);
|
|
gs->g_ncolors_pos = 0;
|
|
gs->state = GS_LOAD_G_CMAP;
|
|
}
|
|
else
|
|
{
|
|
gs->state = GS_OPEN_IMAGE;
|
|
gs->g_ncolors_pos = 0;
|
|
gs->g_ncolors = 0;
|
|
}
|
|
|
|
gs->have_dimensions = 1;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_init_decoder
|
|
*
|
|
* This isn't a separate state.
|
|
*/
|
|
static int
|
|
gs_init_decoder(gs)
|
|
gifState *gs;
|
|
{
|
|
int i;
|
|
byte *b;
|
|
|
|
if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA);
|
|
|
|
gs->root_size = (int)(*b);
|
|
gs->clr_code = 1 << gs->root_size;
|
|
gs->eoi_code = gs->clr_code + 1;
|
|
gs->table_size = gs->clr_code + 2;
|
|
gs->code_size = gs->root_size + 1;
|
|
gs->code_mask = (1 << gs->code_size) - 1;
|
|
gs->work_bits = 0;
|
|
gs->work_data = 0;
|
|
gs->buf_idx = 0;
|
|
gs->buf_cnt = 0;
|
|
gs->first = 0;
|
|
|
|
/* initialize string table */
|
|
for (i = 0; i < STAB_SIZE; i++)
|
|
{
|
|
gs->prefix[i] = NULL_CODE;
|
|
gs->extnsn[i] = i;
|
|
}
|
|
|
|
/* initialize pixel stack */
|
|
gs->pstk_idx = 0;
|
|
|
|
gs->state = GS_MAKE_IMAGE;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_open_image
|
|
*/
|
|
static int
|
|
gs_open_image(gs)
|
|
gifState *gs;
|
|
{
|
|
int separator;
|
|
byte *b;
|
|
int rval;
|
|
int pos = gs->pos;
|
|
|
|
if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA);
|
|
|
|
separator = (int)(*b);
|
|
if (separator == GIF_EXTENSION)
|
|
{
|
|
/* get the extension function byte */
|
|
if (gs_read(gs, &b, 1) != 1)
|
|
{
|
|
gs->pos = pos;
|
|
return(GS_NEED_DATA);
|
|
}
|
|
|
|
if (*b == 0xf9)
|
|
{
|
|
if ((rval = gs_read_data_block(gs)) != GS_SUCCESS)
|
|
{
|
|
gs->pos = pos;
|
|
return(rval);
|
|
}
|
|
if (gs->buf[0] & 0x1) gs->transparent = gs->buf[3];
|
|
}
|
|
else
|
|
{
|
|
if ((rval = gs_read_data_block(gs)) != GS_SUCCESS)
|
|
{
|
|
gs->pos = pos;
|
|
return(rval);
|
|
}
|
|
}
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
else if (separator == GIF_TERMINATOR) return(GS_DONE);
|
|
/*
|
|
* If it's a broken GIF, just keep scanning till we meet a
|
|
* separator we recognise.
|
|
*/
|
|
else if (separator != GIF_SEPARATOR) return(GS_SUCCESS);
|
|
|
|
gs->state = GS_READ_IMAGE_HEADER;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_read_image_header
|
|
*/
|
|
static int
|
|
gs_read_image_header(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
|
|
/* read image descriptor */
|
|
if (gs_read(gs, &b, GIF_ID_SIZE) != GIF_ID_SIZE) return(GS_NEED_DATA);
|
|
|
|
/* decode image descriptor */
|
|
gs->img_left = (b[1] << 8) + b[0];
|
|
gs->img_top = (b[3] << 8) + b[2];
|
|
gs->img_width = (b[5] << 8) + b[4];
|
|
gs->img_height = (b[7] << 8) + b[6];
|
|
gs->l_cmap_flag = (b[8] & 0x80) ? 1 : 0;
|
|
gs->interlace_flag = (b[8] & 0x40) ? 1 : 0;
|
|
gs->l_pixel_bits = (b[8] & 0x07) + 1;
|
|
|
|
/* load local colormap */
|
|
if (gs->l_cmap_flag)
|
|
{
|
|
gs->l_ncolors = (1 << gs->l_pixel_bits);
|
|
gs->l_ncolors_pos = 0;
|
|
gs->state = GS_LOAD_L_CMAP;
|
|
}
|
|
else
|
|
{
|
|
gs->l_ncolors = 0;
|
|
gs->l_ncolors_pos = 0;
|
|
gs->state = GS_INIT_DECODER;
|
|
}
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_load_l_cmap
|
|
*
|
|
* load a local colormap from the input stream
|
|
*/
|
|
static int
|
|
gs_load_l_cmap(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
|
|
if (gs_read(gs, &b, 3) != 3) return(GS_NEED_DATA);
|
|
|
|
gs->l_cmap[GIF_RED][gs->l_ncolors_pos] = b[GIF_RED] << 8;
|
|
gs->l_cmap[GIF_GRN][gs->l_ncolors_pos] = b[GIF_GRN] << 8;
|
|
gs->l_cmap[GIF_BLU][gs->l_ncolors_pos] = b[GIF_BLU] << 8;
|
|
|
|
gs->l_ncolors_pos++;
|
|
if (gs->l_ncolors_pos == gs->l_ncolors) gs->state = GS_INIT_DECODER;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_load_g_cmap
|
|
*
|
|
* load a global colormap from the input stream
|
|
*/
|
|
static int
|
|
gs_load_g_cmap(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
|
|
if (gs_read(gs, &b, 3) != 3) return(GS_NEED_DATA);
|
|
|
|
gs->g_cmap[GIF_RED][gs->g_ncolors_pos] = b[GIF_RED] << 8;
|
|
gs->g_cmap[GIF_GRN][gs->g_ncolors_pos] = b[GIF_GRN] << 8;
|
|
gs->g_cmap[GIF_BLU][gs->g_ncolors_pos] = b[GIF_BLU] << 8;
|
|
|
|
gs->g_ncolors_pos++;
|
|
if (gs->g_ncolors_pos == gs->g_ncolors) gs->state = GS_OPEN_IMAGE;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_read_data_block
|
|
*
|
|
* read a new data block from the input stream
|
|
*/
|
|
static int
|
|
gs_read_data_block(gs)
|
|
gifState *gs;
|
|
{
|
|
byte *b;
|
|
int cnt;
|
|
int pos = gs->pos;
|
|
|
|
/* read the data block header */
|
|
if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA);
|
|
cnt = (int)(*b);
|
|
|
|
/* read the data block body */
|
|
if (gs_read(gs, &gs->buf, cnt) != cnt)
|
|
{
|
|
gs->pos = pos;
|
|
return(GS_NEED_DATA);
|
|
}
|
|
|
|
gs->buf_idx = 0;
|
|
gs->buf_cnt = cnt;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_make_image
|
|
*/
|
|
static int
|
|
gs_make_image(gs)
|
|
gifState *gs;
|
|
{
|
|
Image *image;
|
|
int i;
|
|
bool gray = true;
|
|
|
|
if (gs->l_cmap_flag)
|
|
{
|
|
image = newRGBImage(gs->img_width, gs->img_height, gs->l_pixel_bits);
|
|
if (!image) return(GS_ERR_NOMEM);
|
|
|
|
image->rgb.red = gs->l_cmap[GIF_RED];
|
|
image->rgb.green = gs->l_cmap[GIF_GRN];
|
|
image->rgb.blue = gs->l_cmap[GIF_BLU];
|
|
image->rgb.used = gs->l_ncolors;
|
|
}
|
|
else
|
|
{
|
|
image = newRGBImage(gs->img_width, gs->img_height, gs->g_pixel_bits);
|
|
if (!image) return(GS_ERR_NOMEM);
|
|
|
|
image->rgb.red = gs->g_cmap[GIF_RED];
|
|
image->rgb.green = gs->g_cmap[GIF_GRN];
|
|
image->rgb.blue = gs->g_cmap[GIF_BLU];
|
|
image->rgb.used = gs->g_ncolors;
|
|
}
|
|
|
|
for(i = 0; i < image->rgb.used; i++)
|
|
if(image->rgb.red[i] >> 8 != image->rgb.green[i] >> 8 ||
|
|
image->rgb.red[i] >> 8 != image->rgb.blue[i] >> 8)
|
|
{
|
|
gray = false;
|
|
break;
|
|
}
|
|
if(gray) image->type = IGRAY;
|
|
|
|
image->transparent = gs->transparent;
|
|
gs->image = image;
|
|
gs->state = GS_DECODE_DATA;
|
|
image->x = 0;
|
|
image->pass = 0;
|
|
if (gs->interlace_flag) image->y = interlace_start[image->pass];
|
|
else image->y = 0;
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_get_pixel
|
|
*
|
|
* try to read next pixel from the raster, return result in *pel
|
|
*/
|
|
static int
|
|
gs_get_pixel(gs, pel)
|
|
gifState *gs;
|
|
int *pel;
|
|
{
|
|
int code;
|
|
int rval;
|
|
|
|
/* decode until there are some pixels on the pixel stack */
|
|
while (gs->pstk_idx == 0)
|
|
{
|
|
while (gs->work_bits < gs->code_size)
|
|
{
|
|
int pos = gs->pos;
|
|
|
|
if (gs->buf_idx == gs->buf_cnt)
|
|
{
|
|
if ((rval = gs_read_data_block(gs)) != GS_SUCCESS)
|
|
{
|
|
gs->pos = pos;
|
|
return(rval);
|
|
}
|
|
}
|
|
|
|
gs->work_data |= ((unsigned) gs->buf[gs->buf_idx++]) << gs->work_bits;
|
|
gs->work_bits += 8;
|
|
}
|
|
|
|
/* get the next code */
|
|
code = gs->work_data & gs->code_mask;
|
|
gs->work_data >>= gs->code_size;
|
|
gs->work_bits -= gs->code_size;
|
|
|
|
/* interpret the code */
|
|
if (code > gs->table_size || code == gs->eoi_code)
|
|
{
|
|
/* Format Error */
|
|
return(GS_ERR_EOF);
|
|
}
|
|
|
|
if (code == gs->clr_code)
|
|
{
|
|
/* reset decoder */
|
|
gs->code_size = gs->root_size + 1;
|
|
gs->code_mask = (1 << gs->code_size) - 1;
|
|
gs->table_size = gs->clr_code + 2;
|
|
gs->prev_code = NULL_CODE;
|
|
continue;
|
|
}
|
|
|
|
if (gs->prev_code == NULL_CODE)
|
|
{
|
|
gs->pstk[gs->pstk_idx++] = gs->extnsn[code];
|
|
gs->prev_code = code;
|
|
gs->first = code;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
int in_code = code;
|
|
|
|
if (code == gs->table_size)
|
|
{
|
|
gs->pstk[gs->pstk_idx++] = gs->first;
|
|
code = gs->prev_code;
|
|
}
|
|
while (code > gs->clr_code)
|
|
{
|
|
gs->pstk[gs->pstk_idx++] = gs->extnsn[code];
|
|
code = gs->prefix[code];
|
|
}
|
|
gs->first = gs->extnsn[code];
|
|
/* Add a new string to the string table. */
|
|
if (gs->table_size >= PSTK_SIZE)
|
|
return(GS_ERR_EOF);
|
|
gs->pstk[gs->pstk_idx++] = gs->first;
|
|
gs->prefix[gs->table_size] = gs->prev_code;
|
|
gs->extnsn[gs->table_size] = gs->first;
|
|
gs->table_size ++;
|
|
if (((gs->table_size & gs->code_mask)) == 0
|
|
&& (gs->table_size < PSTK_SIZE))
|
|
{
|
|
gs->code_size ++;
|
|
gs->code_mask += gs->table_size;
|
|
}
|
|
gs->prev_code = in_code;
|
|
}
|
|
}
|
|
|
|
*pel = (int)gs->pstk[--gs->pstk_idx];
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gs_decode_data
|
|
*
|
|
* TODO: This routine looks like something of a performance catastrophe
|
|
*/
|
|
static int
|
|
gs_decode_data(gs)
|
|
gifState *gs;
|
|
{
|
|
Image *image = gs->image;
|
|
int pixel;
|
|
byte *pixptr;
|
|
int rval;
|
|
int xpix;
|
|
|
|
if (gs->interlace_flag)
|
|
{
|
|
if (image->x == image->width)
|
|
{
|
|
image->y += interlace_rate[image->pass];
|
|
while (image->y >= image->height)
|
|
{
|
|
image->pass++;
|
|
if (image->pass == 4) return(GS_DONE);
|
|
image->y = interlace_start[image->pass];
|
|
}
|
|
image->x = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (image->x == image->width)
|
|
{
|
|
image->y++;
|
|
image->x = 0;
|
|
}
|
|
if (image->y == image->height) return(GS_DONE);
|
|
}
|
|
|
|
xpix = image->y * image->bytes_per_line +
|
|
(image->x * image->pixlen) / CHAR_BITS;
|
|
|
|
if (xpix < image->height * image->bytes_per_line)
|
|
{
|
|
pixptr = image->data + xpix;
|
|
if ((rval = gs_get_pixel(gs, &pixel)) != GS_SUCCESS) return(rval);
|
|
if(image->pixlen == 1)
|
|
{
|
|
int bitpos = image->x & 7;
|
|
if (bitpos == 0) *pixptr = 0;
|
|
# ifdef CHIMERA_LITTLE_ENDIAN
|
|
pixel <<= bitpos;
|
|
# else
|
|
pixel <<= 7 - bitpos;
|
|
# endif
|
|
*pixptr |= pixel;
|
|
}
|
|
else /* pixlen is 8 bits */
|
|
{
|
|
*pixptr = pixel;
|
|
}
|
|
}
|
|
|
|
image->x++;
|
|
|
|
if (image->x == image->width)
|
|
{
|
|
if (gs->interlace_flag && interlace_copy[image->pass] > 1)
|
|
{
|
|
byte *origline = image->data + image->y * image->bytes_per_line;
|
|
byte *copyline = origline + image->bytes_per_line;
|
|
int i;
|
|
for (i = 1;
|
|
i < interlace_copy[image->pass] && image->y + i < image->height;
|
|
i++, copyline += image->bytes_per_line)
|
|
{
|
|
memcpy(copyline, origline, image->bytes_per_line);
|
|
}
|
|
if (gs->lineProc != NULL)
|
|
(gs->lineProc)(gs->closure,
|
|
image->y,
|
|
MIN(image->y + interlace_copy[image->pass] - 1,
|
|
image->height - 1));
|
|
}
|
|
else
|
|
{
|
|
if (gs->lineProc != NULL)
|
|
(gs->lineProc)(gs->closure, image->y, image->y);
|
|
}
|
|
}
|
|
|
|
return(GS_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* gifDestroy
|
|
*
|
|
* This doesn't do the whole job!
|
|
*/
|
|
static void
|
|
gifDestroy(pointer)
|
|
void *pointer;
|
|
{
|
|
gifState *gs = (gifState *)pointer;
|
|
if (gs->image)
|
|
freeImage(gs->image);
|
|
gs->image = 0;
|
|
|
|
if (gs != NULL) free_mem(gs);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* gifAddData
|
|
*
|
|
* 0 success
|
|
* 1 needs more data
|
|
* -1 error
|
|
*
|
|
* Assumes data is the address of the beginning of the GIF data and len
|
|
* is the total length.
|
|
*/
|
|
static int
|
|
gifAddData(pointer, data, len, data_ended)
|
|
void *pointer;
|
|
byte *data;
|
|
int len;
|
|
bool data_ended;
|
|
{
|
|
gifState *gs = (gifState *)pointer;
|
|
int rval;
|
|
int pos;
|
|
|
|
gs->data = data;
|
|
gs->datalen = len;
|
|
|
|
for ( ; ; )
|
|
{
|
|
pos = gs->pos; /* save in case we need to rewind */
|
|
switch (gs->state)
|
|
{
|
|
case GS_READ_SIG:
|
|
rval = gs_read_sig(gs);
|
|
break;
|
|
case GS_OPEN_FILE:
|
|
rval = gs_open_file(gs);
|
|
break;
|
|
case GS_OPEN_IMAGE:
|
|
if ((rval = gs_open_image(gs)) == GS_DONE) return(0);
|
|
break;
|
|
case GS_READ_IMAGE_HEADER:
|
|
rval = gs_read_image_header(gs);
|
|
break;
|
|
case GS_LOAD_G_CMAP:
|
|
rval = gs_load_g_cmap(gs);
|
|
break;
|
|
case GS_LOAD_L_CMAP:
|
|
rval = gs_load_l_cmap(gs);
|
|
break;
|
|
case GS_MAKE_IMAGE:
|
|
rval = gs_make_image(gs);
|
|
break;
|
|
case GS_DECODE_DATA:
|
|
if ((rval = gs_decode_data(gs)) == GS_DONE) return(0);
|
|
break;
|
|
case GS_INIT_DECODER:
|
|
rval = gs_init_decoder(gs);
|
|
break;
|
|
default:
|
|
rval = GS_ERR_BAD_STATE;
|
|
}
|
|
if (rval == GS_NEED_DATA)
|
|
{
|
|
gs->pos = pos;
|
|
if(data_ended) return -1;
|
|
return(1);
|
|
}
|
|
else if (rval != GS_SUCCESS) return(-1);
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
|
|
static Image *
|
|
gifGetImage(void *pointer)
|
|
{
|
|
gifState *gs = (gifState *)pointer;
|
|
return gs->image;
|
|
}
|
|
|
|
/*
|
|
* gifInit
|
|
*
|
|
* Initialize GIF reader state
|
|
*/
|
|
void
|
|
gifInit(lineProc, closure, if_vector)
|
|
FormatLineProc lineProc;
|
|
void *closure;
|
|
struct ifs_vector *if_vector;
|
|
{
|
|
gifState *gs;
|
|
|
|
gs = (gifState *)alloc_mem(sizeof(gifState));
|
|
memset(gs, 0, sizeof(gifState));
|
|
gs->state = GS_READ_SIG;
|
|
gs->transparent = -1;
|
|
gs->lineProc = lineProc;
|
|
gs->closure = closure;
|
|
|
|
if_vector->image_format_closure = (void *)gs;
|
|
if_vector->initProc = &gifInit;
|
|
if_vector->destroyProc = &gifDestroy;
|
|
if_vector->addDataProc = &gifAddData;
|
|
if_vector->getImageProc = &gifGetImage;
|
|
}
|
|
|
|
|
|
|
|
|