1/* $Id: pal2rgb.c 276 2010-06-30 12:18:30Z nijtmans $ */ 2 3/* 4 * Copyright (c) 1988-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#include "tif_config.h" 28 29#include <stdio.h> 30#include <string.h> 31#include <stdlib.h> 32#include <ctype.h> 33 34#ifdef HAVE_UNISTD_H 35# include <unistd.h> 36#endif 37 38#include "tiffio.h" 39 40#define streq(a,b) (strcmp(a,b) == 0) 41#define strneq(a,b,n) (strncmp(a,b,n) == 0) 42 43static void usage(void); 44static void cpTags(TIFF* in, TIFF* out); 45 46static int 47checkcmap(int n, uint16* r, uint16* g, uint16* b) 48{ 49 while (n-- > 0) 50 if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) 51 return (16); 52 fprintf(stderr, "Warning, assuming 8-bit colormap.\n"); 53 return (8); 54} 55 56#define CopyField(tag, v) \ 57 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) 58#define CopyField3(tag, v1, v2, v3) \ 59 if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3) 60 61static uint16 compression = (uint16) -1; 62static uint16 predictor = 0; 63static int quality = 75; /* JPEG quality */ 64static int jpegcolormode = JPEGCOLORMODE_RGB; 65static int processCompressOptions(char*); 66 67int 68main(int argc, char* argv[]) 69{ 70 uint16 bitspersample, shortv; 71 uint32 imagewidth, imagelength; 72 uint16 config = PLANARCONFIG_CONTIG; 73 uint32 rowsperstrip = (uint32) -1; 74 uint16 photometric = PHOTOMETRIC_RGB; 75 uint16 *rmap, *gmap, *bmap; 76 uint32 row; 77 int cmap = -1; 78 TIFF *in, *out; 79 int c; 80 extern int optind; 81 extern char* optarg; 82 83 while ((c = getopt(argc, argv, "C:c:p:r:")) != -1) 84 switch (c) { 85 case 'C': /* force colormap interpretation */ 86 cmap = atoi(optarg); 87 break; 88 case 'c': /* compression scheme */ 89 if (!processCompressOptions(optarg)) 90 usage(); 91 break; 92 case 'p': /* planar configuration */ 93 if (streq(optarg, "separate")) 94 config = PLANARCONFIG_SEPARATE; 95 else if (streq(optarg, "contig")) 96 config = PLANARCONFIG_CONTIG; 97 else 98 usage(); 99 break; 100 case 'r': /* rows/strip */ 101 rowsperstrip = atoi(optarg); 102 break; 103 case '?': 104 usage(); 105 /*NOTREACHED*/ 106 } 107 if (argc - optind != 2) 108 usage(); 109 in = TIFFOpen(argv[optind], "r"); 110 if (in == NULL) 111 return (-1); 112 if (!TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &shortv) || 113 shortv != PHOTOMETRIC_PALETTE) { 114 fprintf(stderr, "%s: Expecting a palette image.\n", 115 argv[optind]); 116 return (-1); 117 } 118 if (!TIFFGetField(in, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) { 119 fprintf(stderr, 120 "%s: No colormap (not a valid palette image).\n", 121 argv[optind]); 122 return (-1); 123 } 124 bitspersample = 0; 125 TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample); 126 if (bitspersample != 8) { 127 fprintf(stderr, "%s: Sorry, can only handle 8-bit images.\n", 128 argv[optind]); 129 return (-1); 130 } 131 out = TIFFOpen(argv[optind+1], "w"); 132 if (out == NULL) 133 return (-2); 134 cpTags(in, out); 135 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &imagewidth); 136 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &imagelength); 137 if (compression != (uint16)-1) 138 TIFFSetField(out, TIFFTAG_COMPRESSION, compression); 139 else 140 TIFFGetField(in, TIFFTAG_COMPRESSION, &compression); 141 switch (compression) { 142 case COMPRESSION_JPEG: 143 if (jpegcolormode == JPEGCOLORMODE_RGB) 144 photometric = PHOTOMETRIC_YCBCR; 145 else 146 photometric = PHOTOMETRIC_RGB; 147 TIFFSetField(out, TIFFTAG_JPEGQUALITY, quality); 148 TIFFSetField(out, TIFFTAG_JPEGCOLORMODE, jpegcolormode); 149 break; 150 case COMPRESSION_LZW: 151 case COMPRESSION_DEFLATE: 152 if (predictor != 0) 153 TIFFSetField(out, TIFFTAG_PREDICTOR, predictor); 154 break; 155 } 156 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric); 157 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3); 158 TIFFSetField(out, TIFFTAG_PLANARCONFIG, config); 159 TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, 160 rowsperstrip = TIFFDefaultStripSize(out, rowsperstrip)); 161 (void) TIFFGetField(in, TIFFTAG_PLANARCONFIG, &shortv); 162 if (cmap == -1) 163 cmap = checkcmap(1<<bitspersample, rmap, gmap, bmap); 164 if (cmap == 16) { 165 /* 166 * Convert 16-bit colormap to 8-bit. 167 */ 168 int i; 169 170 for (i = (1<<bitspersample)-1; i >= 0; i--) { 171#define CVT(x) (((x) * 255) / ((1L<<16)-1)) 172 rmap[i] = CVT(rmap[i]); 173 gmap[i] = CVT(gmap[i]); 174 bmap[i] = CVT(bmap[i]); 175 } 176 } 177 { unsigned char *ibuf, *obuf; 178 register unsigned char* pp; 179 register uint32 x; 180 ibuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(in)); 181 obuf = (unsigned char*)_TIFFmalloc(TIFFScanlineSize(out)); 182 switch (config) { 183 case PLANARCONFIG_CONTIG: 184 for (row = 0; row < imagelength; row++) { 185 if (!TIFFReadScanline(in, ibuf, row, 0)) 186 goto done; 187 pp = obuf; 188 for (x = 0; x < imagewidth; x++) { 189 *pp++ = (unsigned char) rmap[ibuf[x]]; 190 *pp++ = (unsigned char) gmap[ibuf[x]]; 191 *pp++ = (unsigned char) bmap[ibuf[x]]; 192 } 193 if (!TIFFWriteScanline(out, obuf, row, 0)) 194 goto done; 195 } 196 break; 197 case PLANARCONFIG_SEPARATE: 198 for (row = 0; row < imagelength; row++) { 199 if (!TIFFReadScanline(in, ibuf, row, 0)) 200 goto done; 201 for (pp = obuf, x = 0; x < imagewidth; x++) 202 *pp++ = (unsigned char) rmap[ibuf[x]]; 203 if (!TIFFWriteScanline(out, obuf, row, 0)) 204 goto done; 205 for (pp = obuf, x = 0; x < imagewidth; x++) 206 *pp++ = (unsigned char) gmap[ibuf[x]]; 207 if (!TIFFWriteScanline(out, obuf, row, 0)) 208 goto done; 209 for (pp = obuf, x = 0; x < imagewidth; x++) 210 *pp++ = (unsigned char) bmap[ibuf[x]]; 211 if (!TIFFWriteScanline(out, obuf, row, 0)) 212 goto done; 213 } 214 break; 215 } 216 _TIFFfree(ibuf); 217 _TIFFfree(obuf); 218 } 219done: 220 (void) TIFFClose(in); 221 (void) TIFFClose(out); 222 return (0); 223} 224 225static int 226processCompressOptions(char* opt) 227{ 228 if (streq(opt, "none")) 229 compression = COMPRESSION_NONE; 230 else if (streq(opt, "packbits")) 231 compression = COMPRESSION_PACKBITS; 232 else if (strneq(opt, "jpeg", 4)) { 233 char* cp = strchr(opt, ':'); 234 235 compression = COMPRESSION_JPEG; 236 while( cp ) 237 { 238 if (isdigit((int)cp[1])) 239 quality = atoi(cp+1); 240 else if (cp[1] == 'r' ) 241 jpegcolormode = JPEGCOLORMODE_RAW; 242 else 243 usage(); 244 245 cp = strchr(cp+1,':'); 246 } 247 } else if (strneq(opt, "lzw", 3)) { 248 char* cp = strchr(opt, ':'); 249 if (cp) 250 predictor = atoi(cp+1); 251 compression = COMPRESSION_LZW; 252 } else if (strneq(opt, "zip", 3)) { 253 char* cp = strchr(opt, ':'); 254 if (cp) 255 predictor = atoi(cp+1); 256 compression = COMPRESSION_DEFLATE; 257 } else 258 return (0); 259 return (1); 260} 261 262#define CopyField(tag, v) \ 263 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) 264#define CopyField2(tag, v1, v2) \ 265 if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2) 266#define CopyField3(tag, v1, v2, v3) \ 267 if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3) 268#define CopyField4(tag, v1, v2, v3, v4) \ 269 if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4) 270 271static void 272cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type) 273{ 274 switch (type) { 275 case TIFF_SHORT: 276 if (count == 1) { 277 uint16 shortv; 278 CopyField(tag, shortv); 279 } else if (count == 2) { 280 uint16 shortv1, shortv2; 281 CopyField2(tag, shortv1, shortv2); 282 } else if (count == 4) { 283 uint16 *tr, *tg, *tb, *ta; 284 CopyField4(tag, tr, tg, tb, ta); 285 } else if (count == (uint16) -1) { 286 uint16 shortv1; 287 uint16* shortav; 288 CopyField2(tag, shortv1, shortav); 289 } 290 break; 291 case TIFF_LONG: 292 { uint32 longv; 293 CopyField(tag, longv); 294 } 295 break; 296 case TIFF_RATIONAL: 297 if (count == 1) { 298 float floatv; 299 CopyField(tag, floatv); 300 } else if (count == (uint16) -1) { 301 float* floatav; 302 CopyField(tag, floatav); 303 } 304 break; 305 case TIFF_ASCII: 306 { char* stringv; 307 CopyField(tag, stringv); 308 } 309 break; 310 case TIFF_DOUBLE: 311 if (count == 1) { 312 double doublev; 313 CopyField(tag, doublev); 314 } else if (count == (uint16) -1) { 315 double* doubleav; 316 CopyField(tag, doubleav); 317 } 318 break; 319 default: 320 TIFFError(TIFFFileName(in), 321 "Data type %d is not supported, tag %d skipped.", 322 tag, type); 323 } 324} 325 326#undef CopyField4 327#undef CopyField3 328#undef CopyField2 329#undef CopyField 330 331static struct cpTag { 332 uint16 tag; 333 uint16 count; 334 TIFFDataType type; 335} tags[] = { 336 { TIFFTAG_IMAGEWIDTH, 1, TIFF_LONG }, 337 { TIFFTAG_IMAGELENGTH, 1, TIFF_LONG }, 338 { TIFFTAG_BITSPERSAMPLE, 1, TIFF_SHORT }, 339 { TIFFTAG_COMPRESSION, 1, TIFF_SHORT }, 340 { TIFFTAG_FILLORDER, 1, TIFF_SHORT }, 341 { TIFFTAG_ROWSPERSTRIP, 1, TIFF_LONG }, 342 { TIFFTAG_GROUP3OPTIONS, 1, TIFF_LONG }, 343 { TIFFTAG_SUBFILETYPE, 1, TIFF_LONG }, 344 { TIFFTAG_THRESHHOLDING, 1, TIFF_SHORT }, 345 { TIFFTAG_DOCUMENTNAME, 1, TIFF_ASCII }, 346 { TIFFTAG_IMAGEDESCRIPTION, 1, TIFF_ASCII }, 347 { TIFFTAG_MAKE, 1, TIFF_ASCII }, 348 { TIFFTAG_MODEL, 1, TIFF_ASCII }, 349 { TIFFTAG_ORIENTATION, 1, TIFF_SHORT }, 350 { TIFFTAG_MINSAMPLEVALUE, 1, TIFF_SHORT }, 351 { TIFFTAG_MAXSAMPLEVALUE, 1, TIFF_SHORT }, 352 { TIFFTAG_XRESOLUTION, 1, TIFF_RATIONAL }, 353 { TIFFTAG_YRESOLUTION, 1, TIFF_RATIONAL }, 354 { TIFFTAG_PAGENAME, 1, TIFF_ASCII }, 355 { TIFFTAG_XPOSITION, 1, TIFF_RATIONAL }, 356 { TIFFTAG_YPOSITION, 1, TIFF_RATIONAL }, 357 { TIFFTAG_GROUP4OPTIONS, 1, TIFF_LONG }, 358 { TIFFTAG_RESOLUTIONUNIT, 1, TIFF_SHORT }, 359 { TIFFTAG_PAGENUMBER, 2, TIFF_SHORT }, 360 { TIFFTAG_SOFTWARE, 1, TIFF_ASCII }, 361 { TIFFTAG_DATETIME, 1, TIFF_ASCII }, 362 { TIFFTAG_ARTIST, 1, TIFF_ASCII }, 363 { TIFFTAG_HOSTCOMPUTER, 1, TIFF_ASCII }, 364 { TIFFTAG_WHITEPOINT, 1, TIFF_RATIONAL }, 365 { TIFFTAG_PRIMARYCHROMATICITIES, (uint16) -1,TIFF_RATIONAL }, 366 { TIFFTAG_HALFTONEHINTS, 2, TIFF_SHORT }, 367 { TIFFTAG_BADFAXLINES, 1, TIFF_LONG }, 368 { TIFFTAG_CLEANFAXDATA, 1, TIFF_SHORT }, 369 { TIFFTAG_CONSECUTIVEBADFAXLINES, 1, TIFF_LONG }, 370 { TIFFTAG_INKSET, 1, TIFF_SHORT }, 371 { TIFFTAG_INKNAMES, 1, TIFF_ASCII }, 372 { TIFFTAG_DOTRANGE, 2, TIFF_SHORT }, 373 { TIFFTAG_TARGETPRINTER, 1, TIFF_ASCII }, 374 { TIFFTAG_SAMPLEFORMAT, 1, TIFF_SHORT }, 375 { TIFFTAG_YCBCRCOEFFICIENTS, (uint16) -1,TIFF_RATIONAL }, 376 { TIFFTAG_YCBCRSUBSAMPLING, 2, TIFF_SHORT }, 377 { TIFFTAG_YCBCRPOSITIONING, 1, TIFF_SHORT }, 378 { TIFFTAG_REFERENCEBLACKWHITE, (uint16) -1,TIFF_RATIONAL }, 379}; 380#define NTAGS (sizeof (tags) / sizeof (tags[0])) 381 382static void 383cpTags(TIFF* in, TIFF* out) 384{ 385 struct cpTag *p; 386 for (p = tags; p < &tags[NTAGS]; p++) 387 cpTag(in, out, p->tag, p->count, p->type); 388} 389#undef NTAGS 390 391char* stuff[] = { 392"usage: pal2rgb [options] input.tif output.tif", 393"where options are:", 394" -p contig pack samples contiguously (e.g. RGBRGB...)", 395" -p separate store samples separately (e.g. RRR...GGG...BBB...)", 396" -r # make each strip have no more than # rows", 397" -C 8 assume 8-bit colormap values (instead of 16-bit)", 398" -C 16 assume 16-bit colormap values", 399"", 400" -c lzw[:opts] compress output with Lempel-Ziv & Welch encoding", 401" -c zip[:opts] compress output with deflate encoding", 402" -c packbits compress output with packbits encoding", 403" -c none use no compression algorithm on output", 404"", 405"LZW and deflate options:", 406" # set predictor value", 407"For example, -c lzw:2 to get LZW-encoded data with horizontal differencing", 408NULL 409}; 410 411static void 412usage(void) 413{ 414 char buf[BUFSIZ]; 415 int i; 416 417 setbuf(stderr, buf); 418 fprintf(stderr, "%s\n\n", TIFFGetVersion()); 419 for (i = 0; stuff[i] != NULL; i++) 420 fprintf(stderr, "%s\n", stuff[i]); 421 exit(-1); 422} 423 424/* vim: set ts=8 sts=8 sw=8 noet: */ 425/* 426 * Local Variables: 427 * mode: c 428 * c-basic-offset: 8 429 * fill-column: 78 430 * End: 431 */ 432