home *** CD-ROM | disk | FTP | other *** search
/ Practical Algorithms for Image Analysis / Practical Algorithms for Image Analysis.iso / TARFILE.GZ / tarfile / libtiff / tools / thumbnail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-09-11  |  15.4 KB  |  573 lines

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