1 /* $Id: thumbnail.c,v 1.15 2010-03-10 18:56:49 bfriesen Exp $ */ 2 3 /* 4 * Copyright (c) 1994-1997 Sam Leffler 5 * Copyright (c) 1994-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 <math.h> 33 34 #ifdef HAVE_UNISTD_H 35 # include <unistd.h> 36 #endif 37 38 #ifdef NEED_LIBPORT 39 # include "libport.h" 40 #endif 41 42 #include "tiffio.h" 43 44 #ifndef HAVE_GETOPT 45 extern int getopt(int, char**, char*); 46 #endif 47 48 #define streq(a,b) (strcmp(a,b) == 0) 49 50 #ifndef TIFFhowmany8 51 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3) 52 #endif 53 54 typedef enum { 55 EXP50, 56 EXP60, 57 EXP70, 58 EXP80, 59 EXP90, 60 EXP, 61 LINEAR 62 } Contrast; 63 64 static uint32 tnw = 216; /* thumbnail width */ 65 static uint32 tnh = 274; /* thumbnail height */ 66 static Contrast contrast = LINEAR; /* current contrast */ 67 static uint8* thumbnail; 68 69 static int cpIFD(TIFF*, TIFF*); 70 static int generateThumbnail(TIFF*, TIFF*); 71 static void initScale(); 72 static void usage(void); 73 74 extern char* optarg; 75 extern int optind; 76 77 int 78 main(int argc, char* argv[]) 79 { 80 TIFF* in; 81 TIFF* out; 82 int c; 83 84 while ((c = getopt(argc, argv, "w:h:c:")) != -1) { 85 switch (c) { 86 case 'w': tnw = strtoul(optarg, NULL, 0); break; 87 case 'h': tnh = strtoul(optarg, NULL, 0); break; 88 case 'c': contrast = streq(optarg, "exp50") ? EXP50 : 89 streq(optarg, "exp60") ? EXP60 : 90 streq(optarg, "exp70") ? EXP70 : 91 streq(optarg, "exp80") ? EXP80 : 92 streq(optarg, "exp90") ? EXP90 : 93 streq(optarg, "exp") ? EXP : 94 streq(optarg, "linear")? LINEAR : 95 EXP; 96 break; 97 default: usage(); 98 } 99 } 100 if (argc-optind != 2) 101 usage(); 102 103 out = TIFFOpen(argv[optind+1], "w"); 104 if (out == NULL) 105 return 2; 106 in = TIFFOpen(argv[optind], "r"); 107 if( in == NULL ) 108 return 2; 109 110 thumbnail = (uint8*) _TIFFmalloc(tnw * tnh); 111 if (!thumbnail) { 112 TIFFError(TIFFFileName(in), 113 "Can't allocate space for thumbnail buffer."); 114 return 1; 115 } 116 117 if (in != NULL) { 118 initScale(); 119 do { 120 if (!generateThumbnail(in, out)) 121 goto bad; 122 if (!cpIFD(in, out) || !TIFFWriteDirectory(out)) 123 goto bad; 124 } while (TIFFReadDirectory(in)); 125 (void) TIFFClose(in); 126 } 127 (void) TIFFClose(out); 128 return 0; 129 bad: 130 (void) TIFFClose(out); 131 return 1; 132 } 133 134 #define CopyField(tag, v) \ 135 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) 136 #define CopyField2(tag, v1, v2) \ 137 if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2) 138 #define CopyField3(tag, v1, v2, v3) \ 139 if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3) 140 #define CopyField4(tag, v1, v2, v3, v4) \ 141 if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4) 142 143 static void 144 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type) 145 { 146 switch (type) { 147 case TIFF_SHORT: 148 if (count == 1) { 149 uint16 shortv; 150 CopyField(tag, shortv); 151 } else if (count == 2) { 152 uint16 shortv1, shortv2; 153 CopyField2(tag, shortv1, shortv2); 154 } else if (count == 4) { 155 uint16 *tr, *tg, *tb, *ta; 156 CopyField4(tag, tr, tg, tb, ta); 157 } else if (count == (uint16) -1) { 158 uint16 shortv1; 159 uint16* shortav; 160 CopyField2(tag, shortv1, shortav); 161 } 162 break; 163 case TIFF_LONG: 164 { uint32 longv; 165 CopyField(tag, longv); 166 } 167 break; 168 case TIFF_LONG8: 169 { uint64 longv8; 170 CopyField(tag, longv8); 171 } 172 break; 173 case TIFF_SLONG8: 174 { int64 longv8; 175 CopyField(tag, longv8); 176 } 177 break; 178 case TIFF_RATIONAL: 179 if (count == 1) { 180 float floatv; 181 CopyField(tag, floatv); 182 } else if (count == (uint16) -1) { 183 float* floatav; 184 CopyField(tag, floatav); 185 } 186 break; 187 case TIFF_ASCII: 188 { char* stringv; 189 CopyField(tag, stringv); 190 } 191 break; 192 case TIFF_DOUBLE: 193 if (count == 1) { 194 double doublev; 195 CopyField(tag, doublev); 196 } else if (count == (uint16) -1) { 197 double* doubleav; 198 CopyField(tag, doubleav); 199 } 200 break; 201 case TIFF_IFD8: 202 { toff_t ifd8; 203 CopyField(tag, ifd8); 204 } 205 break; default: 206 TIFFError(TIFFFileName(in), 207 "Data type %d is not supported, tag %d skipped.", 208 tag, type); 209 } 210 } 211 212 #undef CopyField4 213 #undef CopyField3 214 #undef CopyField2 215 #undef CopyField 216 217 static struct cpTag { 218 uint16 tag; 219 uint16 count; 220 TIFFDataType type; 221 } tags[] = { 222 { TIFFTAG_IMAGEWIDTH, 1, TIFF_LONG }, 223 { TIFFTAG_IMAGELENGTH, 1, TIFF_LONG }, 224 { TIFFTAG_BITSPERSAMPLE, 1, TIFF_SHORT }, 225 { TIFFTAG_COMPRESSION, 1, TIFF_SHORT }, 226 { TIFFTAG_FILLORDER, 1, TIFF_SHORT }, 227 { TIFFTAG_SAMPLESPERPIXEL, 1, TIFF_SHORT }, 228 { TIFFTAG_ROWSPERSTRIP, 1, TIFF_LONG }, 229 { TIFFTAG_PLANARCONFIG, 1, TIFF_SHORT }, 230 { TIFFTAG_GROUP3OPTIONS, 1, TIFF_LONG }, 231 { TIFFTAG_SUBFILETYPE, 1, TIFF_LONG }, 232 { TIFFTAG_PHOTOMETRIC, 1, TIFF_SHORT }, 233 { TIFFTAG_THRESHHOLDING, 1, TIFF_SHORT }, 234 { TIFFTAG_DOCUMENTNAME, 1, TIFF_ASCII }, 235 { TIFFTAG_IMAGEDESCRIPTION, 1, TIFF_ASCII }, 236 { TIFFTAG_MAKE, 1, TIFF_ASCII }, 237 { TIFFTAG_MODEL, 1, TIFF_ASCII }, 238 { TIFFTAG_ORIENTATION, 1, TIFF_SHORT }, 239 { TIFFTAG_MINSAMPLEVALUE, 1, TIFF_SHORT }, 240 { TIFFTAG_MAXSAMPLEVALUE, 1, TIFF_SHORT }, 241 { TIFFTAG_XRESOLUTION, 1, TIFF_RATIONAL }, 242 { TIFFTAG_YRESOLUTION, 1, TIFF_RATIONAL }, 243 { TIFFTAG_PAGENAME, 1, TIFF_ASCII }, 244 { TIFFTAG_XPOSITION, 1, TIFF_RATIONAL }, 245 { TIFFTAG_YPOSITION, 1, TIFF_RATIONAL }, 246 { TIFFTAG_GROUP4OPTIONS, 1, TIFF_LONG }, 247 { TIFFTAG_RESOLUTIONUNIT, 1, TIFF_SHORT }, 248 { TIFFTAG_PAGENUMBER, 2, TIFF_SHORT }, 249 { TIFFTAG_SOFTWARE, 1, TIFF_ASCII }, 250 { TIFFTAG_DATETIME, 1, TIFF_ASCII }, 251 { TIFFTAG_ARTIST, 1, TIFF_ASCII }, 252 { TIFFTAG_HOSTCOMPUTER, 1, TIFF_ASCII }, 253 { TIFFTAG_WHITEPOINT, 1, TIFF_RATIONAL }, 254 { TIFFTAG_PRIMARYCHROMATICITIES, (uint16) -1,TIFF_RATIONAL }, 255 { TIFFTAG_HALFTONEHINTS, 2, TIFF_SHORT }, 256 { TIFFTAG_BADFAXLINES, 1, TIFF_LONG }, 257 { TIFFTAG_CLEANFAXDATA, 1, TIFF_SHORT }, 258 { TIFFTAG_CONSECUTIVEBADFAXLINES, 1, TIFF_LONG }, 259 { TIFFTAG_INKSET, 1, TIFF_SHORT }, 260 { TIFFTAG_INKNAMES, 1, TIFF_ASCII }, 261 { TIFFTAG_DOTRANGE, 2, TIFF_SHORT }, 262 { TIFFTAG_TARGETPRINTER, 1, TIFF_ASCII }, 263 { TIFFTAG_SAMPLEFORMAT, 1, TIFF_SHORT }, 264 { TIFFTAG_YCBCRCOEFFICIENTS, (uint16) -1,TIFF_RATIONAL }, 265 { TIFFTAG_YCBCRSUBSAMPLING, 2, TIFF_SHORT }, 266 { TIFFTAG_YCBCRPOSITIONING, 1, TIFF_SHORT }, 267 { TIFFTAG_REFERENCEBLACKWHITE, (uint16) -1,TIFF_RATIONAL }, 268 { TIFFTAG_EXTRASAMPLES, (uint16) -1, TIFF_SHORT }, 269 }; 270 #define NTAGS (sizeof (tags) / sizeof (tags[0])) 271 272 static void 273 cpTags(TIFF* in, TIFF* out) 274 { 275 struct cpTag *p; 276 for (p = tags; p < &tags[NTAGS]; p++) 277 cpTag(in, out, p->tag, p->count, p->type); 278 } 279 #undef NTAGS 280 281 static int 282 cpStrips(TIFF* in, TIFF* out) 283 { 284 tsize_t bufsize = TIFFStripSize(in); 285 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize); 286 287 if (buf) { 288 tstrip_t s, ns = TIFFNumberOfStrips(in); 289 uint64 *bytecounts; 290 291 TIFFGetField(in, TIFFTAG_STRIPBYTECOUNTS, &bytecounts); 292 for (s = 0; s < ns; s++) { 293 if (bytecounts[s] > (uint64) bufsize) { 294 buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[s]); 295 if (!buf) 296 goto bad; 297 bufsize = (tmsize_t)bytecounts[s]; 298 } 299 if (TIFFReadRawStrip(in, s, buf, (tmsize_t)bytecounts[s]) < 0 || 300 TIFFWriteRawStrip(out, s, buf, (tmsize_t)bytecounts[s]) < 0) { 301 _TIFFfree(buf); 302 return 0; 303 } 304 } 305 _TIFFfree(buf); 306 return 1; 307 } 308 309 bad: 310 TIFFError(TIFFFileName(in), 311 "Can't allocate space for strip buffer."); 312 return 0; 313 } 314 315 static int 316 cpTiles(TIFF* in, TIFF* out) 317 { 318 tsize_t bufsize = TIFFTileSize(in); 319 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize); 320 321 if (buf) { 322 ttile_t t, nt = TIFFNumberOfTiles(in); 323 uint64 *bytecounts; 324 325 TIFFGetField(in, TIFFTAG_TILEBYTECOUNTS, &bytecounts); 326 for (t = 0; t < nt; t++) { 327 if (bytecounts[t] > (uint64) bufsize) { 328 buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[t]); 329 if (!buf) 330 goto bad; 331 bufsize = (tmsize_t)bytecounts[t]; 332 } 333 if (TIFFReadRawTile(in, t, buf, (tmsize_t)bytecounts[t]) < 0 || 334 TIFFWriteRawTile(out, t, buf, (tmsize_t)bytecounts[t]) < 0) { 335 _TIFFfree(buf); 336 return 0; 337 } 338 } 339 _TIFFfree(buf); 340 return 1; 341 } 342 343 bad: 344 TIFFError(TIFFFileName(in), 345 "Can't allocate space for tile buffer."); 346 return (0); 347 } 348 349 static int 350 cpIFD(TIFF* in, TIFF* out) 351 { 352 cpTags(in, out); 353 if (TIFFIsTiled(in)) { 354 if (!cpTiles(in, out)) 355 return (0); 356 } else { 357 if (!cpStrips(in, out)) 358 return (0); 359 } 360 return (1); 361 } 362 363 static uint16 photometric; /* current photometric of raster */ 364 static uint16 filterWidth; /* filter width in pixels */ 365 static uint32 stepSrcWidth; /* src image stepping width */ 366 static uint32 stepDstWidth; /* dest stepping width */ 367 static uint8* src0; /* horizontal bit stepping (start) */ 368 static uint8* src1; /* horizontal bit stepping (middle) */ 369 static uint8* src2; /* horizontal bit stepping (end) */ 370 static uint32* rowoff; /* row offset for stepping */ 371 static uint8 cmap[256]; /* colormap indexes */ 372 static uint8 bits[256]; /* count of bits set */ 373 374 static void 375 setupBitsTables() 376 { 377 int i; 378 for (i = 0; i < 256; i++) { 379 int n = 0; 380 if (i&0x01) n++; 381 if (i&0x02) n++; 382 if (i&0x04) n++; 383 if (i&0x08) n++; 384 if (i&0x10) n++; 385 if (i&0x20) n++; 386 if (i&0x40) n++; 387 if (i&0x80) n++; 388 bits[i] = n; 389 } 390 } 391 392 static int clamp(float v, int low, int high) 393 { return (v < low ? low : v > high ? high : (int)v); } 394 395 #ifndef M_E 396 #define M_E 2.7182818284590452354 397 #endif 398 399 static void 400 expFill(float pct[], uint32 p, uint32 n) 401 { 402 uint32 i; 403 uint32 c = (p * n) / 100; 404 for (i = 1; i < c; i++) 405 pct[i] = (float) (1-exp(i/((double)(n-1)))/ M_E); 406 for (; i < n; i++) 407 pct[i] = 0.; 408 } 409 410 static void 411 setupCmap() 412 { 413 float pct[256]; /* known to be large enough */ 414 uint32 i; 415 pct[0] = 1; /* force white */ 416 switch (contrast) { 417 case EXP50: expFill(pct, 50, 256); break; 418 case EXP60: expFill(pct, 60, 256); break; 419 case EXP70: expFill(pct, 70, 256); break; 420 case EXP80: expFill(pct, 80, 256); break; 421 case EXP90: expFill(pct, 90, 256); break; 422 case EXP: expFill(pct, 100, 256); break; 423 case LINEAR: 424 for (i = 1; i < 256; i++) 425 pct[i] = 1-((float)i)/(256-1); 426 break; 427 } 428 switch (photometric) { 429 case PHOTOMETRIC_MINISWHITE: 430 for (i = 0; i < 256; i++) 431 cmap[i] = clamp(255*pct[(256-1)-i], 0, 255); 432 break; 433 case PHOTOMETRIC_MINISBLACK: 434 for (i = 0; i < 256; i++) 435 cmap[i] = clamp(255*pct[i], 0, 255); 436 break; 437 } 438 } 439 440 static void 441 initScale() 442 { 443 src0 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw); 444 src1 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw); 445 src2 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw); 446 rowoff = (uint32*) _TIFFmalloc(sizeof (uint32) * tnw); 447 filterWidth = 0; 448 stepDstWidth = stepSrcWidth = 0; 449 setupBitsTables(); 450 } 451 452 /* 453 * Calculate the horizontal accumulation parameteres 454 * according to the widths of the src and dst images. 455 */ 456 static void 457 setupStepTables(uint32 sw) 458 { 459 if (stepSrcWidth != sw || stepDstWidth != tnw) { 460 int step = sw; 461 int limit = tnw; 462 int err = 0; 463 uint32 sx = 0; 464 uint32 x; 465 int fw; 466 uint8 b; 467 for (x = 0; x < tnw; x++) { 468 uint32 sx0 = sx; 469 err += step; 470 while (err >= limit) { 471 err -= limit; 472 sx++; 473 } 474 rowoff[x] = sx0 >> 3; 475 fw = sx - sx0; /* width */ 476 b = (fw < 8) ? 0xff<<(8-fw) : 0xff; 477 src0[x] = b >> (sx0&7); 478 fw -= 8 - (sx0&7); 479 if (fw < 0) 480 fw = 0; 481 src1[x] = fw >> 3; 482 fw -= (fw>>3)<<3; 483 src2[x] = 0xff << (8-fw); 484 } 485 stepSrcWidth = sw; 486 stepDstWidth = tnw; 487 } 488 } 489 490 static void 491 setrow(uint8* row, uint32 nrows, const uint8* rows[]) 492 { 493 uint32 x; 494 uint32 area = nrows * filterWidth; 495 for (x = 0; x < tnw; x++) { 496 uint32 mask0 = src0[x]; 497 uint32 fw = src1[x]; 498 uint32 mask1 = src1[x]; 499 uint32 off = rowoff[x]; 500 uint32 acc = 0; 501 uint32 y, i; 502 for (y = 0; y < nrows; y++) { 503 const uint8* src = rows[y] + off; 504 acc += bits[*src++ & mask0]; 505 switch (fw) { 506 default: 507 for (i = fw; i > 8; i--) 508 acc += bits[*src++]; 509 /* fall thru... */ 510 case 8: acc += bits[*src++]; 511 case 7: acc += bits[*src++]; 512 case 6: acc += bits[*src++]; 513 case 5: acc += bits[*src++]; 514 case 4: acc += bits[*src++]; 515 case 3: acc += bits[*src++]; 516 case 2: acc += bits[*src++]; 517 case 1: acc += bits[*src++]; 518 case 0: break; 519 } 520 acc += bits[*src & mask1]; 521 } 522 *row++ = cmap[(255*acc)/area]; 523 } 524 } 525 526 /* 527 * Install the specified image. The 528 * image is resized to fit the display page using 529 * a box filter. The resultant pixels are mapped 530 * with a user-selectable contrast curve. 531 */ 532 static void 533 setImage1(const uint8* br, uint32 rw, uint32 rh) 534 { 535 int step = rh; 536 int limit = tnh; 537 int err = 0; 538 int bpr = TIFFhowmany8(rw); 539 int sy = 0; 540 uint8* row = thumbnail; 541 uint32 dy; 542 for (dy = 0; dy < tnh; dy++) { 543 const uint8* rows[256]; 544 uint32 nrows = 1; 545 fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy); 546 rows[0] = br + bpr*sy; 547 err += step; 548 while (err >= limit) { 549 err -= limit; 550 sy++; 551 if (err >= limit) 552 rows[nrows++] = br + bpr*sy; 553 } 554 setrow(row, nrows, rows); 555 row += tnw; 556 } 557 } 558 559 static void 560 setImage(const uint8* br, uint32 rw, uint32 rh) 561 { 562 filterWidth = (uint16) ceil((double) rw / (double) tnw); 563 setupStepTables(rw); 564 setImage1(br, rw, rh); 565 } 566 567 static int 568 generateThumbnail(TIFF* in, TIFF* out) 569 { 570 unsigned char* raster; 571 unsigned char* rp; 572 uint32 sw, sh, rps; 573 uint16 bps, spp; 574 tsize_t rowsize, rastersize; 575 tstrip_t s, ns = TIFFNumberOfStrips(in); 576 toff_t diroff[1]; 577 578 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &sw); 579 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &sh); 580 TIFFGetFieldDefaulted(in, TIFFTAG_BITSPERSAMPLE, &bps); 581 TIFFGetFieldDefaulted(in, TIFFTAG_SAMPLESPERPIXEL, &spp); 582 TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &rps); 583 if (spp != 1 || bps != 1) 584 return 0; 585 rowsize = TIFFScanlineSize(in); 586 rastersize = sh * rowsize; 587 fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize); 588 raster = (unsigned char*)_TIFFmalloc(rastersize); 589 if (!raster) { 590 TIFFError(TIFFFileName(in), 591 "Can't allocate space for raster buffer."); 592 return 0; 593 } 594 rp = raster; 595 for (s = 0; s < ns; s++) { 596 (void) TIFFReadEncodedStrip(in, s, rp, -1); 597 rp += rps * rowsize; 598 } 599 TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric); 600 setupCmap(); 601 setImage(raster, sw, sh); 602 _TIFFfree(raster); 603 604 TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE); 605 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) tnw); 606 TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) tnh); 607 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (uint16) 8); 608 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (uint16) 1); 609 TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS); 610 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE); 611 TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 612 TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); 613 cpTag(in, out, TIFFTAG_SOFTWARE, (uint16) -1, TIFF_ASCII); 614 cpTag(in, out, TIFFTAG_IMAGEDESCRIPTION, (uint16) -1, TIFF_ASCII); 615 cpTag(in, out, TIFFTAG_DATETIME, (uint16) -1, TIFF_ASCII); 616 cpTag(in, out, TIFFTAG_HOSTCOMPUTER, (uint16) -1, TIFF_ASCII); 617 diroff[0] = 0UL; 618 TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff); 619 return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 && 620 TIFFWriteDirectory(out) != -1); 621 } 622 623 char* stuff[] = { 624 "usage: thumbnail [options] input.tif output.tif", 625 "where options are:", 626 " -h # specify thumbnail image height (default is 274)", 627 " -w # specify thumbnail image width (default is 216)", 628 "", 629 " -c linear use linear contrast curve", 630 " -c exp50 use 50% exponential contrast curve", 631 " -c exp60 use 60% exponential contrast curve", 632 " -c exp70 use 70% exponential contrast curve", 633 " -c exp80 use 80% exponential contrast curve", 634 " -c exp90 use 90% exponential contrast curve", 635 " -c exp use pure exponential contrast curve", 636 NULL 637 }; 638 639 static void 640 usage(void) 641 { 642 char buf[BUFSIZ]; 643 int i; 644 645 setbuf(stderr, buf); 646 fprintf(stderr, "%s\n\n", TIFFGetVersion()); 647 for (i = 0; stuff[i] != NULL; i++) 648 fprintf(stderr, "%s\n", stuff[i]); 649 exit(-1); 650 } 651 652 /* vim: set ts=8 sts=8 sw=8 noet: */ 653 /* 654 * Local Variables: 655 * mode: c 656 * c-basic-offset: 8 657 * fill-column: 78 658 * End: 659 */ 660