This repository has been archived on 2024-01-04. You can view files and clone it, but cannot push or open issues or pull requests.
ncsa-mosaic/src/gifread.c

888 lines
21 KiB
C

/* +-------------------------------------------------------------------+ */
/* | Copyright 1990 - 1994, David Koblas. (koblas@netcom.com) | */
/* | Permission to use, copy, modify, and distribute this software | */
/* | and its documentation for any purpose and without fee is hereby | */
/* | granted, provided that the above copyright notice appear in all | */
/* | copies and that both that copyright notice and this permission | */
/* | notice appear in supporting documentation. This software is | */
/* | provided "as is" without express or implied warranty. | */
/* +-------------------------------------------------------------------+ */
/****************************************************************************
* NCSA Mosaic for the X Window System *
* Software Development Group *
* National Center for Supercomputing Applications *
* University of Illinois at Urbana-Champaign *
* 605 E. Springfield, Champaign IL 61820 *
* mosaic@ncsa.uiuc.edu *
* *
* Copyright (C) 1993, Board of Trustees of the University of Illinois *
* *
* NCSA Mosaic software, both binary and source (hereafter, Software) is *
* copyrighted by The Board of Trustees of the University of Illinois *
* (UI), and ownership remains with the UI. *
* *
* The UI grants you (hereafter, Licensee) a license to use the Software *
* for academic, research and internal business purposes only, without a *
* fee. Licensee may distribute the binary and source code (if released) *
* to third parties provided that the copyright notice and this statement *
* appears on all copies and that no charge is associated with such *
* copies. *
* *
* Licensee may make derivative works. However, if Licensee distributes *
* any derivative work based on or derived from the Software, then *
* Licensee will (1) notify NCSA regarding its distribution of the *
* derivative work, and (2) clearly notify users that such derivative *
* work is a modified version and not the original NCSA Mosaic *
* distributed by the UI. *
* *
* Any Licensee wishing to make commercial use of the Software should *
* contact the UI, c/o NCSA, to negotiate an appropriate license for such *
* commercial use. Commercial use includes (1) integration of all or *
* part of the source code into a product for sale or license by or on *
* behalf of Licensee to third parties, or (2) distribution of the binary *
* code or source code to third parties that need it to utilize a *
* commercial product sold or licensed by or on behalf of Licensee. *
* *
* UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR *
* ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED *
* WARRANTY. THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE *
* USERS OF THIS SOFTWARE. *
* *
* By using or copying this Software, Licensee agrees to abide by the *
* copyright law and all other applicable laws of the U.S. including, but *
* not limited to, export control laws, and the terms of this license. *
* UI shall have the right to terminate this license immediately by *
* written notice upon Licensee's breach of, or non-compliance with, any *
* of its terms. Licensee may be held legally responsible for any *
* copyright infringement that is caused or encouraged by Licensee's *
* failure to abide by the terms of this license. *
* *
* Comments and questions are welcome and can be sent to *
* mosaic-x@ncsa.uiuc.edu. *
****************************************************************************/
#include "../config.h"
#include <stdio.h>
#include <stdlib.h>
#include <X11/Intrinsic.h>
#include "gifread.h"
/*
#define TIMING 1
*/
#include <sys/time.h>
static struct timeval Tv;
static struct timezone Tz;
#define MAXCOLORMAPSIZE 256
#define TRUE 1
#define FALSE 0
#define CM_RED 0
#define CM_GREEN 1
#define CM_BLUE 2
#define MAX_LWZ_BITS 12
#define INTERLACE 0x40
#define LOCALCOLORMAP 0x80
#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
#define LM_to_uint(a,b) (((b)<<8)|(a))
static struct {
unsigned int Width;
unsigned int Height;
unsigned char ColorMap[3][MAXCOLORMAPSIZE];
unsigned int BitPixel;
unsigned int ColorResolution;
unsigned int Background;
unsigned int AspectRatio;
/*
**
*/
int xGrayScale;
} GifScreen;
static struct {
int transparent;
int delayTime;
int inputFlag;
int disposal;
} Gif89 = { -1, -1, -1, 0 };
static int verbose = FALSE;
static int showComment = FALSE;
static int ReadColorMap(FILE *, int, unsigned char [3][MAXCOLORMAPSIZE], int *);
static int DoExtension(FILE *, int);
static int GetDataBlock(FILE *, unsigned char *);
static unsigned char *ReadImage(FILE *, int, int, XColor *, int,
unsigned char[3][MAXCOLORMAPSIZE], int, int, int);
/*
unsigned char *ReadGIF ARGS(( FILE *fd, int imageNumber, int *bg ));
static int ReadColorMap ARGS(( FILE *fd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE] ));
static int DoExtension ARGS(( FILE *fd, int label ));
static int GetDataBlock ARGS(( FILE *fd, unsigned char *buf ));
static int GetCode ARGS(( FILE *fd, int code_size, int flag ));
static int LWZReadByte ARGS(( FILE *fd, int flag, int input_code_size ));
static unsigned char *ReadImage ARGS(( FILE *fd, int len, int height, unsigned char cmap[3][MAXCOLORMAPSIZE], int interlace, int ignore ));
*/
/*
static int ReadColorMap();
static int DoExtension();
static int GetDataBlock();
static int GetCode();
static int LWZReadByte();
static unsigned char *ReadImage();
*/
#ifndef DISABLE_TRACE
extern int srcTrace;
#endif
unsigned char *
ReadGIF(FILE *fd, int *w, int *h, XColor *colrs, int *bg)
{
unsigned char buf[16];
unsigned char c;
unsigned char localColorMap[3][MAXCOLORMAPSIZE];
int grayScale;
int useGlobalColormap;
int bitPixel;
int imageCount = 0;
char version[4];
int imageNumber = 1;
unsigned char *image = NULL;
int i;
#ifndef DISABLE_TRACE
if (srcTrace) {
gettimeofday(&Tv, &Tz);
fprintf(stderr, "ReadGIF_DK enter (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
}
#endif
verbose = FALSE;
showComment = FALSE;
/*
* Initialize GIF89 extensions
*/
Gif89.transparent = -1;
Gif89.delayTime = -1;
Gif89.inputFlag = -1;
Gif89.disposal = 0;
if (! ReadOK(fd,buf,6))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "error reading magic number\n");
}
#endif
return(NULL);
}
if (strncmp((char *)buf,"GIF",3) != 0)
{
#ifndef DISABLE_TRACE
if (srcTrace)
fprintf(stderr, "not a GIF file\n");
#endif
return(NULL);
}
strncpy(version, (char *)buf + 3, 3);
version[3] = '\0';
if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "bad version number, not '87a' or '89a'\n");
}
#endif
return(NULL);
}
if (! ReadOK(fd,buf,7))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "failed to read screen descriptor\n");
}
#endif
return(NULL);
}
GifScreen.Width = LM_to_uint(buf[0],buf[1]);
GifScreen.Height = LM_to_uint(buf[2],buf[3]);
GifScreen.BitPixel = 2<<(buf[4]&0x07);
GifScreen.ColorResolution = (((buf[4]&0x70)>>3)+1);
GifScreen.Background = buf[5];
GifScreen.AspectRatio = buf[6];
if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
int scale = 65536/MAXCOLORMAPSIZE;
if (ReadColorMap(fd,GifScreen.BitPixel,GifScreen.ColorMap,
&GifScreen.xGrayScale))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "error reading global colormap\n");
}
#endif
return(NULL);
}
for (i=0; i < GifScreen.BitPixel; i++)
{
colrs[i].red = GifScreen.ColorMap[0][i] * scale;
colrs[i].green = GifScreen.ColorMap[1][i] * scale;
colrs[i].blue = GifScreen.ColorMap[2][i] * scale;
colrs[i].pixel = i;
colrs[i].flags = DoRed|DoGreen|DoBlue;
}
for (i = GifScreen.BitPixel; i<MAXCOLORMAPSIZE; i++)
{
colrs[i].red = 0;
colrs[i].green = 0;
colrs[i].blue = 0;
colrs[i].pixel = i;
colrs[i].flags = DoRed|DoGreen|DoBlue;
}
}
if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) {
/* float r;
r = ( (float) GifScreen.AspectRatio + 15.0 ) / 64.0;*/
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "Warning: non-square pixels!\n");
}
#endif
}
while (image == NULL) {
if (! ReadOK(fd,&c,1))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "EOF / read error on image data\n");
}
#endif
return(NULL);
}
if (c == ';') { /* GIF terminator */
if (imageCount < imageNumber)
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "No images found in file\n");
}
#endif
return(NULL);
}
break;
}
if (c == '!') { /* Extension */
if (! ReadOK(fd,&c,1))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "EOF / read error on extention function code\n");
}
#endif
return(NULL);
}
DoExtension(fd, c);
continue;
}
if (c != ',') { /* Not a valid start character */
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "bogus character 0x%02x, ignoring\n",
(int)c);
}
#endif
continue;
}
++imageCount;
if (! ReadOK(fd,buf,9))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr,"couldn't read left/top/width/height\n");
}
#endif
return(NULL);
}
useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
bitPixel = 1<<((buf[8]&0x07)+1);
/*
* We only want to set width and height for the imageNumber
* we are requesting.
*/
if (imageCount == imageNumber)
{
*w = LM_to_uint(buf[4],buf[5]);
*h = LM_to_uint(buf[6],buf[7]);
}
if (! useGlobalColormap) {
if (ReadColorMap(fd,bitPixel,localColorMap,&grayScale))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "error reading local colormap\n");
}
#endif
return(NULL);
}
/*
* We only want to set the data for the
* imageNumber we are requesting.
*/
if (imageCount == imageNumber)
{
image = ReadImage(fd, LM_to_uint(buf[4],buf[5]),
LM_to_uint(buf[6],buf[7]), colrs,
bitPixel, localColorMap, grayScale,
BitSet(buf[8], INTERLACE),
imageCount != imageNumber);
}
else
{
/* unsigned char *tdata;
tdata =*/ ReadImage(fd, LM_to_uint(buf[4],buf[5]),
LM_to_uint(buf[6],buf[7]), colrs,
bitPixel, localColorMap, grayScale,
BitSet(buf[8], INTERLACE),
imageCount != imageNumber);
}
} else {
/*
* We only want to set the data for the
* imageNumber we are requesting.
*/
if (imageCount == imageNumber)
{
image = ReadImage(fd, LM_to_uint(buf[4],buf[5]),
LM_to_uint(buf[6],buf[7]), colrs,
GifScreen.BitPixel, GifScreen.ColorMap,
GifScreen.xGrayScale,
BitSet(buf[8], INTERLACE),
imageCount != imageNumber);
}
else
{
/* unsigned char *tdata;
tdata =*/ ReadImage(fd, LM_to_uint(buf[4],buf[5]),
LM_to_uint(buf[6],buf[7]), colrs,
GifScreen.BitPixel, GifScreen.ColorMap,
GifScreen.xGrayScale,
BitSet(buf[8], INTERLACE),
imageCount != imageNumber);
}
}
}
*bg = Gif89.transparent;
#ifndef DISABLE_TRACE
if (srcTrace) {
gettimeofday(&Tv, &Tz);
fprintf(stderr, "ReadGIF_DK exit (%d.%d)\n", Tv.tv_sec, Tv.tv_usec);
}
#endif
return(image);
}
static int
ReadColorMap(FILE *fd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE],
int *gray)
{
int i;
unsigned char rgb[3];
int flag;
flag = TRUE;
for (i = 0; i < number; ++i) {
if (! ReadOK(fd, rgb, sizeof(rgb)))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "bad colormap\n");
}
#endif
return(TRUE);
}
buffer[CM_RED][i] = rgb[0] ;
buffer[CM_GREEN][i] = rgb[1] ;
buffer[CM_BLUE][i] = rgb[2] ;
flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
}
*gray = flag;
return FALSE;
}
static int
DoExtension(FILE *fd, int label)
{
static char buf[256];
char str[256];
switch (label) {
case 0x01: /* Plain Text Extension */
strcpy(str,"Plain Text Extension");
#ifdef notdef
if (GetDataBlock(fd, (unsigned char*) buf) <= 0)
;
lpos = LM_to_uint(buf[0], buf[1]);
tpos = LM_to_uint(buf[2], buf[3]);
width = LM_to_uint(buf[4], buf[5]);
height = LM_to_uint(buf[6], buf[7]);
cellw = buf[8];
cellh = buf[9];
foreground = buf[10];
background = buf[11];
while (GetDataBlock(fd, (unsigned char*) buf) > 0) {
PPM_ASSIGN(image[ypos][xpos],
cmap[CM_RED][v],
cmap[CM_GREEN][v],
cmap[CM_BLUE][v]);
++index;
}
return FALSE;
#else
break;
#endif
case 0xff: /* Application Extension */
strcpy(str,"Application Extension");
break;
case 0xfe: /* Comment Extension */
strcpy(str,"Comment Extension");
while (GetDataBlock(fd, (unsigned char*) buf) > 0) {
if (showComment)
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "gif comment: %s\n", buf);
}
#endif
}
}
return FALSE;
case 0xf9: /* Graphic Control Extension */
strcpy(str,"Graphic Control Extension");
(void) GetDataBlock(fd, (unsigned char*) buf);
Gif89.disposal = (buf[0] >> 2) & 0x7;
Gif89.inputFlag = (buf[0] >> 1) & 0x1;
Gif89.delayTime = LM_to_uint(buf[1],buf[2]);
if ((buf[0] & 0x1) != 0)
Gif89.transparent = (int)((unsigned char)buf[3]);
while (GetDataBlock(fd, (unsigned char*) buf) > 0)
;
return FALSE;
default:
sprintf(str, "UNKNOWN (0x%02x)", label);
break;
}
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "got a '%s' extension\n", str);
}
#endif
while (GetDataBlock(fd, (unsigned char*) buf) > 0)
;
return FALSE;
}
static int ZeroDataBlock = FALSE;
static int
GetDataBlock(FILE *fd, unsigned char *buf)
{
unsigned char count;
count = 0;
if (! ReadOK(fd, &count, 1)) {
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "error in getting DataBlock size\n");
}
#endif
return -1;
}
ZeroDataBlock = count == 0;
if ((count != 0) && (! ReadOK(fd, buf, count))) {
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "error in reading DataBlock\n");
}
#endif
return -1;
}
return((int)count);
}
/*
** Pulled out of nextCode
*/
static int curbit, lastbit, get_done, last_byte;
static int return_clear;
/*
** Out of nextLWZ
*/
static int stack[(1<<(MAX_LWZ_BITS))*2], *sp;
static int code_size, set_code_size;
static int max_code, max_code_size;
static int clear_code, end_code;
static void initLWZ(int input_code_size)
{
/* static int inited = FALSE;*/
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;
curbit = lastbit = 0;
last_byte = 2;
get_done = FALSE;
return_clear = TRUE;
sp = stack;
}
static int nextCode(FILE *fd, int code_size)
{
static unsigned char buf[280];
static int maskTbl[16] = {
0x0000, 0x0001, 0x0003, 0x0007,
0x000f, 0x001f, 0x003f, 0x007f,
0x00ff, 0x01ff, 0x03ff, 0x07ff,
0x0fff, 0x1fff, 0x3fff, 0x7fff,
};
int i, j, ret, end;
if (return_clear) {
return_clear = FALSE;
return clear_code;
}
end = curbit + code_size;
if (end >= lastbit) {
int count;
if (get_done) {
if (curbit >= lastbit)
{
#if 0
ERROR("ran off the end of my bits" );
#endif
}
return -1;
}
buf[0] = buf[last_byte-2];
buf[1] = buf[last_byte-1];
if ((count = GetDataBlock(fd, &buf[2])) == 0)
get_done = TRUE;
last_byte = 2 + count;
curbit = (curbit - lastbit) + 16;
lastbit = (2+count)*8 ;
end = curbit + code_size;
}
j = end / 8;
i = curbit / 8;
if (i == j)
ret = (int)buf[i];
else if (i + 1 == j)
ret = (int)buf[i] | ((int)buf[i+1] << 8);
else
ret = (int)buf[i] | ((int)buf[i+1] << 8) | ((int)buf[i+2] << 16);
ret = (ret >> (curbit % 8)) & maskTbl[code_size];
curbit += code_size;
return ret;
}
#define readLWZ(fd) ((sp > stack) ? *--sp : nextLWZ(fd))
static int nextLWZ(FILE *fd)
{
static int table[2][(1<< MAX_LWZ_BITS)];
static int firstcode, oldcode;
int code, incode;
register int i;
while ((code = nextCode(fd, code_size)) >= 0) {
if (code == clear_code) {
/* corrupt GIFs can make this happen */
if (clear_code >= (1<<MAX_LWZ_BITS))
{
return -2;
}
for (i = 0; i < clear_code; ++i) {
table[0][i] = 0;
table[1][i] = i;
}
for (; i < (1<<MAX_LWZ_BITS); ++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;
do {
firstcode = oldcode = nextCode(fd, code_size);
} while (firstcode == clear_code);
return firstcode;
}
if (code == end_code) {
int count;
unsigned char buf[260];
if (ZeroDataBlock)
return -2;
while ((count = GetDataBlock(fd, buf)) > 0)
;
if (count != 0)
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr,"missing EOD in data stream (common occurence)");
}
#endif
}
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])
{
#if 0
ERROR("circular table entry BIG ERROR");
#endif
return(code);
}
if ((int)sp >= ((int)stack + sizeof(stack)))
{
#if 0
ERROR("circular table STACK OVERFLOW!");
#endif
return(code);
}
code = table[0][code];
}
*sp++ = firstcode = table[1][code];
if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
table[0][code] = oldcode;
table[1][code] = firstcode;
++max_code;
if ((max_code >= max_code_size) &&
(max_code_size < (1<<MAX_LWZ_BITS))) {
max_code_size *= 2;
++code_size;
}
}
oldcode = incode;
if (sp > stack)
return *--sp;
}
return code;
}
static unsigned char *
ReadImage(FILE *fd, int len, int height, XColor *colrs, int cmapSize,
unsigned char cmap[3][MAXCOLORMAPSIZE], int gray,
int interlace, int ignore)
{
unsigned char *dp, c;
int v;
int xpos = 0, ypos = 0/*, pass = 0*/;
unsigned char *image;
/*
** Initialize the Compression routines
*/
if (! ReadOK(fd,&c,1))
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "EOF / read error on image data\n");
}
#endif
return(NULL);
}
initLWZ(c);
/*
** If this is an "uninteresting picture" ignore it.
*/
if (ignore) {
#ifndef DISABLE_TRACE
if (srcTrace)
fprintf(stderr, "skipping image...\n" );
#endif
while (readLWZ(fd) >= 0)
;
return(NULL);
}
image = (unsigned char *)calloc(len * height, sizeof(char));
if (image == NULL)
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr, "Cannot allocate space for image data\n");
}
#endif
return(NULL);
}
for (v = 0; v < MAXCOLORMAPSIZE; v++) {
colrs[v].red = colrs[v].green = colrs[v].blue = 0;
colrs[v].pixel = v;
colrs[v].flags = DoRed|DoGreen|DoBlue;
}
for (v = 0; v < cmapSize; v++) {
colrs[v].red = cmap[CM_RED][v] * 0x101;
colrs[v].green = cmap[CM_GREEN][v] * 0x101;
colrs[v].blue = cmap[CM_BLUE][v] * 0x101;
}
#ifndef DISABLE_TRACE
if (srcTrace)
fprintf(stderr, "reading %d by %d%s GIF image\n",
len, height, interlace ? " interlaced" : "" );
#endif
if (interlace) {
int i;
int pass = 0, step = 8;
for (i = 0; i < height; i++) {
if (ypos < height)
{
dp = &image[len * ypos];
for (xpos = 0; xpos < len; xpos++) {
if ((v = readLWZ(fd)) < 0)
goto fini;
*dp++ = v;
}
}
if ((ypos += step) >= height) {
if (pass++ > 0)
step /= 2;
ypos = step / 2;
}
}
} else {
dp = image;
for (ypos = 0; ypos < height; ypos++) {
for (xpos = 0; xpos < len; xpos++) {
if ((v = readLWZ(fd)) < 0)
goto fini;
*dp++ = v;
}
}
}
fini:
if (readLWZ(fd)>=0)
{
#ifndef DISABLE_TRACE
if (srcTrace) {
fprintf(stderr,"too much input data, ignoring extra...");
}
#endif
}
return(image);
}