/* * $Id: t2gif.c,v 1.7 1998/07/27 19:48:45 alberto Exp alberto $ * * TIFF G4 to GIF translator. * This program translates input TIFF bitmap files into grayscale * GIF files, after proper resampling. * * For best run-time results, compile with the following gcc options: * -O3 -funroll-loops -fomit-frame-pointer * * Standard disclaimer: * * This program is part of the NASA Astrophysics Data System * article service data reduction/conversion software. * * Copyright (C): 1997, 1998 Smithsonian Astrophysical Observatory. * You may do anything you like with this file except remove * this copyright. The Smithsonian Astrophysical Observatory * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. It may not be incorporated into * commercial products without permission of the Smithsonian * Astrophysical Observatory. * * Written by Alberto Accomazzi * http://cfa-www.harvard.edu/~alberto/ * * * $Log: t2gif.c,v $ * Revision 1.7 1998/07/27 19:48:45 alberto * Fixed processing of command-line options when running as a non-cgi * application, updated usage message. * * Revision 1.6 1998/06/10 14:32:02 alberto * Added timestamp check to ensure that cached imnage is not stale. * * Revision 1.5 1998/04/24 16:40:27 alberto * Simplified code dealing with the creation of filename for cached gif, * supporting more general input pathnames. * Fixed HTTP header response so that an Expires header is sent on redirects. * * Revision 1.4 1998/03/02 16:18:58 alberto * Cleaned up some of the #ifdef mess, fixed problem with image caching * and 1 bit/pixel output. * * Revision 1.3 1997/08/27 19:59:34 alberto * Added option for variable resolution dinamic range, * fixed code to work as non-cgi script * * Revision 1.2 1996/07/29 15:57:42 alberto * Fixed minor problem with ending sequence for LZW stream. * * Revision 1.1 1996/07/24 18:02:21 alberto * Initial revision * * */ #include #include #include #include #include #include #include #include "tiffio.h" #ifdef MMAP #include #include static flock_t giffcntl; static unsigned char *marray, *marrayptr; #endif static int giffd = 0; static FILE *gif = NULL; static char *progname; static int debug = 0; static char imfile[BUFSIZ]; static int tiff2gif(int, int, int, int, TIFF*, FILE*); static void cleanup (int); static int mkdirs (char*); /* letter page sizes (plus 0.5in margin for oversized scans) * used to figure out image resolution */ #define PGSIZEX 9 #define PGSIZEY 11.5 #define NMAX 10 #define max(x,y) (((x)>(y))?(x):(y)) #define min(x,y) (((x)<(y))?(x):(y)) #define match(string, option, lmin) \ (strncmp((string), (option), max((lmin),strlen(string))) == 0) #define MAXSCALE 256 #define MAXCOLORS 256 #define DEFAULT_GIFRES 100 #define DEFAULT_GIFBITS 4 #define DEFAULT_GIFSCALE 16 /* default foreground color */ static struct { unsigned int red; unsigned int green; unsigned int blue; } BGColor = { MAXCOLORS - 1, MAXCOLORS - 1, MAXCOLORS - 1 }; /* this array structure defines which combination of attributes should generate a GIF image which gets cached. Set aray to contain only the element {0, -1, -1} if you don't want any caching */ static struct { int bits; int dpi; int factor; } CacheGIF[] = { { 4, 100, -1 }, /* full-screen GIF page (100dpi, 4 bits) */ { 3, -1, 16 }, /* thumbnails (200dpi / 16, 3 bits) */ { 0, -1, -1 } }; #ifdef CGI /* set an expiration date for redirects (in seconds) */ #define EXPIRES (3*24*60*60) /* timeout on long transfers (10 minutes) */ #define TIMEOUT (10*60) /* sets permissions for the cached GIF */ #define PERM (022) /* variable in config file (or environment) that contains the absolute location of source TIFFs */ #define ARTICLES_DIR "ARTICLES_DIR" static void dump (int); static int cgi = 1; static char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } static void unescape_url(char *url) { register int x,y; for (x=0,y=0;url[y];++x,++y) { if ((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } static void plustospace(char *str) { register int x; for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; } static void killdotdot(char *url) { register int x, y; for (x = 0, y = 0; url[y]; y++) { if (0 == strncmp(&url[y],"/../",4)) y += 2; else if (strncmp(&url[y],"//",2)) url[x++] = url[y]; } url[x] = '\0'; } static int cgiparse (int *argc, char ***argv) { int n; char *adr, *str; if (getenv("REQUEST_METHOD") == NULL) return(1); if (strcasecmp(getenv("REQUEST_METHOD"),"POST") == 0) { if ((adr = getenv("CONTENT_LENGTH")) == NULL) { int size = 0; if ((adr = (char *) malloc (BUFSIZ+1)) == NULL) return(2); while ((n = fread(&adr[size],1,BUFSIZ,stdin)) == BUFSIZ) { size += BUFSIZ; if ((adr = (char *) realloc (adr, size + BUFSIZ + 1)) == NULL) return(2); } adr[size+n] = 0; } else { n = 2 + (int) strtol(adr, (char **)NULL, 10); if ((adr = malloc(n)) == NULL) return(2); str = adr; while ((n > 0) && (read(0, str, 1) == 1)) { if (*str) { n--; str++; } } *str = 0; } } else { if ((str = getenv("QUERY_STRING")) == NULL) return(1); n = strlen(str); if ((adr = malloc(n + 2)) == NULL) return(2); strcpy(adr, str); } if ((*argv = (char **) malloc (10 * sizeof(char *))) == NULL) return(2); for (str = strtok(adr, "&"), n = 0; str; str = strtok(NULL, "&"), n++) { plustospace(str); unescape_url(str); if (n % 10 == 9) { if ((*argv = (char **) realloc (argv, 10 * sizeof(char *))) == NULL) return(2); } (*argv)[n] = str; } *argc = n; return(0); } #include /* HTTP status codes */ #define ERROR 500 #define OK 200 #define NOTMOD 304 #define REDIRECT 302 static struct _http_status { int status; const char *msg; } http_code[5] = { { ERROR, "Internal Server Error" }, { OK, "OK" }, { NOTMOD, "Not Modified" }, { REDIRECT, "Found" }, { 0, NULL } }; static int print_http_head (int code, char *head) { register int i; time_t timeval; struct tm *tmstruct; char timestr[30], *str; timeval = time(NULL); tmstruct = gmtime(&timeval); strftime(timestr, 30, "%a, %d %b %Y %T GMT", tmstruct); if (strncmp(progname, "nph-", 4) == 0) { for (i = 0; http_code[i].status != 0; i++) { if (code == http_code[i].status) break; } if (0 == http_code[i].status) i = 0; printf("HTTP/1.0 %i %s\n", code, http_code[i].msg); printf ("Date: %s\n", timestr) ; if ((str = getenv("SERVER_SOFTWARE"))) printf ("Server: %s\n", str); } if (code == OK) printf ("Last-modified: %s\n", timestr); if (code == REDIRECT) { /* do expires header */ timeval += EXPIRES; tmstruct = gmtime(&timeval); strftime(timestr, 30, "%a, %d %b %Y %T GMT", tmstruct); printf ("Expires: %s\n", timestr); } if (head) printf ("%s", head); printf("\n"); fflush(stdout); return(0); } static char * get_param (const char *param, int key) { char entry[BUFSIZ], dir[BUFSIZ], p[BUFSIZ]; FILE *fp; if (key <= 0) return(NULL); sprintf(entry, "%s/config/abs_config.%d", getenv("DOCUMENT_ROOT"), key); if (access(entry, R_OK)) return(NULL); if (NULL == (fp = fopen(entry, "r"))) return(NULL); sprintf(p, "%s=%%s", param); while (fgets(entry, BUFSIZ, fp)) { if (sscanf(entry, p, &dir)) { if (access(dir, F_OK)) { fclose(fp); return(NULL); } else { fclose(fp); return(strdup(dir)); } } } fclose(fp); return(NULL); } static void usage(void) { printf("%s: Usage error\n", progname); exit(0); } #else /* CGI */ static int cgi = 0; static void usage(void) { fprintf(stderr, "Usage: %s [options] file.tiff > file.gif\n", progname); fprintf(stderr, " This program reads a 1-bit/pixel TIFF file and writes a grayscale\n"); fprintf(stderr, " GIF image to stdout\n"); fprintf(stderr, "Options: -res dpi output GIF has dpi dots per inch\n"); fprintf(stderr, " -bg #RRGGBB set GIF background color to #RRBBGG\n"); fprintf(stderr, " -bits N output GIF has N bits per pixel\n"); fprintf(stderr, " -scale M scale output GIF by a factor of M\n"); fprintf(stderr, " -c run in command line mode\n"); exit(1); } #endif /* CGI */ /* signal handling */ static void cleanup (int signo) { fflush(stdout); fflush(stderr); if (giffd) close(giffd); else if (gif) fclose(gif); if (0 == access(imfile,W_OK)) unlink(imfile); #ifdef CGI exit(0); #else if (signo) exit(signo); else return; #endif } int main (int argc, char *argv[]) { int argn = 1, xdpi = 0, ydpi = 0, resolution = 0, factor = 0, i; int bits = DEFAULT_GIFBITS, dpi = 0, cachegif = 0; TIFF *tif; const char *dot = "."; char *f, *dir, tiff[BUFSIZ], entry[BUFSIZ]; float xres, yres; uint32 w, h; uint16 config, bitspersample; void cleanup(); #ifdef CGI char httphead[BUFSIZ], uri[BUFSIZ], *files[NMAX]; int urloff = 0, dbk, n; char *artdir = getenv(ARTICLES_DIR); #endif progname = strrchr(argv[0], '/'); if (progname) progname++; else progname = argv[0]; #ifdef CGI signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGABRT, cleanup); signal(SIGPIPE, cleanup); signal(SIGALRM, cleanup); signal(SIGTERM, cleanup); signal(SIGUSR1, cleanup); signal(SIGUSR2, cleanup); signal(SIGTRAP, dump); signal(SIGFPE, dump); signal(SIGBUS, dump); signal(SIGSEGV, dump); signal(SIGSYS, dump); alarm(TIMEOUT); umask (PERM); if (cgi) { /* we are a CGI script, parse input */ if (cgiparse(&argc,&argv)) { fprintf(stderr, "%s: error reading CGI input\n", progname); exit(1); } argn = 0; n = 0; while (argn < argc) { if (sscanf(argv[argn], "bg=#%02x%02x%02x", &BGColor.red, &BGColor.green, &BGColor.blue)) {} else if (sscanf(argv[argn], "res=%d", &dpi)) {} else if (sscanf(argv[argn], "scale=%d", &factor)) {} else if (sscanf(argv[argn], "bits=%d", &bits)) {} else if (sscanf(argv[argn], "debug=%d", &debug)) {} else if (sscanf(argv[argn], "db_key=%d", &dbk) && artdir == NULL) artdir = get_param (ARTICLES_DIR, dbk); else if (n < NMAX) files[n++] = argv[argn]; argn++; } for (i = 0; i < n; i++) { argv[i] = files[i]; } argc = n; argn = 0; if (argn > argc) print_http_head(ERROR, NULL); else if (argn > argc - 1) argc = argn + 1; /* remove double dots to avoid letting users fetch documents outside of article tree */ killdotdot(argv[argn]); if (artdir) strncpy(entry, artdir, BUFSIZ-1); else entry[0] = '\0'; entry[BUFSIZ-1] = '\0'; urloff = strlen(entry); if (argv[argn][0] != '/') strcat(entry, "/"); strncat(entry, argv[argn], BUFSIZ - strlen(entry) - 1); entry[BUFSIZ-1] = '\0'; } #else while (argn < argc && argv[argn][0] == '-') { if (match(argv[argn], "-debug", 3)) debug = 1; else if (0 == strcmp(argv[argn], "-c")) cgi = 0; else if (0 == strcmp(argv[argn], "-bg")) { if (++argn >= argc) usage(); else if (0 == sscanf(argv[argn], "#%02x%02x%02x", &BGColor.red, &BGColor.green, &BGColor.blue)) usage(); } else if (0 == strcmp(argv[argn], "-res")) { if (++argn >= argc) usage(); else if (0 == (dpi = (int)strtol(argv[argn], NULL, 10))) usage(); } else if (0 == strcmp(argv[argn], "-bits")) { if (++argn >= argc) usage(); else if (0 == (bits = (int)strtol(argv[argn], NULL, 10))) usage(); } else if (0 == strcmp(argv[argn], "-scale")) { if (++argn >= argc) usage(); else if (0 == (factor = (int)strtol(argv[argn], NULL, 10))) usage(); } argn++; } if (argn >= argc) usage(); strncpy(entry, argv[argn++], BUFSIZ -1); entry[BUFSIZ-1] = '\0'; cachegif = 0; if (argn < argc) usage(); #endif if (dpi == 0 && factor == 0) dpi = DEFAULT_GIFRES; strcpy(tiff, entry); /* if input file isn't there, exit with error message */ if ((access(tiff, R_OK))) { #ifdef CGI if (cgi) print_http_head(ERROR, NULL); #endif fprintf(stderr, "%s: %s: cannot open!\n", progname, tiff); exit(1); } #ifdef CGI /* cache GIF only if resolution and depth are set to pre-defined values */ for (i = 0; CacheGIF[i].bits != 0; i++) { if (bits == CacheGIF[i].bits && (dpi == CacheGIF[i].dpi || factor == CacheGIF[i].factor)) { cachegif = 1; continue; } } /* if output GIF needs to be cached, create file name and open fd */ if (cachegif) { /* now build output GIF pathname */ if (argv[argn][0] == '/') entry[0] = '\0'; else entry[0] = '/'; strncat(entry, argv[argn], BUFSIZ - 2); entry[BUFSIZ-1] = '\0'; /* remove ".tiff" extension if one exists */ if ((f = strrchr(entry, '.'))) *f = 0; else f = entry; /* append ".gif" to tiff file name */ strcat(entry, ".gif"); /* complete GIF path name */ sprintf(imfile, "%s/cache%s", getenv("DOCUMENT_ROOT"), entry); if (cgi) { /* check to see whether the GIF file is already there */ if (0 == access(imfile, R_OK)) { /* file was cached: see if GIF has more recent timestamp */ struct stat gbuf, tbuf; if (lstat(tiff, &tbuf)) { fprintf(stderr, "%s: %s: cannot stat()\n", progname, tiff); } else if (lstat(imfile, &gbuf)) { fprintf(stderr, "%s: %s: cannot stat()\n", progname, imfile); } else if (gbuf.st_ctime > tbuf.st_ctime) { sprintf(httphead, "Location: /cache%s\n", entry); print_http_head(REDIRECT, httphead); exit(0); } } } /* create output directory tree if necessary */ strcpy(entry, imfile); if ((f = strrchr(entry, '/'))) { *f++ = '\0'; dir = entry; } else { f = entry; dir = (char *)dot; } if (access(dir, F_OK | W_OK)) { if (mkdirs(dir)) { fprintf(stderr, "%s: error creating directory %s, " "skipping GIF caching.\n", progname, dir); cachegif = 0; } } } #endif if (debug) fprintf(stderr, "%s: input file is: %s\n", progname, tiff); if (NULL == (tif = TIFFOpen(tiff, "r"))) { fprintf(stderr, "%s: %s: error: cannot open file for reading!\n", progname, tiff); exit(1); } if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h)) { fprintf(stderr, "%s: %s: error: cannot get imagelength!\n", progname, tiff); exit(1); } if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w)) { fprintf(stderr, "%s: %s: error: cannot get imagewidth!\n", progname, tiff); exit(1); } if (TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config) && config != PLANARCONFIG_CONTIG) { fprintf(stderr, "%s: %s: error: file has non-planar configuration!\n", progname, tiff); exit(1); } if (TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample) && bitspersample != 1) { fprintf(stderr, "%s: %s: error: file has bitsperpixel != 1!\n", progname, tiff); exit(1); } /* calculate resolution (assume it's a multiple of 75 dpi) */ xres = yres = 0.0; if (!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres)) { for (xdpi = 75; ((double)w/(double)xdpi) > PGSIZEX; xdpi *= 2) {} xres = 0; } if (!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)) { for (ydpi = 75; ((double)h/(double)ydpi) > PGSIZEY; ydpi *= 2) {} yres = 0; } if (xres == 0 && yres == 0) resolution = max(xdpi,ydpi); else resolution = max(xres,yres); if (debug) fprintf(stderr, "%s: input file resolution set to %ddpi\n", progname, resolution); if (! factor) factor = resolution / dpi; else dpi = resolution / factor; if (dpi > resolution) { fprintf(stderr, "%s: input resolution (%ddpi) must be greater than output resolutions (%ddpi)!\n", progname, resolution, dpi); exit(1); } if (factor != 2 && factor != 3 && factor != 4 && factor != 6 && factor != 8 && factor != 16 && factor != 32 && factor != 64) { if (debug) fprintf(stderr, "%s: %s: cannot reduce image by a factor of %d\n", progname, tiff, factor); dpi = DEFAULT_GIFRES; factor = resolution / dpi; if (debug) fprintf(stderr, "%s: %s: reduction factor now reset to %d\n", progname, tiff, factor); } if (debug) { fprintf(stderr, "%s: output file resolution set to %ddpi\n", progname, dpi); fprintf(stderr, "%s: output bits per pixel set to %d\n", progname, bits); fprintf(stderr, "%s: reducing image by a factor of %d\n", progname, factor); if (cachegif) fprintf(stderr, "%s: GIF cached to: %s\n", progname, imfile); } if (cachegif) { #ifdef MMAP if ((giffd = open(imfile, O_RDWR | O_CREAT | O_TRUNC, PERM)) < 0) { fprintf(stderr, "%s: %s: error opening output file %s\n", progname, tiff, imfile); exit(1); } lseek(giffd, 2 * (w/factor) * (h/factor), SEEK_SET); write(giffd, "", 1); marrayptr = marray = (unsigned char *) mmap(0, 2*(w/factor)* (h/factor), PROT_WRITE, MAP_SHARED, giffd, 0); if (marray == (unsigned char *) -1) { fprintf(stderr, "%s: %s: mmap error (%ld bytes)\n", progname, tiff, 2 * (w/factor)*(h/factor)); close(giffd); giffd = 0; unlink(imfile); marrayptr = marray = (unsigned char *) malloc (2 * (w/factor) * (h/factor)); if (marray == NULL) exit(1); } #else if (NULL == (gif = fopen(imfile, "w"))) { fprintf(stderr, "%s: %s: error opening output file %s\n", progname, tiff, imfile); cachegif = 0; } #endif } #ifdef CGI if (cgi) { /* we're sending the contents of the new file back, but set the base URL to be the cached image's one (note: HTTP/1.0 clients ignore this) */ if (cachegif) sprintf(httphead, "Content-Location: %s\n", uri); strcat(httphead, "Content-type: image/gif\n"); print_http_head(OK, httphead); } #endif if (tiff2gif((int)w, (int)h, factor, bits, tif, gif)) { fprintf(stderr, "%s: %s: error creating gif image file %s\n", progname, tiff, imfile); cleanup(0); } TIFFClose(tif); #ifdef MMAP if (giffd) { /* truncate memory mapped file */ giffcntl.l_whence = 0; giffcntl.l_start = marrayptr - marray; giffcntl.l_len = 0; fcntl(giffd, F_FREESP, &giffcntl); close(giffd); giffd = 0; } #else if (cachegif && gif) fclose(gif); #endif exit(0); } /* create a directory (tree) */ static int mkdirs(char *dir) { char *p, path[128]; strcpy(path, dir); p = path; for (p = strchr((p+1), '/'); p; p = strchr((p+1), '/')) { *p = '\0'; if(access(path, F_OK)) /* create dir */ if (mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IXGRP |S_IROTH|S_IXOTH)) return 1; *p = '/'; } /* make sure last one got created */ if(access(dir, F_OK)) if (mkdir(dir, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IXGRP |S_IROTH|S_IXOTH)) return 1; return 0; } /************** GIF encoding routines (ugh!) ***************/ /**** stop reading here unless you have a tough stomach ****/ /* a code_int must be able to hold 2**BITS values of type int, and also -1 */ typedef int code_int; #ifdef SIGNED_COMPARE_SLOW typedef unsigned long int count_int; typedef unsigned short int count_short; #else /*SIGNED_COMPARE_SLOW*/ typedef long int count_int; #endif /*SIGNED_COMPARE_SLOW*/ static int GIFNextPixel(); static int GIFEncode(FILE*, int, int, int, int, int, int, int*, int*, int*); static void Putword(int, FILE*); static void compress(int, FILE*); static void output(code_int); static void cl_block(void); static void cl_hash(count_int); static void char_out(int); static void flush_char(void); static int mkcolormaps(int*, int*, int*, int); static int Width; static int Height; static int Factor; static int Pass = 0; static int Interlace; static int OutputCount = 0; static unsigned char *Row[MAXSCALE]; static unsigned char *LUT8, *LUT6, *LUT4, *LUT3, *LUT2; static TIFF *Tif; static int x; static uint32 row; #define CGIWRITE(p,n) fwrite((p),(n),1,stdout) #define CGIPUT(c) putchar(c) #ifdef MMAP #define INIT_CHAR *marrayptr++ = OutputCount = 0 #define FLUSH_CHAR() do { \ if (OutputCount) { \ marrayptr[-OutputCount-1] = OutputCount; \ CGIWRITE(&marrayptr[-OutputCount-1],OutputCount+1); \ } else { \ marrayptr--; \ } \ } while (0) /************ #define CHAR_OUT(c) do { \ OutputCount++; \ *marrayptr++ = (c); \ if (OutputCount >= 254) { \ marrayptr[-OutputCount-1] = OutputCount; \ CGIWRITE(&marrayptr[-OutputCount-1],OutputCount+1); \ *marrayptr++ = OutputCount = 0; \ } \ } while (0) *************/ static void CHAR_OUT (int c) { OutputCount++; *marrayptr++ = (c); if (OutputCount >= 254) { marrayptr[-OutputCount-1] = OutputCount; fwrite(&marrayptr[-OutputCount-1],OutputCount+1,1,stdout); *marrayptr++ = OutputCount = 0; } } #define PUTC(c,f) do { \ *marrayptr++ = (c); \ CGIPUT(c); \ } while (0) #define FWRITE(p,s,n,f) do { \ memcpy(marrayptr,(p),((n)*(s))); \ CGIWRITE(marrayptr,((n)*(s))); \ marrayptr += ((n)*(s)); \ } while (0) #else /* MMAP */ #define INIT_CHAR OutputCount = 0 #define FLUSH_CHAR() flush_char() #define CHAR_OUT(c) char_out(c) #define PUTC(c,f) do { \ if ((f)) \ putc((c),(f)); \ CGIPUT(c); \ } while (0) #define FWRITE(p,s,n,f) do { \ if ((f)) \ fwrite((p),(s),(n),(f)); \ CGIWRITE((p),((n)*(s))); \ } while (0) #endif static int tiff2gif (int w, int h, int factor, int bits, TIFF *tif, FILE *gif) { int i; int red[MAXCOLORS], green[MAXCOLORS], blue[MAXCOLORS]; static unsigned char *image = NULL; static uint32 buffersize = 0; if (image == NULL) { buffersize = ((factor+1) * w) / 8; image = (unsigned char *) malloc (buffersize); } else if (buffersize < ((factor+1) * w) / 8) { if (debug) fprintf(stderr, "%s: increasing output buffer size from %ld to %ld\n", progname, buffersize, (long)((factor+1) * w / 8)); buffersize = ((factor+1) * w) / 8; image = (unsigned char *) realloc ((void *)image, buffersize); } if (image == NULL) return 1; for (i = 0; i < factor; i++) Row[i] = &image[i * (w/8)]; /* initialize global variables */ Height = h / factor; Width = w / factor; Factor = factor; OutputCount = 0; Tif = tif; x = 0; row = 0; if (mkcolormaps(red, green, blue, bits)) return (1); if (GIFEncode(gif, Width, Height, 0, 0, -1, bits, red, green, blue)) return (1); else return (0); } /* scales 0-4 values into 4-bit values, inverting them */ static unsigned char lut4to4inv[] = { 15, 11, 7, 3, 0 }; /* scales 0-9 values into 4-bit values, inverting them */ static unsigned char lut9to4inv[] = { 15, 14, 12, 10, 8, 7, 5, 3, 1, 0 }; /* scales 0-16 values into 4-bit values, inverting them */ static unsigned char lut16to4inv[] = { 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; /* scales 0-36 values into 4-bit values, inverting them */ static unsigned char lut36to4inv[] = { 15, 15, 15, 14, 14, 13, 13, 12, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0 }; /* scales 0-64 values into 4-bit values, inverting them */ static unsigned char lut64to4inv[] = { 15, 15, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, }; /* scales 0-4 values into 3-bit values, inverting them */ static unsigned char lut4to3inv[] = { 7, 5, 3, 1, 0 }; /* scales 0-9 values into 3-bit values, inverting them */ static unsigned char lut9to3inv[] = { 7, 6, 5, 5, 4, 3, 2, 2, 1, 0 }; /* scales 0-16 values into 3-bit values, inverting them */ static unsigned char lut16to3inv[] = { 7, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 }; /* scales 0-36 values into 3-bit values, inverting them */ static unsigned char lut36to3inv[] = { 7, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; /* scales 0-64 values into 3-bit values, inverting them */ static unsigned char lut64to3inv[] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; /* scales 0-4 values into 2-bit values, inverting them */ static unsigned char lut4to2inv[] = { 3, 3, 2, 1, 0 }; /* scales 0-9 values into 2-bit values, inverting them */ static unsigned char lut9to2inv[] = { 3, 3, 3, 2, 2, 1, 1, 1, 0, 0 }; /* scales 0-16 values into 2-bit values, inverting them */ static unsigned char lut16to2inv[] = { 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 }; /* scales 0-36 values into 2-bit values, inverting them */ static unsigned char lut36to2inv[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* scales 0-64 values into 2-bit values, inverting them */ static unsigned char lut64to2inv[] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* scales 0-4 values into 1-bit values, inverting them */ static unsigned char lut4to1inv[] = { 1, 1, 1, 0, 0 }; /* scales 0-9 values into 1-bit values, inverting them */ static unsigned char lut9to1inv[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; /* scales 0-16 values into 1-bit values, inverting them */ static unsigned char lut16to1inv[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; /* scales 0-36 values into 1-bit values, inverting them */ static unsigned char lut36to1inv[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* scales 0-64 values into 1-bit values, inverting them */ static unsigned char lut64to1inv[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* returns number of higher 3 bits set to 1 in entry */ static unsigned char lut3hi[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; /* returns number of lower 4 bits set to 1 in entry */ static unsigned char lut4lo[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 }; /* returns number of upper 4 bits set to 1 in entry */ static unsigned char lut4hi[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; /* returns number of higher 6 bits set to 1 in entry */ static unsigned char lut6hi[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6 }; /* returns number of bits set to 1 in entry */ static unsigned char lut8[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; static unsigned char lut2hi1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; static unsigned char lut2hi2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; static unsigned char lut2lo1[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2 }; static unsigned char lut2lo2[] = { 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2 }; static int mkcolormaps(int *red, int *green, int *blue, int bpp) { int i, grays; double rstep, gstep, bstep; if (bpp <= 0) return (1); grays = 0x1 << bpp; rstep = (double)BGColor.red / (double)(grays - 1); gstep = (double)BGColor.green / (double)(grays - 1); bstep = (double)BGColor.blue / (double)(grays - 1); for (i = 0; i < grays; i++) { red[i] = rstep * i; green[i] = gstep * i; blue[i] = bstep * i; } for( ; i < MAXCOLORS; i++) red[i] = green[i] = blue[i] = 0; switch (bpp) { case 1: LUT8 = lut64to1inv; LUT6 = lut36to1inv; LUT4 = lut16to1inv; LUT3 = lut9to1inv; LUT2 = lut4to1inv; break; case 2: LUT8 = lut64to2inv; LUT6 = lut36to2inv; LUT4 = lut16to2inv; LUT3 = lut9to2inv; LUT2 = lut4to2inv; break; case 3: LUT8 = lut64to3inv; LUT6 = lut36to3inv; LUT4 = lut16to3inv; LUT3 = lut9to3inv; LUT2 = lut4to3inv; break; case 4: /* fall through */ default: LUT8 = lut64to4inv; LUT6 = lut36to4inv; LUT4 = lut16to4inv; LUT3 = lut9to4inv; LUT2 = lut4to4inv; break; } return (0); } static int GIFNextPixel(void) { int n, r = EOF; unsigned int sum = 0; static int bits = 0, bitsleft = 0; static unsigned char *i1, *i2, *i3, *i4, *i5, *i6, *i7, *i8; static unsigned char *i[MAXSCALE]; x = x % Width; if (x == 0) { /* see if we are at the end of the image */ if (row >= Height * Factor) return(EOF); /* need to read another line */ for (n = 0; n < Factor; n++) { if (TIFFReadScanline(Tif, Row[n], row++, 0) < 0) return(EOF); else i[n] = Row[n]; } i1 = Row[0]; i2 = Row[1]; i3 = Row[2]; i4 = Row[3]; i5 = Row[4]; i6 = Row[5]; i7 = Row[6]; i8 = Row[7]; bits = 0; bitsleft = 0; } /* now produce next pixel */ x++; switch (Factor) { case 64: /* do one row at a time */ for (n = sum = 0; n < 64; n++, i[n] += 8) sum += lut8[*(i[n])] + lut8[*(i[n]+1)] + lut8[*(i[n]+2)] + lut8[*(i[n]+3)] + lut8[*(i[n]+4)] + lut8[*(i[n]+5)] + lut8[*(i[n]+6)] + lut8[*(i[n]+7)]; /* divide results of sum by 64 (iterations in loop above) */ r = LUT8[sum >> 6]; break; case 32: /* do two rows at a time for efficiency */ for (n = sum = 0; n < 32; n++, i[n] += 4) sum += lut8[*(i[n])] + lut8[*(i[n]+1)] + lut8[*(i[n]+2)] + lut8[*(i[n]+3)]; /* divide results of sum by 16 (iterations in loop above) */ r = LUT8[sum >> 4]; break; case 16: for (n = sum = 0; n < 16; n++, i[n] += 2) sum += lut8[*(i[n])] + lut8[*(i[n]+1)]; /* divide results of sum by 4 (iterations in loop above) */ r = LUT8[sum >> 2]; break; case 8: sum = lut8[*(i[0]++)] + lut8[*(i[1]++)] + lut8[*(i[2]++)] + lut8[*(i[3]++)] + lut8[*(i[4]++)] + lut8[*(i[5]++)] + lut8[*(i[6]++)] + lut8[*(i[7]++)]; r = LUT8[sum]; break; case 6: sum = lut6hi[(*i1 << bits) & 0xfc] + lut6hi[(*i2 << bits) & 0xfc] + lut6hi[(*i3 << bits) & 0xfc] + lut6hi[(*i4 << bits) & 0xfc] + lut6hi[(*i5 << bits) & 0xfc] + lut6hi[(*i6 << bits) & 0xfc]; bits += 6; if (bits == 8) { i1++; i2++; i3++; i4++; i5++; i6++; bits = 0; } else if (bits > 8) { i1++; i2++; i3++; i4++; i5++; i6++; bitsleft = 14 - bits; sum += lut6hi[(*i1 >> bitsleft) & 0xfc] + lut6hi[(*i2 >> bitsleft) & 0xfc] + lut6hi[(*i3 >> bitsleft) & 0xfc] + lut6hi[(*i4 >> bitsleft) & 0xfc] + lut6hi[(*i5 >> bitsleft) & 0xfc] + lut6hi[(*i6 >> bitsleft) & 0xfc]; bits -= 8; } r = LUT6[sum]; break; case 4: if (x % 2) sum = lut4hi[*i1] + lut4hi[*i2] + lut4hi[*i3] + lut4hi[*i4]; else sum = lut4lo[*i1++] + lut4lo[*i2++] + lut4lo[*i3++] + lut4lo[*i4++]; r = LUT4[sum]; break; case 3: sum = lut3hi[(*i1 << bits) & 0xe0] + lut3hi[(*i2 << bits) & 0xe0] + lut3hi[(*i3 << bits) & 0xe0]; bits += 3; if (bits == 8) { i1++; i2++; i3++; bits = 0; } else if (bits > 8) { i1++; i2++; i3++; bitsleft = 11 - bits; sum += lut3hi[(*i1 >> bitsleft) & 0xe0] + lut3hi[(*i2 >> bitsleft) & 0xe0] + lut3hi[(*i3 >> bitsleft) & 0xe0]; bits -= 8; } r = LUT3[sum]; break; case 2: switch (bits) { case 0: sum = lut2hi1[*i1] + lut2hi1[*i2]; bits++; break; case 1: sum = lut2hi2[*i1] + lut2hi2[*i2]; bits++; break; case 2: sum = lut2lo1[*i1] + lut2lo1[*i2]; bits++; break; case 3: sum = lut2lo2[*i1] + lut2lo1[*i2]; i1++, i2++; bits = 0; break; } r = LUT2[sum]; break; } /* switch */ return(r); } /* private */ static int GIFEncode(FILE* fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue) { int B; int RWidth, RHeight; int LeftOfs, TopOfs; int Resolution; int ColorMapSize; int InitCodeSize; int i; char gifsig[20]; Interlace = GInterlace; ColorMapSize = 1 << BitsPerPixel; RWidth = Width = GWidth; RHeight = Height = GHeight; LeftOfs = TopOfs = 0; Resolution = BitsPerPixel; /* * Indicate which pass we are on (if interlace) */ Pass = 0; /* * The initial code size */ if( BitsPerPixel <= 1 ) InitCodeSize = 2; else InitCodeSize = BitsPerPixel; /* * Write the Magic header */ sprintf(gifsig, "GIF8%ca", (Transparent < 0) ? '7' : '9'); FWRITE(gifsig, 1, 6, fp); /* * Write out the screen width and height */ Putword( RWidth, fp ); Putword( RHeight, fp ); /* * Indicate that there is a global colour map */ B = 0x80; /* Yes, there is a color map */ /* * OR in the resolution */ B |= (Resolution - 1) << 5; /* * OR in the Bits per Pixel */ B |= (BitsPerPixel - 1); /* * Write it out */ PUTC( B, fp ); /* * Write out the Background colour */ PUTC( Background, fp ); /* * Byte of 0's (future expansion) */ PUTC( 0, fp ); /* * Write out the Global Colour Map */ for( i=0; i= 0 ) { PUTC( '!', fp ); PUTC( 0xf9, fp ); PUTC( 4, fp ); PUTC( 1, fp ); PUTC( 0, fp ); PUTC( 0, fp ); PUTC( Transparent, fp ); PUTC( 0, fp ); } /* * Write an Image separator */ PUTC( ',', fp ); /* * Write the Image header */ Putword( LeftOfs, fp ); Putword( TopOfs, fp ); Putword( Width, fp ); Putword( Height, fp ); /* * Write out whether or not the image is interlaced */ if( Interlace ) PUTC( 0x40, fp ); else PUTC( 0x00, fp ); /* * Write out the initial code size */ PUTC( InitCodeSize, fp ); /* * Go and actually compress the data */ compress(InitCodeSize+1, fp); /* * Write out a Zero-length packet (to end the series) */ PUTC( 0, fp ); /* * Write the GIF file terminator */ PUTC( ';', fp ); #ifdef MMAP return(0); #else if (fp) return(ferror( fp )); else return(0); #endif } /* * Write out a word to the GIF file */ static void Putword(int w, FILE *fp) { PUTC( w & 0xff, fp ); PUTC( (w / 256) & 0xff, fp ); } /*************************************************************************** * * GIFCOMPR.C - GIF Image compression routines * * Lempel-Ziv compression based on 'compress'. GIF modifications by * David Rowley (mgardi@watdcsu.waterloo.edu) * ***************************************************************************/ /* * General DEFINEs */ #define BITS 12 #define HSIZE 5003 /* 80% occupancy */ #ifdef NO_UCHAR typedef char char_type; #else /*NO_UCHAR*/ typedef unsigned char char_type; #endif /*NO_UCHAR*/ /* * * GIF Image compression - modified 'compress' * * Based on: compress.c - File compression ala IEEE Computer, June 1984. * * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) * Jim McKie (decvax!mcvax!jim) * Steve Davies (decvax!vax135!petsd!peora!srd) * Ken Turkowski (decvax!decwrl!turtlevax!ken) * James A. Woods (decvax!ihnp4!ames!jaw) * Joe Orost (decvax!vax135!petsd!joe) * */ #include static int n_bits = 0; /* number of bits/code */ static int maxbits = BITS; /* user settable max # bits/code */ static code_int maxcode = 0; /* maximum code, given n_bits */ static code_int maxmaxcode = (code_int)1 << BITS; /* should NEVER generate this code */ #ifdef COMPATIBLE /* But wrong! */ # define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1) #else /*COMPATIBLE*/ # define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) #endif /*COMPATIBLE*/ static count_int htab [HSIZE]; static unsigned short codetab [HSIZE]; #define HashTabOf(i) htab[i] #define CodeTabOf(i) codetab[i] static code_int hsize = HSIZE; /* for dynamic table sizing */ /* * To save much memory, we overlay the table used by compress() with those * used by decompress(). The tab_prefix table is the same size and type * as the codetab. The tab_suffix table needs 2**BITS characters. We * get this from the beginning of htab. The output stack uses the rest * of htab, and contains characters. There is plenty of room for any * possible stack (stack used to be 8000 characters). */ #define tab_prefixof(i) CodeTabOf(i) #define tab_suffixof(i) ((char_type*)(htab))[i] #define de_stack ((char_type*)&tab_suffixof((code_int)1< 0 ) goto probe; nomatch: output ( (code_int) ent ); ++out_count; ent = c; #ifdef SIGNED_COMPARE_SLOW if ( (unsigned) free_ent < (unsigned) maxmaxcode) { #else /*SIGNED_COMPARE_SLOW*/ if ( free_ent < maxmaxcode ) { /* } */ #endif /*SIGNED_COMPARE_SLOW*/ CodeTabOf (i) = free_ent++; /* code -> hashtable */ HashTabOf (i) = fcode; } else cl_block(); } /* * Put out the final code. */ output( (code_int)ent ); ++out_count; output( (code_int) EOFCode ); } /***************************************************************** * TAG( output ) * * Output the given code. * Inputs: * code: A n_bits-bit integer. If == -1, then EOF. This assumes * that n_bits =< (long)wordsize - 1. * Outputs: * Outputs code to the file. * Assumptions: * Chars are 8 bits long. * Algorithm: * Maintain a BITS character long buffer (so that 8 codes will * fit in it exactly). Use the VAX insv instruction to insert each * code in turn. When the buffer fills up empty it and start over. */ static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; static void output(code_int code) { cur_accum &= masks[ cur_bits ]; if( cur_bits > 0 ) cur_accum |= ((long)code << cur_bits); else cur_accum = code; cur_bits += n_bits; while( cur_bits >= 8 ) { CHAR_OUT( (unsigned int)(cur_accum & 0xff) ); cur_accum >>= 8; cur_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if ( free_ent > maxcode || clear_flg ) { if( clear_flg ) { maxcode = MAXCODE (n_bits = g_init_bits); clear_flg = 0; } else { ++n_bits; if ( n_bits == maxbits ) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } if( code == EOFCode ) { /* * At EOF, write the rest of the buffer. */ while( cur_bits > 0 ) { CHAR_OUT( (unsigned int)(cur_accum & 0xff) ); cur_accum >>= 8; cur_bits -= 8; } FLUSH_CHAR(); fflush(stdout); if (g_outfile) fflush( g_outfile ); } } /* * Clear out the hash table */ static void cl_block () /* table clear for block compress */ { cl_hash ( (count_int) hsize ); free_ent = ClearCode + 2; clear_flg = 1; output( (code_int)ClearCode ); } static void cl_hash(register count_int hsize) /* reset code table */ { register count_int *htab_p = htab+hsize; register long i; register long m1 = -1; i = hsize - 16; do { /* might use Sys V memset(3) here */ *(htab_p-16) = m1; *(htab_p-15) = m1; *(htab_p-14) = m1; *(htab_p-13) = m1; *(htab_p-12) = m1; *(htab_p-11) = m1; *(htab_p-10) = m1; *(htab_p-9) = m1; *(htab_p-8) = m1; *(htab_p-7) = m1; *(htab_p-6) = m1; *(htab_p-5) = m1; *(htab_p-4) = m1; *(htab_p-3) = m1; *(htab_p-2) = m1; *(htab_p-1) = m1; htab_p -= 16; } while ((i -= 16) >= 0); for ( i += 16; i > 0; --i ) *--htab_p = m1; } /****************************************************************************** * * GIF Specific routines * ******************************************************************************/ /* * Define the storage for the packet accumulator */ static char accum[ 256 ]; /* * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. */ static void char_out(int c) { accum[ OutputCount++ ] = c; if( OutputCount >= 254 ) flush_char(); } /* * Flush the packet to disk, and reset the accumulator */ static void flush_char() { if( OutputCount > 0 ) { PUTC( OutputCount, g_outfile ); FWRITE( accum, 1, OutputCount, g_outfile ); OutputCount = 0; } } /* The End */ #ifdef CGI /* used for debugging */ static void dump (int signo) { FILE *out; char tmp[BUFSIZ]; fflush(stdout); fflush(stderr); #ifdef MMAP if (giffd) close(giffd); #endif sprintf(tmp, "/tmp/%s.%ld", progname, (long)getpid()); if (NULL == (out = fopen(tmp,"w"))) exit(0); fprintf(out, "Signal received: %d\n", signo); fprintf(out, "Input file: %s\n", imfile); #ifdef MMAP fprintf(out, "Array pointer base address: %ld (0x%lx)\n", (long)marray, (long)marray); fprintf(out, "Array pointer current location: %ld (0x%lx)\n", (long)marrayptr, (long)marrayptr); #endif fprintf(out, "Partial output byte count: %d\n", OutputCount); fprintf(out, "Input byte count: %ld\n", in_count); fprintf(out, "Output byte count: %ld\n", out_count); fprintf(out, "Output Image width: %d\n", Width); fprintf(out, "Output Image height: %d\n", Height); fclose(out); /* restore signal handler */ signal(signo, SIG_DFL); } #endif