#include "image.h"
#define GIF_INTERLACE 0x40
#define GIF_COLORMAP 0x80
typedef ib_t gif_cmap_t[256][4];
typedef short gif_table_t[4096];
static int gif_eof = 0;
static int gif_read_cmap(FILE *fp, int ncolors, gif_cmap_t cmap,
int *gray);
static int gif_get_block(FILE *fp, unsigned char *buffer);
static int gif_get_code (FILE *fp, int code_size, int first_time);
static int gif_read_lzw(FILE *fp, int first_time, int input_code_size);
static int gif_read_image(FILE *fp, image_t *img, gif_cmap_t cmap,
int interlace);
int
ImageReadGIF(image_t *img,
FILE *fp,
int primary,
int secondary,
int saturation,
int hue,
const ib_t *lut)
{
unsigned char buf[1024];
gif_cmap_t cmap;
int i,
bpp,
gray,
ncolors,
transparent;
if (primary == IMAGE_RGB_CMYK)
primary = IMAGE_RGB;
fread(buf, 13, 1, fp);
img->xsize = (buf[7] << 8) | buf[6];
img->ysize = (buf[9] << 8) | buf[8];
ncolors = 2 << (buf[10] & 0x07);
gray = primary == IMAGE_BLACK || primary == IMAGE_WHITE;
if (buf[10] & GIF_COLORMAP)
if (gif_read_cmap(fp, ncolors, cmap, &gray))
{
fclose(fp);
return (-1);
}
transparent = -1;
for (;;)
{
switch (getc(fp))
{
case ';' :
fclose(fp);
return (-1);
case '!' :
buf[0] = getc(fp);
if (buf[0] == 0xf9)
{
gif_get_block(fp, buf);
if (buf[0] & 1)
transparent = buf[3];
}
while (gif_get_block(fp, buf) != 0);
break;
case ',' :
fread(buf, 9, 1, fp);
if (buf[8] & GIF_COLORMAP)
{
ncolors = 2 << (buf[8] & 0x07);
gray = primary == IMAGE_BLACK || primary == IMAGE_WHITE;
if (gif_read_cmap(fp, ncolors, cmap, &gray))
{
fclose(fp);
return (-1);
}
}
if (transparent >= 0)
{
cmap[transparent][0] = 255;
cmap[transparent][1] = 255;
cmap[transparent][2] = 255;
}
if (gray)
{
switch (secondary)
{
case IMAGE_CMYK :
for (i = ncolors - 1; i >= 0; i --)
ImageWhiteToCMYK(cmap[i], cmap[i], 1);
break;
case IMAGE_CMY :
for (i = ncolors - 1; i >= 0; i --)
ImageWhiteToCMY(cmap[i], cmap[i], 1);
break;
case IMAGE_BLACK :
for (i = ncolors - 1; i >= 0; i --)
ImageWhiteToBlack(cmap[i], cmap[i], 1);
break;
case IMAGE_WHITE :
break;
case IMAGE_RGB :
for (i = ncolors - 1; i >= 0; i --)
ImageWhiteToRGB(cmap[i], cmap[i], 1);
break;
}
img->colorspace = secondary;
}
else
{
if (hue != 0 || saturation != 100)
for (i = ncolors - 1; i >= 0; i --)
ImageRGBAdjust(cmap[i], 1, saturation, hue);
switch (primary)
{
case IMAGE_CMYK :
for (i = ncolors - 1; i >= 0; i --)
ImageRGBToCMYK(cmap[i], cmap[i], 1);
break;
case IMAGE_CMY :
for (i = ncolors - 1; i >= 0; i --)
ImageRGBToCMY(cmap[i], cmap[i], 1);
break;
case IMAGE_BLACK :
for (i = ncolors - 1; i >= 0; i --)
ImageRGBToBlack(cmap[i], cmap[i], 1);
break;
case IMAGE_WHITE :
for (i = ncolors - 1; i >= 0; i --)
ImageRGBToWhite(cmap[i], cmap[i], 1);
break;
case IMAGE_RGB :
break;
}
img->colorspace = primary;
}
if (lut)
{
bpp = ImageGetDepth(img);
for (i = ncolors - 1; i >= 0; i --)
ImageLut(cmap[i], bpp, lut);
}
img->xsize = (buf[5] << 8) | buf[4];
img->ysize = (buf[7] << 8) | buf[6];
if (img->xsize == 0 || img->ysize == 0)
{
fprintf(stderr, "ERROR: Bad GIF image dimensions: %dx%d\n",
img->xsize, img->ysize);
fclose(fp);
return (1);
}
i = gif_read_image(fp, img, cmap, buf[8] & GIF_INTERLACE);
fclose(fp);
return (i);
}
}
}
static int
gif_read_cmap(FILE *fp,
int ncolors,
gif_cmap_t cmap,
int *gray)
{
int i;
for (i = 0; i < ncolors; i ++)
if (fread(cmap[i], 3, 1, fp) < 1)
return (-1);
for (i = 0; i < ncolors; i ++)
if (cmap[i][0] != cmap[i][1] || cmap[i][1] != cmap[i][2])
break;
if (i == ncolors)
{
*gray = 1;
return (0);
}
if (*gray)
for (i = 0; i < ncolors; i ++)
cmap[i][0] = (cmap[i][0] * 31 + cmap[i][1] * 61 + cmap[i][2] * 8) / 100;
return (0);
}
static int
gif_get_block(FILE *fp,
unsigned char *buf)
{
int count;
if ((count = getc(fp)) == EOF)
{
gif_eof = 1;
return (-1);
}
else if (count == 0)
gif_eof = 1;
else if (fread(buf, 1, count, fp) < count)
{
gif_eof = 1;
return (-1);
}
else
gif_eof = 0;
return (count);
}
static int
gif_get_code(FILE *fp,
int code_size,
int first_time)
{
unsigned i, j,
ret;
int count;
static unsigned char buf[280];
static unsigned curbit,
lastbit,
done,
last_byte;
static const unsigned char bits[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
};
if (first_time)
{
curbit = 0;
lastbit = 0;
last_byte = 0;
done = 0;
return (0);
}
if ((curbit + code_size) >= lastbit)
{
if (done)
return (-1);
if (last_byte > 1)
{
buf[0] = buf[last_byte - 2];
buf[1] = buf[last_byte - 1];
last_byte = 2;
}
else if (last_byte == 1)
{
buf[0] = buf[last_byte - 1];
last_byte = 1;
}
if ((count = gif_get_block (fp, buf + last_byte)) <= 0)
{
done = 1;
return (-1);
}
curbit = (curbit - lastbit) + 8 * last_byte;
last_byte += count;
lastbit = last_byte * 8;
}
ret = 0;
for (ret = 0, i = curbit + code_size - 1, j = code_size;
j > 0;
i --, j --)
ret = (ret << 1) | ((buf[i / 8] & bits[i & 7]) != 0);
curbit += code_size;
return ret;
}
static int
gif_read_lzw(FILE *fp,
int first_time,
int input_code_size)
{
int i,
code,
incode;
static short fresh = 0,
code_size,
set_code_size,
max_code,
max_code_size,
firstcode,
oldcode,
clear_code,
end_code,
*stack = NULL,
*sp;
static gif_table_t *table = NULL;
if (first_time)
{
set_code_size = input_code_size;
code_size = set_code_size + 1;
clear_code = 1 << set_code_size;
end_code = clear_code + 1;
max_code_size = 2 * clear_code;
max_code = clear_code + 2;
if (table == NULL)
table = calloc(2, sizeof(gif_table_t));
if (table == NULL)
return (-1);
if (stack == NULL)
stack = calloc(8192, sizeof(short));
if (stack == NULL)
return (-1);
gif_get_code(fp, 0, 1);
fresh = 1;
for (i = 0; i < clear_code; i ++)
{
table[0][i] = 0;
table[1][i] = i;
}
for (; i < 4096; i ++)
table[0][i] = table[1][0] = 0;
sp = stack;
return (0);
}
else if (fresh)
{
fresh = 0;
do
firstcode = oldcode = gif_get_code(fp, code_size, 0);
while (firstcode == clear_code);
return (firstcode);
}
if (sp > stack)
return (*--sp);
while ((code = gif_get_code (fp, code_size, 0)) >= 0)
{
if (code == clear_code)
{
for (i = 0; i < clear_code; i ++)
{
table[0][i] = 0;
table[1][i] = i;
}
for (; i < 4096; i ++)
table[0][i] = table[1][i] = 0;
code_size = set_code_size + 1;
max_code_size = 2 * clear_code;
max_code = clear_code + 2;
sp = stack;
firstcode = oldcode = gif_get_code(fp, code_size, 0);
return (firstcode);
}
else if (code == end_code)
{
unsigned char buf[260];
if (!gif_eof)
while (gif_get_block(fp, buf) > 0);
return (-2);
}
incode = code;
if (code >= max_code)
{
*sp++ = firstcode;
code = oldcode;
}
while (code >= clear_code)
{
*sp++ = table[1][code];
if (code == table[0][code])
return (255);
code = table[0][code];
}
*sp++ = firstcode = table[1][code];
code = max_code;
if (code < 4096)
{
table[0][code] = oldcode;
table[1][code] = firstcode;
max_code ++;
if (max_code >= max_code_size && max_code_size < 4096)
{
max_code_size *= 2;
code_size ++;
}
}
oldcode = incode;
if (sp > stack)
return (*--sp);
}
return (code);
}
static int
gif_read_image(FILE *fp,
image_t *img,
gif_cmap_t cmap,
int interlace)
{
unsigned char code_size;
ib_t *pixels,
*temp;
int xpos,
ypos,
pass;
int pixel;
int bpp;
static const int xpasses[4] =
{ 8, 8, 4, 2 },
ypasses[5] =
{ 0, 4, 2, 1, 999999 };
bpp = ImageGetDepth(img);
pixels = calloc(bpp, img->xsize);
xpos = 0;
ypos = 0;
pass = 0;
code_size = getc(fp);
if (gif_read_lzw(fp, 1, code_size) < 0)
return (-1);
temp = pixels;
while ((pixel = gif_read_lzw(fp, 0, code_size)) >= 0)
{
switch (bpp)
{
case 4 :
temp[3] = cmap[pixel][3];
case 3 :
temp[2] = cmap[pixel][2];
case 2 :
temp[1] = cmap[pixel][1];
default :
temp[0] = cmap[pixel][0];
}
xpos ++;
temp += bpp;
if (xpos == img->xsize)
{
ImagePutRow(img, 0, ypos, img->xsize, pixels);
xpos = 0;
temp = pixels;
if (interlace)
{
ypos += xpasses[pass];
if (ypos >= img->ysize)
{
pass ++;
ypos = ypasses[pass];
}
}
else
ypos ++;
}
if (ypos >= img->ysize)
break;
}
free(pixels);
return (0);
}