| 1 | /* $Id$ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 1990-1997 Sam Leffler |
| 5 | * Copyright (c) 1991-1997 Silicon Graphics, Inc. |
| 6 | * |
| 7 | * Permission to use, copy, modify, distribute, and sell this software and |
| 8 | * its documentation for any purpose is hereby granted without fee, provided |
| 9 | * that (i) the above copyright notices and this permission notice appear in |
| 10 | * all copies of the software and related documentation, and (ii) the names of |
| 11 | * Sam Leffler and Silicon Graphics may not be used in any advertising or |
| 12 | * publicity relating to the software without the specific, prior written |
| 13 | * permission of Sam Leffler and Silicon Graphics. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, |
| 16 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY |
| 17 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. |
| 18 | * |
| 19 | * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR |
| 20 | * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, |
| 21 | * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| 22 | * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF |
| 23 | * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
| 24 | * OF THIS SOFTWARE. |
| 25 | */ |
| 26 | |
| 27 | /* |
| 28 | * convert a GIF file into a TIFF file. |
| 29 | * based on Paul Haeberli's fromgif program which in turn is |
| 30 | * based on a GIF file reader by Marcel J.E. Mol March 23 1989 |
| 31 | * |
| 32 | * if input is 320 by 200 pixel aspect is probably 1.2 |
| 33 | * if input is 640 350 pixel aspect is probably 1.37 |
| 34 | * |
| 35 | */ |
| 36 | #include "tif_config.h" |
| 37 | |
| 38 | #include <stdio.h> |
| 39 | #include <stdlib.h> |
| 40 | #include <string.h> |
| 41 | #include <math.h> |
| 42 | |
| 43 | #ifdef HAVE_UNISTD_H |
| 44 | # include <unistd.h> |
| 45 | #endif |
| 46 | |
| 47 | #include "tiffio.h" |
| 48 | |
| 49 | #define GIFGAMMA (1.5) /* smaller makes output img brighter */ |
| 50 | #define IMAX 0xffff /* max intensity value */ |
| 51 | #define EXTRAFUDGE 128 /* some people write BAD .gif files */ |
| 52 | |
| 53 | #define streq(a,b) (strcmp(a,b) == 0) |
| 54 | #define strneq(a,b,n) (strncmp(a,b,n) == 0) |
| 55 | |
| 56 | unsigned short gamtab[256]; |
| 57 | |
| 58 | void |
| 59 | makegamtab(float gam) |
| 60 | { |
| 61 | int i; |
| 62 | |
| 63 | for(i=0; i<256; i++) |
| 64 | gamtab[i] = (unsigned short) (IMAX*pow(i/255.0,gam)+0.5); |
| 65 | } |
| 66 | |
| 67 | char* stuff[] = { |
| 68 | "usage: gif2tiff [options] input.gif output.tif", |
| 69 | "where options are:", |
| 70 | " -r # make each strip have no more than # rows", |
| 71 | "", |
| 72 | " -c lzw[:opts] compress output with Lempel-Ziv & Welch encoding", |
| 73 | " -c zip[:opts] compress output with deflate encoding", |
| 74 | " -c packbits compress output with packbits encoding", |
| 75 | " -c none use no compression algorithm on output", |
| 76 | "", |
| 77 | "LZW and deflate options:", |
| 78 | " # set predictor value", |
| 79 | "For example, -c lzw:2 to get LZW-encoded data with horizontal differencing", |
| 80 | NULL |
| 81 | }; |
| 82 | |
| 83 | static void |
| 84 | usage(void) |
| 85 | { |
| 86 | char buf[BUFSIZ]; |
| 87 | int i; |
| 88 | |
| 89 | setbuf(stderr, buf); |
| 90 | fprintf(stderr, "%s\n\n", TIFFGetVersion()); |
| 91 | for (i = 0; stuff[i] != NULL; i++) |
| 92 | fprintf(stderr, "%s\n", stuff[i]); |
| 93 | exit(-1); |
| 94 | } |
| 95 | |
| 96 | #define COLSIZE 256 |
| 97 | |
| 98 | unsigned char *stackp; |
| 99 | unsigned int prefix[4096]; |
| 100 | unsigned char suffix[4096]; |
| 101 | unsigned char stack[4096]; |
| 102 | int datasize,codesize,codemask; /* Decoder working variables */ |
| 103 | int clear,eoi; /* Special code values */ |
| 104 | int avail, oldcode; |
| 105 | |
| 106 | FILE *infile; |
| 107 | int global; /* Is there a global color map? */ |
| 108 | int globalbits; /* Number of bits of global colors */ |
| 109 | unsigned char globalmap[COLSIZE][3];/* RGB values for global color map */ |
| 110 | unsigned char *raster; /* Decoded image data */ |
| 111 | unsigned long width, height; |
| 112 | unsigned short red[COLSIZE]; |
| 113 | unsigned short green[COLSIZE]; |
| 114 | unsigned short blue[COLSIZE]; |
| 115 | char *filename, *imagename; |
| 116 | |
| 117 | static uint16 compression = COMPRESSION_PACKBITS; |
| 118 | static uint16 predictor = 0; |
| 119 | static uint32 rowsperstrip = (uint32) -1; |
| 120 | static int processCompressOptions(char*); |
| 121 | |
| 122 | int convert(void); |
| 123 | int checksignature(void); |
| 124 | void readscreen(void); |
| 125 | int readgifimage(char*); |
| 126 | void readextension(void); |
| 127 | int readraster(void); |
| 128 | int process(int, unsigned char**); |
| 129 | void initcolors(unsigned char [COLSIZE][3], int); |
| 130 | void rasterize(int, char*); |
| 131 | |
| 132 | int |
| 133 | main(int argc, char* argv[]) |
| 134 | { |
| 135 | extern int optind; |
| 136 | extern char *optarg; |
| 137 | int c, status; |
| 138 | |
| 139 | while ((c = getopt(argc, argv, "c:r:")) != -1) |
| 140 | switch (c) { |
| 141 | case 'c': /* compression scheme */ |
| 142 | if (!processCompressOptions(optarg)) |
| 143 | usage(); |
| 144 | break; |
| 145 | case 'r': /* rows/strip */ |
| 146 | rowsperstrip = atoi(optarg); |
| 147 | break; |
| 148 | case '?': |
| 149 | usage(); |
| 150 | /*NOTREACHED*/ |
| 151 | } |
| 152 | if (argc - optind != 2) |
| 153 | usage(); |
| 154 | |
| 155 | makegamtab(GIFGAMMA); |
| 156 | filename = argv[optind]; |
| 157 | imagename = argv[optind+1]; |
| 158 | if ((infile = fopen(imagename, "rb")) != NULL) { |
| 159 | int c; |
| 160 | fclose(infile); |
| 161 | printf("overwrite %s? ", imagename); fflush(stdout); |
| 162 | c = getc(stdin); |
| 163 | if (c != 'y' && c != 'Y') |
| 164 | return (1); |
| 165 | } |
| 166 | if ((infile = fopen(filename, "rb")) == NULL) { |
| 167 | perror(filename); |
| 168 | return (1); |
| 169 | } |
| 170 | status = convert(); |
| 171 | fclose(infile); |
| 172 | return (status); |
| 173 | } |
| 174 | |
| 175 | static int |
| 176 | processCompressOptions(char* opt) |
| 177 | { |
| 178 | if (streq(opt, "none")) |
| 179 | compression = COMPRESSION_NONE; |
| 180 | else if (streq(opt, "packbits")) |
| 181 | compression = COMPRESSION_PACKBITS; |
| 182 | else if (strneq(opt, "lzw", 3)) { |
| 183 | char* cp = strchr(opt, ':'); |
| 184 | if (cp) |
| 185 | predictor = atoi(cp+1); |
| 186 | compression = COMPRESSION_LZW; |
| 187 | } else if (strneq(opt, "zip", 3)) { |
| 188 | char* cp = strchr(opt, ':'); |
| 189 | if (cp) |
| 190 | predictor = atoi(cp+1); |
| 191 | compression = COMPRESSION_DEFLATE; |
| 192 | } else |
| 193 | return (0); |
| 194 | return (1); |
| 195 | } |
| 196 | |
| 197 | int |
| 198 | convert(void) |
| 199 | { |
| 200 | int ch; |
| 201 | char* mode = "w"; |
| 202 | |
| 203 | if (!checksignature()) |
| 204 | return (-1); |
| 205 | readscreen(); |
| 206 | while ((ch = getc(infile)) != ';' && ch != EOF) { |
| 207 | switch (ch) { |
| 208 | case '\0': break; /* this kludge for non-standard files */ |
| 209 | case ',': if (!readgifimage(mode)) |
| 210 | return (-1); |
| 211 | mode = "a"; /* subsequent images append */ |
| 212 | break; |
| 213 | case '!': readextension(); |
| 214 | break; |
| 215 | default: fprintf(stderr, "illegal GIF block type\n"); |
| 216 | return (-1); |
| 217 | } |
| 218 | } |
| 219 | return (0); |
| 220 | } |
| 221 | |
| 222 | int |
| 223 | checksignature(void) |
| 224 | { |
| 225 | char buf[6]; |
| 226 | |
| 227 | fread(buf,1,6,infile); |
| 228 | if (strncmp(buf,"GIF",3)) { |
| 229 | fprintf(stderr, "file is not a GIF file\n"); |
| 230 | return 0; |
| 231 | } |
| 232 | if (strncmp(&buf[3],"87a",3)) { |
| 233 | fprintf(stderr, "unknown GIF version number\n"); |
| 234 | return 0; |
| 235 | } |
| 236 | return 1; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * readscreen - |
| 241 | * Get information which is global to all the images stored |
| 242 | * in the file |
| 243 | */ |
| 244 | void |
| 245 | readscreen(void) |
| 246 | { |
| 247 | unsigned char buf[7]; |
| 248 | |
| 249 | fread(buf,1,7,infile); |
| 250 | global = buf[4] & 0x80; |
| 251 | if (global) { |
| 252 | globalbits = (buf[4] & 0x07) + 1; |
| 253 | fread(globalmap,3,1<<globalbits,infile); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | int |
| 258 | readgifimage(char* mode) |
| 259 | { |
| 260 | unsigned char buf[9]; |
| 261 | int local, interleaved; |
| 262 | unsigned char localmap[256][3]; |
| 263 | int localbits; |
| 264 | int status; |
| 265 | |
| 266 | if (fread(buf, 1, 9, infile) == 0) { |
| 267 | perror(filename); |
| 268 | return (0); |
| 269 | } |
| 270 | width = buf[4] + (buf[5] << 8); |
| 271 | height = buf[6] + (buf[7] << 8); |
| 272 | local = buf[8] & 0x80; |
| 273 | interleaved = buf[8] & 0x40; |
| 274 | |
| 275 | if (local == 0 && global == 0) { |
| 276 | fprintf(stderr, "no colormap present for image\n"); |
| 277 | return (0); |
| 278 | } |
| 279 | if ((raster = (unsigned char*) _TIFFmalloc(width*height+EXTRAFUDGE)) == NULL) { |
| 280 | fprintf(stderr, "not enough memory for image\n"); |
| 281 | return (0); |
| 282 | } |
| 283 | if (local) { |
| 284 | localbits = (buf[8] & 0x7) + 1; |
| 285 | |
| 286 | fprintf(stderr, " local colors: %d\n", 1<<localbits); |
| 287 | |
| 288 | fread(localmap, 3, 1<<localbits, infile); |
| 289 | initcolors(localmap, 1<<localbits); |
| 290 | } else if (global) { |
| 291 | initcolors(globalmap, 1<<globalbits); |
| 292 | } |
| 293 | if ((status = readraster())) |
| 294 | rasterize(interleaved, mode); |
| 295 | _TIFFfree(raster); |
| 296 | return status; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * readextension - |
| 301 | * Read a GIF extension block (and do nothing with it). |
| 302 | * |
| 303 | */ |
| 304 | void |
| 305 | readextension(void) |
| 306 | { |
| 307 | int count; |
| 308 | char buf[255]; |
| 309 | |
| 310 | (void) getc(infile); |
| 311 | while ((count = getc(infile))) |
| 312 | fread(buf, 1, count, infile); |
| 313 | } |
| 314 | |
| 315 | /* |
| 316 | * readraster - |
| 317 | * Decode a raster image |
| 318 | * |
| 319 | */ |
| 320 | int |
| 321 | readraster(void) |
| 322 | { |
| 323 | unsigned char *fill = raster; |
| 324 | unsigned char buf[255]; |
| 325 | register int bits=0; |
| 326 | register unsigned long datum=0; |
| 327 | register unsigned char *ch; |
| 328 | register int count, code; |
| 329 | int status = 1; |
| 330 | |
| 331 | datasize = getc(infile); |
| 332 | clear = 1 << datasize; |
| 333 | eoi = clear + 1; |
| 334 | avail = clear + 2; |
| 335 | oldcode = -1; |
| 336 | codesize = datasize + 1; |
| 337 | codemask = (1 << codesize) - 1; |
| 338 | for (code = 0; code < clear; code++) { |
| 339 | prefix[code] = 0; |
| 340 | suffix[code] = code; |
| 341 | } |
| 342 | stackp = stack; |
| 343 | for (count = getc(infile); count > 0; count = getc(infile)) { |
| 344 | fread(buf,1,count,infile); |
| 345 | for (ch=buf; count-- > 0; ch++) { |
| 346 | datum += (unsigned long) *ch << bits; |
| 347 | bits += 8; |
| 348 | while (bits >= codesize) { |
| 349 | code = datum & codemask; |
| 350 | datum >>= codesize; |
| 351 | bits -= codesize; |
| 352 | if (code == eoi) { /* This kludge put in */ |
| 353 | goto exitloop; /* because some GIF files*/ |
| 354 | } /* aren't standard */ |
| 355 | if (!process(code, &fill)) { |
| 356 | status = 0; |
| 357 | goto exitloop; |
| 358 | } |
| 359 | } |
| 360 | } |
| 361 | if (fill >= raster + width*height) { |
| 362 | fprintf(stderr, "raster full before eoi code\n"); |
| 363 | break; |
| 364 | } |
| 365 | } |
| 366 | exitloop: |
| 367 | if (fill != raster + width*height) { |
| 368 | fprintf(stderr, "warning: wrong rastersize: %ld bytes\n", |
| 369 | (long) (fill-raster)); |
| 370 | fprintf(stderr, " instead of %ld bytes\n", |
| 371 | (long) width*height); |
| 372 | } |
| 373 | return status; |
| 374 | } |
| 375 | |
| 376 | /* |
| 377 | * process - |
| 378 | * Process a compression code. "clear" resets the code table. |
| 379 | * Otherwise make a new code table entry, and output the bytes |
| 380 | * associated with the code. |
| 381 | */ |
| 382 | int |
| 383 | process(register int code, unsigned char** fill) |
| 384 | { |
| 385 | int incode; |
| 386 | static unsigned char firstchar; |
| 387 | |
| 388 | if (code == clear) { |
| 389 | codesize = datasize + 1; |
| 390 | codemask = (1 << codesize) - 1; |
| 391 | avail = clear + 2; |
| 392 | oldcode = -1; |
| 393 | return 1; |
| 394 | } |
| 395 | |
| 396 | if (oldcode == -1) { |
| 397 | *(*fill)++ = suffix[code]; |
| 398 | firstchar = oldcode = code; |
| 399 | return 1; |
| 400 | } |
| 401 | if (code > avail) { |
| 402 | fprintf(stderr, "code %d too large for %d\n", code, avail); |
| 403 | return 0; |
| 404 | } |
| 405 | |
| 406 | incode = code; |
| 407 | if (code == avail) { /* the first code is always < avail */ |
| 408 | *stackp++ = firstchar; |
| 409 | code = oldcode; |
| 410 | } |
| 411 | while (code > clear) { |
| 412 | *stackp++ = suffix[code]; |
| 413 | code = prefix[code]; |
| 414 | } |
| 415 | |
| 416 | *stackp++ = firstchar = suffix[code]; |
| 417 | prefix[avail] = oldcode; |
| 418 | suffix[avail] = firstchar; |
| 419 | avail++; |
| 420 | |
| 421 | if (((avail & codemask) == 0) && (avail < 4096)) { |
| 422 | codesize++; |
| 423 | codemask += avail; |
| 424 | } |
| 425 | oldcode = incode; |
| 426 | do { |
| 427 | *(*fill)++ = *--stackp; |
| 428 | } while (stackp > stack); |
| 429 | return 1; |
| 430 | } |
| 431 | |
| 432 | /* |
| 433 | * initcolors - |
| 434 | * Convert a color map (local or global) to arrays with R, G and B |
| 435 | * values. |
| 436 | * |
| 437 | */ |
| 438 | void |
| 439 | initcolors(unsigned char colormap[COLSIZE][3], int ncolors) |
| 440 | { |
| 441 | register int i; |
| 442 | |
| 443 | for (i = 0; i < ncolors; i++) { |
| 444 | red[i] = gamtab[colormap[i][0]]; |
| 445 | green[i] = gamtab[colormap[i][1]]; |
| 446 | blue[i] = gamtab[colormap[i][2]]; |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | void |
| 451 | rasterize(int interleaved, char* mode) |
| 452 | { |
| 453 | register unsigned long row; |
| 454 | unsigned char *newras; |
| 455 | unsigned char *ras; |
| 456 | TIFF *tif; |
| 457 | tstrip_t strip; |
| 458 | tsize_t stripsize; |
| 459 | |
| 460 | if ((newras = (unsigned char*) _TIFFmalloc(width*height+EXTRAFUDGE)) == NULL) { |
| 461 | fprintf(stderr, "not enough memory for image\n"); |
| 462 | return; |
| 463 | } |
| 464 | #define DRAWSEGMENT(offset, step) { \ |
| 465 | for (row = offset; row < height; row += step) { \ |
| 466 | _TIFFmemcpy(newras + row*width, ras, width);\ |
| 467 | ras += width; \ |
| 468 | } \ |
| 469 | } |
| 470 | ras = raster; |
| 471 | if (interleaved) { |
| 472 | DRAWSEGMENT(0, 8); |
| 473 | DRAWSEGMENT(4, 8); |
| 474 | DRAWSEGMENT(2, 4); |
| 475 | DRAWSEGMENT(1, 2); |
| 476 | } else |
| 477 | DRAWSEGMENT(0, 1); |
| 478 | #undef DRAWSEGMENT |
| 479 | |
| 480 | tif = TIFFOpen(imagename, mode); |
| 481 | if (!tif) { |
| 482 | TIFFError(imagename,"Can not open output image"); |
| 483 | exit(-1); |
| 484 | } |
| 485 | TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32) width); |
| 486 | TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32) height); |
| 487 | TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE); |
| 488 | TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); |
| 489 | TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); |
| 490 | TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8); |
| 491 | TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, |
| 492 | rowsperstrip = TIFFDefaultStripSize(tif, rowsperstrip)); |
| 493 | TIFFSetField(tif, TIFFTAG_COMPRESSION, compression); |
| 494 | switch (compression) { |
| 495 | case COMPRESSION_LZW: |
| 496 | case COMPRESSION_DEFLATE: |
| 497 | if (predictor != 0) |
| 498 | TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor); |
| 499 | break; |
| 500 | } |
| 501 | TIFFSetField(tif, TIFFTAG_COLORMAP, red, green, blue); |
| 502 | TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); |
| 503 | strip = 0; |
| 504 | stripsize = TIFFStripSize(tif); |
| 505 | for (row=0; row<height; row += rowsperstrip) { |
| 506 | if (TIFFWriteEncodedStrip(tif, strip, newras+row*width, stripsize) < 0) |
| 507 | break; |
| 508 | strip++; |
| 509 | } |
| 510 | TIFFClose(tif); |
| 511 | |
| 512 | _TIFFfree(newras); |
| 513 | } |
| 514 | |
| 515 | /* vim: set ts=8 sts=8 sw=8 noet: */ |