/* +-------------------------------------------------------------------+ */ /* | 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 #include #include "gifread.h" /* #define TIMING 1 */ #include 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 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< 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_code_size) && (max_code_size < (1< 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); }