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