/* +-------------------------------------------------------------------+ */ /* | Copyright 1990, 1991, 1993, 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. | */ /* +-------------------------------------------------------------------+ */ #include "config.h" #include "misc.h" #include "lisp.h" #include "imf.h" /* Fake defns. */ #define PBM_TYPE 1 #define PGM_TYPE 2 #define PPM_TYPE 3 #define MAXCOLORMAPSIZE 256 #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 GrayScale; } GifScreen; static struct { int transparent; int delayTime; int inputFlag; int disposal; } Gif89 = { -1, -1, -1, 0 }; int verbose; int showComment; static int ReadGIF( FILE *fd, int imageNumber, int *numcols, int *rawpal, char **rawdata ); static int ReadColorMap( FILE *fd, int number, unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag ); static int DoExtension( FILE *fd, int label ); static int GetDataBlock( FILE *fd, unsigned char *buf ); static int GetCode( FILE *fd, int code_size, int flag ); static int LWZReadByte( FILE *fd, int flag, int input_code_size ); static int ReadImage( FILE *fd, int len, int height, int gray, int interlace, int ignore, char **rawdata ); static int get_gif_from_file(FileImage *fimg, FILE *fp); extern void make_pathname(char *path, char *name, char *extn, char *pathbuf); int get_gif(FileImage *fimg) { int rslt; char fallback[BUFSIZE]; LibraryPath *p; FILE *fp; for_all_library_paths(p) { make_pathname(p->path, fimg->name, "", spbuf); if ((fp = open_file(spbuf, "rb")) != NULL) { rslt = get_gif_from_file(fimg, fp); fclose(fp); return rslt; } } #ifdef MAC /* We must not use unix pathnames on the mac. */ sprintf(fallback, "::images:%s", fimg->name); /* Adding library paths here gives the wrong result. */ if ((fp = open_file(fallback, "rb")) != NULL) { rslt = get_gif_from_file(fimg, fp); fclose(fp); return rslt; } #else /* If we didn't find in the list, try adding a "../images" to see if the image is in a sibling dir. */ sprintf(fallback, "../images/%s", fimg->name); for_all_library_paths(p) { make_pathname(p->path, fallback, "", spbuf); if ((fp = open_file(spbuf, "rb")) != NULL) { rslt = get_gif_from_file(fimg, fp); fclose(fp); return rslt; } } #endif return FALSE; } int get_gif_from_file(FileImage *fimg, FILE *fp) { char *rawdata; int rslt, i, j, numcolors, rawpal[1024]; int blacki = -1; rslt = ReadGIF(fp, 1, &numcolors, rawpal, &rawdata); if (rslt == 0) return FALSE; fimg->width = GifScreen.Width; fimg->height = GifScreen.Height; fimg->data = rawdata; /* Collect the transparent colors. */ fimg->transparent = (char *) xmalloc(12); fimg->transparent[0] = rawdata[0]; for (i = 1; i < 12; ++i) { if (rawdata[2 * i] != fimg->transparent[0]) fimg->transparent[i] = rawdata[2 * i]; else break; } fimg->numtransparent = i; if (fimg->numtransparent > 0) { /* Look for black. */ for (i = 0; i < numcolors; ++i) { if (rawpal[4*i+1] == 0 && rawpal[4*i+2] == 0 && rawpal[4*i+3] == 0) blacki = i; } } fimg->numcolors = numcolors; /* If we're going to add black to the palette, bump up the number of colors now, so the right amount of space gets allocated. */ if (!use_clip_mask && fimg->numtransparent > 0 && blacki < 0) ++(fimg->numcolors); fimg->palette = (int *) xmalloc(4 * fimg->numcolors * sizeof(int)); for (i = 0; i < numcolors; ++i) { fimg->palette[4*i] = i; for (j = 1; j < 4; ++j) fimg->palette[4*i+j] = rawpal[4*i+j]; } /* Maybe add black to the palette. */ if (!use_clip_mask) { if (fimg->numtransparent > 0 && blacki < 0) { blacki = numcolors; fimg->palette[4 * blacki] = blacki; fimg->palette[4 * blacki + 1] = 0; fimg->palette[4 * blacki + 2] = 0; fimg->palette[4 * blacki + 3] = 0; } if (fimg->numtransparent > 0) { /* Record blackness as another one of the transparent colors. */ fimg->transparent[fimg->numtransparent++] = blacki; } } return TRUE; } static int ReadGIF(FILE *fd, int imageNumber, int *numcolsp, int *rawpal, char **rawdata) { 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 rslt; if (! ReadOK(fd,buf,6)) { init_warning("error reading magic number" ); return FALSE; } if (strncmp((char *)buf,"GIF",3) != 0) { init_warning("not a GIF file" ); return FALSE; } strncpy(version, (char *)buf + 3, 3); version[3] = '\0'; if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) { init_warning("bad version number, not '87a' or '89a'" ); return FALSE; } if (! ReadOK(fd,buf,7)) { init_warning("failed to read screen descriptor" ); return FALSE; } 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 */ if (ReadColorMap(fd,GifScreen.BitPixel,GifScreen.ColorMap, &GifScreen.GrayScale)) { init_warning("error reading global colormap" ); return FALSE; } } if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) { float r; r = ( (float) GifScreen.AspectRatio + 15.0 ) / 64.0; init_warning("warning - non-square pixels; to fix do a 'pnmscale -%cscale %g'", r < 1.0 ? 'x' : 'y', r < 1.0 ? 1.0 / r : r ); } for (;;) { if (! ReadOK(fd,&c,1)) { init_warning("EOF / read error on image data" ); return FALSE; } if (c == ';') { /* GIF terminator */ if (imageCount < imageNumber) init_warning("only %d image%s found in file", imageCount, imageCount>1?"s":"" ); return 1; } if (c == '!') { /* Extension */ if (! ReadOK(fd,&c,1)) { init_warning("EOF / read error on extention function code"); return FALSE; } DoExtension(fd, c); continue; } if (c != ',') { /* Not a valid start character */ run_warning("bogus character 0x%02x, ignoring", (int) c ); continue; } ++imageCount; if (! ReadOK(fd,buf,9)) { init_warning("couldn't read left/top/width/height"); return FALSE; } useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP); bitPixel = 1<<((buf[8]&0x07)+1); if (! useGlobalColormap) { if (ReadColorMap(fd, bitPixel, localColorMap, &grayScale)) { init_warning("error reading local colormap" ); return FALSE; } rslt = ReadImage(fd, LM_to_uint(buf[4],buf[5]), LM_to_uint(buf[6],buf[7]), grayScale, BitSet(buf[8], INTERLACE), imageCount != imageNumber, rawdata); if (rslt == FALSE) return FALSE; { int i, j; for (i = 0; i < 256; ++i) for (j = 0; j < 3; ++j) rawpal[4*i+j+1] = localColorMap[j][i] << 8; } *numcolsp = bitPixel; } else { rslt = ReadImage(fd, LM_to_uint(buf[4],buf[5]), LM_to_uint(buf[6],buf[7]), GifScreen.GrayScale, BitSet(buf[8], INTERLACE), imageCount != imageNumber, rawdata); if (rslt == FALSE) return FALSE; { int i, j; for (i = 0; i < 256; ++i) for (j = 0; j < 3; ++j) rawpal[4*i+j+1] = GifScreen.ColorMap[j][i] << 8; } *numcolsp = GifScreen.BitPixel; } } return 1; } static int ReadColorMap(fd,number,buffer,pbm_format) FILE *fd; int number; unsigned char buffer[3][MAXCOLORMAPSIZE]; int *pbm_format; { int i; unsigned char rgb[3]; int flag; flag = TRUE; for (i = 0; i < number; ++i) { if (! ReadOK(fd, rgb, sizeof(rgb))) init_warning("bad colormap" ); 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]); } if (flag) *pbm_format = (number == 2) ? PBM_TYPE : PGM_TYPE; else *pbm_format = PPM_TYPE; return FALSE; } static int DoExtension(fd, label) FILE *fd; int label; { static char buf[256]; char *str; switch (label) { case 0x01: /* Plain Text Extension */ 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 */ str = "Application Extension"; break; case 0xfe: /* Comment Extension */ str = "Comment Extension"; while (GetDataBlock(fd, (unsigned char*) buf) != 0) { if (showComment) run_warning("gif comment: %s", buf ); } return FALSE; case 0xf9: /* Graphic Control Extension */ 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 = buf[3]; while (GetDataBlock(fd, (unsigned char*) buf) != 0) ; return FALSE; default: str = buf; sprintf(buf, "UNKNOWN (0x%02x)", label); break; } run_warning("got a '%s' extension", str ); while (GetDataBlock(fd, (unsigned char*) buf) != 0) ; return FALSE; } int ZeroDataBlock = FALSE; static int GetDataBlock(fd, buf) FILE *fd; unsigned char *buf; { unsigned char count; if (! ReadOK(fd,&count,1)) { run_warning("error in getting DataBlock size" ); return -1; } ZeroDataBlock = count == 0; if ((count != 0) && (! ReadOK(fd, buf, count))) { run_warning("error in reading DataBlock" ); return -1; } return count; } static int GetCode(fd, code_size, flag) FILE *fd; int code_size; int flag; { static unsigned char buf[280]; static int curbit, lastbit, done, last_byte; int i, j, ret; unsigned char count; if (flag) { curbit = 0; lastbit = 0; done = FALSE; return 0; } if ( (curbit+code_size) >= lastbit) { if (done) { if (curbit >= lastbit) init_warning("ran off the end of my bits" ); return -1; } buf[0] = buf[last_byte-2]; buf[1] = buf[last_byte-1]; if ((count = GetDataBlock(fd, &buf[2])) == 0) done = TRUE; last_byte = 2 + count; curbit = (curbit - lastbit) + 16; lastbit = (2+count)*8 ; } ret = 0; for (i = curbit, j = 0; j < code_size; ++i, ++j) ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j; curbit += code_size; return ret; } static int LWZReadByte(fd, flag, input_code_size) FILE *fd; int flag; int input_code_size; { static int fresh = FALSE; int code, incode; static int code_size, set_code_size; static int max_code, max_code_size; static int firstcode, oldcode; static int clear_code, end_code; static int table[2][(1<< MAX_LWZ_BITS)]; static int stack[(1<<(MAX_LWZ_BITS))*2], *sp; register int i; if (flag) { 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; GetCode(fd, 0, TRUE); fresh = TRUE; for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1< stack) return *--sp; while ((code = GetCode(fd, code_size, FALSE)) >= 0) { if (code == clear_code) { for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1< 0) ; if (count != 0) run_warning("missing EOD in data stream (common occurence)"); 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]) { init_warning("circular table entry BIG ERROR"); return -3; } 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 int ReadImage(fd, len, height, pbm_format, interlace, ignore, rawdata) FILE *fd; int len, height; int pbm_format, interlace, ignore; char **rawdata; { unsigned char c; int v; int xpos = 0, ypos = 0, pass = 0; char *image; /* ** Initialize the Compression routines */ if (! ReadOK(fd,&c,1)) { init_warning("EOF / read error on image data" ); return FALSE; } if (LWZReadByte(fd, TRUE, c) < 0) { init_warning("error reading image" ); return FALSE; } /* ** If this is an "uninteresting picture" ignore it. */ if (ignore) { if (verbose) run_warning("skipping image..." ); while (LWZReadByte(fd, FALSE, c) >= 0) ; return TRUE; } image = malloc(len * height); if (verbose) run_warning("reading %d by %d%s GIF image", len, height, interlace ? " interlaced" : "" ); while ((v = LWZReadByte(fd,FALSE,c)) >= 0 ) { image[ypos*len+xpos] = v; #if 0 PPM_ASSIGN(image[ypos][xpos], cmap[CM_RED][v], cmap[CM_GREEN][v], cmap[CM_BLUE][v]); #endif ++xpos; if (xpos == len) { xpos = 0; if (interlace) { switch (pass) { case 0: case 1: ypos += 8; break; case 2: ypos += 4; break; case 3: ypos += 2; break; } if (ypos >= height) { ++pass; switch (pass) { case 1: ypos = 4; break; case 2: ypos = 2; break; case 3: ypos = 1; break; default: goto fini; } } } else { ++ypos; } } if (ypos >= height) break; } fini: if (LWZReadByte(fd,FALSE,c)>=0) run_warning("too much input data, ignoring extra..."); *rawdata = image; return TRUE; }