/* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing * following copyright notice: */ /* +-------------------------------------------------------------------+ */ /* | Copyright 1990, David Koblas. | */ /* | 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 "tile.h" #ifndef MONITOR_HEAP extern long *FDECL(alloc, (unsigned int)); #endif #define PPM_ASSIGN(p,red,grn,blu) do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 ) #define MAX_LWZ_BITS 12 #define INTERLACE 0x40 #define LOCALCOLORMAP 0x80 #define BitSet(byte, bit) (((byte) & (bit)) == (bit)) #define ReadOK(file,buffer,len) (fread((genericptr_t)buffer, (int)len, 1, file) != 0) #define LM_to_uint(a,b) (((b)<<8)|(a)) struct gifscreen { int Width; int Height; int Colors; int ColorResolution; int Background; int AspectRatio; int Interlace; } GifScreen; struct { int transparent; int delayTime; int inputFlag; int disposal; } Gif89 = { -1, -1, -1, 0 }; int ZeroDataBlock = FALSE; static FILE *gif_file; static int tiles_across, tiles_down, curr_tiles_across, curr_tiles_down; static pixel **image; static unsigned char input_code_size; static int FDECL(GetDataBlock, (FILE *fd, unsigned char *buf)); static void FDECL(DoExtension, (FILE *fd, int label)); static boolean FDECL(ReadColorMap, (FILE *fd, int number)); static void FDECL(read_header, (FILE *fd)); static int FDECL(GetCode, (FILE *fd, int code_size, int flag)); static int FDECL(LWZReadByte, (FILE *fd, int flag, int input_code_size)); static void FDECL(ReadInterleavedImage, (FILE *fd, int len, int height)); static void FDECL(ReadTileStrip, (FILE *fd, int len)); /* These should be in gif.h, but there isn't one. */ boolean FDECL(fopen_gif_file, (const char *, const char *)); boolean FDECL(read_gif_tile, (pixel(*)[])); int NDECL(fclose_gif_file); static int GetDataBlock(fd, buf) FILE *fd; unsigned char *buf; { unsigned char count; if (!ReadOK(fd,&count,1)) { Fprintf(stderr, "error in getting DataBlock size\n"); return -1; } ZeroDataBlock = (count == 0); if ((count != 0) && (!ReadOK(fd, buf, count))) { Fprintf(stderr, "error in reading DataBlock\n"); return -1; } return count; } static void 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; #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) { Fprintf(stderr, "gif comment: %s\n", buf ); } return; 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; default: str = buf; Sprintf(buf, "UNKNOWN (0x%02x)", label); break; } Fprintf(stderr, "got a '%s' extension\n", str); while (GetDataBlock(fd, (unsigned char*) buf) != 0) ; } static boolean ReadColorMap(fd,number) FILE *fd; int number; { int i; unsigned char rgb[3]; for (i = 0; i < number; ++i) { if (!ReadOK(fd, rgb, sizeof(rgb))) { return(FALSE); } ColorMap[CM_RED][i] = rgb[0] ; ColorMap[CM_GREEN][i] = rgb[1] ; ColorMap[CM_BLUE][i] = rgb[2] ; } colorsinmap = number; return TRUE; } /* * Read gif header, including colormaps. We expect only one image per * file, so if that image has a local colormap, overwrite the global one. */ static void read_header(fd) FILE *fd; { unsigned char buf[16]; unsigned char c; char version[4]; if (!ReadOK(fd,buf,6)) { Fprintf(stderr, "error reading magic number\n"); exit(EXIT_FAILURE); } if (strncmp((genericptr_t)buf,"GIF",3) != 0) { Fprintf(stderr, "not a GIF file\n"); exit(EXIT_FAILURE); } (void) strncpy(version, (char *)buf + 3, 3); version[3] = '\0'; if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) { Fprintf(stderr, "bad version number, not '87a' or '89a'\n"); exit(EXIT_FAILURE); } if (!ReadOK(fd,buf,7)) { Fprintf(stderr, "failed to read screen descriptor\n"); exit(EXIT_FAILURE); } GifScreen.Width = LM_to_uint(buf[0],buf[1]); GifScreen.Height = LM_to_uint(buf[2],buf[3]); GifScreen.Colors = 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.Colors)) { Fprintf(stderr, "error reading global colormap\n"); exit(EXIT_FAILURE); } } if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) { Fprintf(stderr, "warning - non-square pixels\n"); } for (;;) { if (!ReadOK(fd,&c,1)) { Fprintf(stderr, "EOF / read error on image data\n"); exit(EXIT_FAILURE); } if (c == ';') { /* GIF terminator */ return; } if (c == '!') { /* Extension */ if (!ReadOK(fd,&c,1)) { Fprintf(stderr, "EOF / read error on extension function code\n"); exit(EXIT_FAILURE); } DoExtension(fd, (int)c); continue; } if (c != ',') { /* Not a valid start character */ Fprintf(stderr, "bogus character 0x%02x, ignoring\n", (int) c); continue; } if (!ReadOK(fd,buf,9)) { Fprintf(stderr, "couldn't read left/top/width/height\n"); exit(EXIT_FAILURE); } if (BitSet(buf[8], LOCALCOLORMAP)) { /* replace global color map with local */ GifScreen.Colors = 1<<((buf[8]&0x07)+1); if (!ReadColorMap(fd, GifScreen.Colors)) { Fprintf(stderr, "error reading local colormap\n"); exit(EXIT_FAILURE); } } if (GifScreen.Width != LM_to_uint(buf[4],buf[5])) { Fprintf(stderr, "warning: widths don't match\n"); GifScreen.Width = LM_to_uint(buf[4],buf[5]); } if (GifScreen.Height != LM_to_uint(buf[6],buf[7])) { Fprintf(stderr, "warning: heights don't match\n"); GifScreen.Height = LM_to_uint(buf[6],buf[7]); } GifScreen.Interlace = BitSet(buf[8], INTERLACE); return; } } 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) Fprintf(stderr, "ran off the end of my bits\n"); 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; (void) 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) Fprintf(stderr, "missing EOD in data stream (common occurrence)\n"); 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]) { Fprintf(stderr, "circular table entry BIG ERROR\n"); exit(EXIT_FAILURE); } 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 void ReadInterleavedImage(fd, len, height) FILE *fd; int len, height; { int v; int xpos = 0, ypos = 0, pass = 0; while ((v = LWZReadByte(fd,FALSE,(int)input_code_size)) >= 0 ) { PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v], ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]); ++xpos; if (xpos == len) { xpos = 0; 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; } } } if (ypos >= height) break; } fini: if (LWZReadByte(fd,FALSE,(int)input_code_size)>=0) Fprintf(stderr, "too much input data, ignoring extra...\n"); } static void ReadTileStrip(fd,len) FILE *fd; int len; { int v; int xpos = 0, ypos = 0; while ((v = LWZReadByte(fd,FALSE,(int)input_code_size)) >= 0 ) { PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v], ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]); ++xpos; if (xpos == len) { xpos = 0; ++ypos; } if (ypos >= TILE_Y) break; } } boolean fopen_gif_file(filename, type) const char *filename; const char *type; { int i; if (strcmp(type, RDBMODE)) { Fprintf(stderr, "using reading routine for non-reading?\n"); return FALSE; } gif_file = fopen(filename, type); if (gif_file == (FILE *)0) { Fprintf(stderr, "cannot open gif file %s\n", filename); return FALSE; } read_header(gif_file); if (GifScreen.Width % TILE_X) { Fprintf(stderr, "error: width %d not divisible by %d\n", GifScreen.Width, TILE_X); exit(EXIT_FAILURE); } tiles_across = GifScreen.Width / TILE_X; curr_tiles_across = 0; if (GifScreen.Height % TILE_Y) { Fprintf(stderr, "error: height %d not divisible by %d\n", GifScreen.Height, TILE_Y); /* exit(EXIT_FAILURE) */; } tiles_down = GifScreen.Height / TILE_Y; curr_tiles_down = 0; if (GifScreen.Interlace) { /* sigh -- hope this doesn't happen on micros */ image = (pixel **)alloc(GifScreen.Height * sizeof(pixel *)); for (i = 0; i < GifScreen.Height; i++) { image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel)); } } else { image = (pixel **)alloc(TILE_Y * sizeof(pixel *)); for (i = 0; i < TILE_Y; i++) { image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel)); } } /* ** Initialize the Compression routines */ if (!ReadOK(gif_file,&input_code_size,1)) { Fprintf(stderr, "EOF / read error on image data\n"); exit(EXIT_FAILURE); } if (LWZReadByte(gif_file, TRUE, (int)input_code_size) < 0) { Fprintf(stderr, "error reading image\n"); exit(EXIT_FAILURE); } /* read first section */ if (GifScreen.Interlace) { ReadInterleavedImage(gif_file, GifScreen.Width,GifScreen.Height); } else { ReadTileStrip(gif_file,GifScreen.Width); } return TRUE; } /* Read a tile. Returns FALSE when there are no more tiles */ boolean read_gif_tile(pixels) pixel (*pixels)[TILE_X]; { int i, j; if (curr_tiles_down >= tiles_down) return FALSE; if (curr_tiles_across == tiles_across) { curr_tiles_across = 0; curr_tiles_down++; if (curr_tiles_down >= tiles_down) return FALSE; if (!GifScreen.Interlace) ReadTileStrip(gif_file,GifScreen.Width); } if (GifScreen.Interlace) { for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { pixels[j][i] = image[curr_tiles_down*TILE_Y + j] [curr_tiles_across*TILE_X + i]; } } } else { for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { pixels[j][i] = image[j][curr_tiles_across*TILE_X + i]; } } } curr_tiles_across++; /* check for "filler" tile */ for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X && i < 4; i += 2) { if (pixels[j][i].r != ColorMap[CM_RED][0] || pixels[j][i].g != ColorMap[CM_GREEN][0] || pixels[j][i].b != ColorMap[CM_BLUE][0] || pixels[j][i+1].r != ColorMap[CM_RED][1] || pixels[j][i+1].g != ColorMap[CM_GREEN][1] || pixels[j][i+1].b != ColorMap[CM_BLUE][1]) return TRUE; } } return FALSE; } int fclose_gif_file() { int i; if (GifScreen.Interlace) { for (i = 0; i < GifScreen.Height; i++) { free((genericptr_t)image[i]); } free((genericptr_t)image); } else { for (i = 0; i < TILE_Y; i++) { free((genericptr_t)image[i]); } free((genericptr_t)image); } return(fclose(gif_file)); } #ifndef AMIGA static char *std_args[] = { "tilemap", /* dummy argv[0] */ "monsters.gif", "monsters.txt", "objects.gif", "objects.txt", "other.gif", "other.txt" }; int main(argc, argv) int argc; char *argv[]; { pixel pixels[TILE_Y][TILE_X]; if (argc == 1) { argc = SIZE(std_args); argv = std_args; } else if (argc != 3) { Fprintf(stderr, "usage: gif2txt giffile txtfile\n"); exit(EXIT_FAILURE); } while (argc > 1) { if (!fopen_gif_file(argv[1], RDBMODE)) exit(EXIT_FAILURE); init_colormap(); if (!fopen_text_file(argv[2], WRTMODE)) { (void) fclose_gif_file(); exit(EXIT_FAILURE); } while (read_gif_tile(pixels)) (void) write_text_tile(pixels); (void) fclose_gif_file(); (void) fclose_text_file(); argc -= 2; argv += 2; } exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; } #endif