4  * Copyright (c) 1994-1997 Sam Leffler 
   5  * Copyright (c) 1994-1997 Silicon Graphics, Inc. 
   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. 
  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.   
  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  
  27 #include "tif_config.h" 
  41 extern int getopt(int, char**, char*); 
  44 #define streq(a,b)      (strcmp(a,b) == 0) 
  47 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3) 
  60 static  uint32 tnw 
= 216;               /* thumbnail width */ 
  61 static  uint32 tnh 
= 274;               /* thumbnail height */ 
  62 static  Contrast contrast 
= LINEAR
;     /* current contrast */ 
  63 static  uint8
* thumbnail
; 
  65 static  int cpIFD(TIFF
*, TIFF
*); 
  66 static  int generateThumbnail(TIFF
*, TIFF
*); 
  67 static  void initScale(); 
  68 static  void usage(void); 
  74 main(int argc
, char* argv
[]) 
  80     while ((c 
= getopt(argc
, argv
, "w:h:c:")) != -1) { 
  82         case 'w':       tnw 
= strtoul(optarg
, NULL
, 0); break; 
  83         case 'h':       tnh 
= strtoul(optarg
, NULL
, 0); break; 
  84         case 'c':       contrast 
= streq(optarg
, "exp50") ? EXP50 
: 
  85                                    streq(optarg
, "exp60") ? EXP60 
: 
  86                                    streq(optarg
, "exp70") ? EXP70 
: 
  87                                    streq(optarg
, "exp80") ? EXP80 
: 
  88                                    streq(optarg
, "exp90") ? EXP90 
: 
  89                                    streq(optarg
, "exp")   ? EXP 
: 
  90                                    streq(optarg
, "linear")? LINEAR 
: 
  99     out 
= TIFFOpen(argv
[optind
+1], "w"); 
 102     in 
= TIFFOpen(argv
[optind
], "r"); 
 104     thumbnail 
= (uint8
*) _TIFFmalloc(tnw 
* tnh
); 
 106             TIFFError(TIFFFileName(in
), 
 107                       "Can't allocate space for thumbnail buffer."); 
 114             if (!generateThumbnail(in
, out
)) 
 116             if (!cpIFD(in
, out
) || !TIFFWriteDirectory(out
)) 
 118         } while (TIFFReadDirectory(in
)); 
 119         (void) TIFFClose(in
); 
 121     (void) TIFFClose(out
); 
 124     (void) TIFFClose(out
); 
 128 #define CopyField(tag, v) \ 
 129     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v) 
 130 #define CopyField2(tag, v1, v2) \ 
 131     if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2) 
 132 #define CopyField3(tag, v1, v2, v3) \ 
 133     if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3) 
 134 #define CopyField4(tag, v1, v2, v3, v4) \ 
 135     if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4) 
 138 cpTag(TIFF
* in
, TIFF
* out
, uint16 tag
, uint16 count
, TIFFDataType type
) 
 144                         CopyField(tag
, shortv
); 
 145                 } else if (count 
== 2) { 
 146                         uint16 shortv1
, shortv2
; 
 147                         CopyField2(tag
, shortv1
, shortv2
); 
 148                 } else if (count 
== 4) { 
 149                         uint16 
*tr
, *tg
, *tb
, *ta
; 
 150                         CopyField4(tag
, tr
, tg
, tb
, ta
); 
 151                 } else if (count 
== (uint16
) -1) { 
 154                         CopyField2(tag
, shortv1
, shortav
); 
 159                   CopyField(tag
, longv
); 
 165                         CopyField(tag
, floatv
); 
 166                 } else if (count 
== (uint16
) -1) { 
 168                         CopyField(tag
, floatav
); 
 173                   CopyField(tag
, stringv
); 
 179                         CopyField(tag
, doublev
); 
 180                 } else if (count 
== (uint16
) -1) { 
 182                         CopyField(tag
, doubleav
); 
 186                 TIFFError(TIFFFileName(in
), 
 187                           "Data type %d is not supported, tag %d skipped.", 
 197 static struct cpTag 
{ 
 202     { TIFFTAG_IMAGEWIDTH
,               1, TIFF_LONG 
}, 
 203     { TIFFTAG_IMAGELENGTH
,              1, TIFF_LONG 
}, 
 204     { TIFFTAG_BITSPERSAMPLE
,            1, TIFF_SHORT 
}, 
 205     { TIFFTAG_COMPRESSION
,              1, TIFF_SHORT 
}, 
 206     { TIFFTAG_FILLORDER
,                1, TIFF_SHORT 
}, 
 207     { TIFFTAG_SAMPLESPERPIXEL
,          1, TIFF_SHORT 
}, 
 208     { TIFFTAG_ROWSPERSTRIP
,             1, TIFF_LONG 
}, 
 209     { TIFFTAG_PLANARCONFIG
,             1, TIFF_SHORT 
}, 
 210     { TIFFTAG_GROUP3OPTIONS
,            1, TIFF_LONG 
}, 
 211     { TIFFTAG_SUBFILETYPE
,              1, TIFF_LONG 
}, 
 212     { TIFFTAG_PHOTOMETRIC
,              1, TIFF_SHORT 
}, 
 213     { TIFFTAG_THRESHHOLDING
,            1, TIFF_SHORT 
}, 
 214     { TIFFTAG_DOCUMENTNAME
,             1, TIFF_ASCII 
}, 
 215     { TIFFTAG_IMAGEDESCRIPTION
,         1, TIFF_ASCII 
}, 
 216     { TIFFTAG_MAKE
,                     1, TIFF_ASCII 
}, 
 217     { TIFFTAG_MODEL
,                    1, TIFF_ASCII 
}, 
 218     { TIFFTAG_ORIENTATION
,              1, TIFF_SHORT 
}, 
 219     { TIFFTAG_MINSAMPLEVALUE
,           1, TIFF_SHORT 
}, 
 220     { TIFFTAG_MAXSAMPLEVALUE
,           1, TIFF_SHORT 
}, 
 221     { TIFFTAG_XRESOLUTION
,              1, TIFF_RATIONAL 
}, 
 222     { TIFFTAG_YRESOLUTION
,              1, TIFF_RATIONAL 
}, 
 223     { TIFFTAG_PAGENAME
,                 1, TIFF_ASCII 
}, 
 224     { TIFFTAG_XPOSITION
,                1, TIFF_RATIONAL 
}, 
 225     { TIFFTAG_YPOSITION
,                1, TIFF_RATIONAL 
}, 
 226     { TIFFTAG_GROUP4OPTIONS
,            1, TIFF_LONG 
}, 
 227     { TIFFTAG_RESOLUTIONUNIT
,           1, TIFF_SHORT 
}, 
 228     { TIFFTAG_PAGENUMBER
,               2, TIFF_SHORT 
}, 
 229     { TIFFTAG_SOFTWARE
,                 1, TIFF_ASCII 
}, 
 230     { TIFFTAG_DATETIME
,                 1, TIFF_ASCII 
}, 
 231     { TIFFTAG_ARTIST
,                   1, TIFF_ASCII 
}, 
 232     { TIFFTAG_HOSTCOMPUTER
,             1, TIFF_ASCII 
}, 
 233     { TIFFTAG_WHITEPOINT
,               1, TIFF_RATIONAL 
}, 
 234     { TIFFTAG_PRIMARYCHROMATICITIES
,    (uint16
) -1,TIFF_RATIONAL 
}, 
 235     { TIFFTAG_HALFTONEHINTS
,            2, TIFF_SHORT 
}, 
 236     { TIFFTAG_BADFAXLINES
,              1, TIFF_LONG 
}, 
 237     { TIFFTAG_CLEANFAXDATA
,             1, TIFF_SHORT 
}, 
 238     { TIFFTAG_CONSECUTIVEBADFAXLINES
,   1, TIFF_LONG 
}, 
 239     { TIFFTAG_INKSET
,                   1, TIFF_SHORT 
}, 
 240     { TIFFTAG_INKNAMES
,                 1, TIFF_ASCII 
}, 
 241     { TIFFTAG_DOTRANGE
,                 2, TIFF_SHORT 
}, 
 242     { TIFFTAG_TARGETPRINTER
,            1, TIFF_ASCII 
}, 
 243     { TIFFTAG_SAMPLEFORMAT
,             1, TIFF_SHORT 
}, 
 244     { TIFFTAG_YCBCRCOEFFICIENTS
,        (uint16
) -1,TIFF_RATIONAL 
}, 
 245     { TIFFTAG_YCBCRSUBSAMPLING
,         2, TIFF_SHORT 
}, 
 246     { TIFFTAG_YCBCRPOSITIONING
,         1, TIFF_SHORT 
}, 
 247     { TIFFTAG_REFERENCEBLACKWHITE
,      (uint16
) -1,TIFF_RATIONAL 
}, 
 248     { TIFFTAG_EXTRASAMPLES
,             (uint16
) -1, TIFF_SHORT 
}, 
 250 #define NTAGS   (sizeof (tags) / sizeof (tags[0])) 
 253 cpTags(TIFF
* in
, TIFF
* out
) 
 256     for (p 
= tags
; p 
< &tags
[NTAGS
]; p
++) 
 257         cpTag(in
, out
, p
->tag
, p
->count
, p
->type
); 
 262 cpStrips(TIFF
* in
, TIFF
* out
) 
 264     tsize_t bufsize  
= TIFFStripSize(in
); 
 265     unsigned char *buf 
= (unsigned char *)_TIFFmalloc(bufsize
); 
 268         tstrip_t s
, ns 
= TIFFNumberOfStrips(in
); 
 271         TIFFGetField(in
, TIFFTAG_STRIPBYTECOUNTS
, &bytecounts
); 
 272         for (s 
= 0; s 
< ns
; s
++) { 
 273             if (bytecounts
[s
] > bufsize
) { 
 274                 buf 
= (unsigned char *)_TIFFrealloc(buf
, bytecounts
[s
]); 
 277                 bufsize 
= bytecounts
[s
]; 
 279             if (TIFFReadRawStrip(in
, s
, buf
, bytecounts
[s
]) < 0 || 
 280                 TIFFWriteRawStrip(out
, s
, buf
, bytecounts
[s
]) < 0) { 
 290         TIFFError(TIFFFileName(in
), 
 291                   "Can't allocate space for strip buffer."); 
 296 cpTiles(TIFF
* in
, TIFF
* out
) 
 298     tsize_t bufsize 
= TIFFTileSize(in
); 
 299     unsigned char *buf 
= (unsigned char *)_TIFFmalloc(bufsize
); 
 302         ttile_t t
, nt 
= TIFFNumberOfTiles(in
); 
 305         TIFFGetField(in
, TIFFTAG_TILEBYTECOUNTS
, &bytecounts
); 
 306         for (t 
= 0; t 
< nt
; t
++) { 
 307             if (bytecounts
[t
] > bufsize
) { 
 308                 buf 
= (unsigned char *)_TIFFrealloc(buf
, bytecounts
[t
]); 
 311                 bufsize 
= bytecounts
[t
]; 
 313             if (TIFFReadRawTile(in
, t
, buf
, bytecounts
[t
]) < 0 || 
 314                 TIFFWriteRawTile(out
, t
, buf
, bytecounts
[t
]) < 0) { 
 324     TIFFError(TIFFFileName(in
), 
 325                   "Can't allocate space for tile buffer."); 
 330 cpIFD(TIFF
* in
, TIFF
* out
) 
 333     if (TIFFIsTiled(in
)) { 
 334         if (!cpTiles(in
, out
)) 
 337         if (!cpStrips(in
, out
)) 
 343 static  uint16  photometric
;            /* current photometric of raster */ 
 344 static  uint16  filterWidth
;            /* filter width in pixels */ 
 345 static  uint32  stepSrcWidth
;           /* src image stepping width */ 
 346 static  uint32  stepDstWidth
;           /* dest stepping width */ 
 347 static  uint8
* src0
;                    /* horizontal bit stepping (start) */ 
 348 static  uint8
* src1
;                    /* horizontal bit stepping (middle) */ 
 349 static  uint8
* src2
;                    /* horizontal bit stepping (end) */ 
 350 static  uint32
* rowoff
;                 /* row offset for stepping */ 
 351 static  uint8 cmap
[256];                /* colormap indexes */ 
 352 static  uint8 bits
[256];                /* count of bits set */ 
 358     for (i 
= 0; i 
< 256; i
++) { 
 372 static int clamp(float v
, int low
, int high
) 
 373     { return (v 
< low 
? low 
: v 
> high 
? high 
: (int)v
); } 
 376 #define M_E             2.7182818284590452354 
 380 expFill(float pct
[], uint32 p
, uint32 n
) 
 383     uint32 c 
= (p 
* n
) / 100; 
 384     for (i 
= 1; i 
< c
; i
++) 
 385         pct
[i
] = (float) (1-exp(i
/((double)(n
-1)))/ M_E
); 
 393     float pct
[256];                     /* known to be large enough */ 
 395     pct
[0] = 1;                         /* force white */ 
 397     case EXP50
: expFill(pct
, 50, 256); break; 
 398     case EXP60
: expFill(pct
, 60, 256); break; 
 399     case EXP70
: expFill(pct
, 70, 256); break; 
 400     case EXP80
: expFill(pct
, 80, 256); break; 
 401     case EXP90
: expFill(pct
, 90, 256); break; 
 402     case EXP
:   expFill(pct
, 100, 256); break; 
 404         for (i 
= 1; i 
< 256; i
++) 
 405             pct
[i
] = 1-((float)i
)/(256-1); 
 408     switch (photometric
) { 
 409     case PHOTOMETRIC_MINISWHITE
: 
 410         for (i 
= 0; i 
< 256; i
++) 
 411             cmap
[i
] = clamp(255*pct
[(256-1)-i
], 0, 255); 
 413     case PHOTOMETRIC_MINISBLACK
: 
 414         for (i 
= 0; i 
< 256; i
++) 
 415             cmap
[i
] = clamp(255*pct
[i
], 0, 255); 
 423     src0 
= (uint8
*) _TIFFmalloc(sizeof (uint8
) * tnw
); 
 424     src1 
= (uint8
*) _TIFFmalloc(sizeof (uint8
) * tnw
); 
 425     src2 
= (uint8
*) _TIFFmalloc(sizeof (uint8
) * tnw
); 
 426     rowoff 
= (uint32
*) _TIFFmalloc(sizeof (uint32
) * tnw
); 
 428     stepDstWidth 
= stepSrcWidth 
= 0; 
 433  * Calculate the horizontal accumulation parameteres 
 434  * according to the widths of the src and dst images. 
 437 setupStepTables(uint32 sw
) 
 439     if (stepSrcWidth 
!= sw 
|| stepDstWidth 
!= tnw
) { 
 447         for (x 
= 0; x 
< tnw
; x
++) { 
 450             while (err 
>= limit
) { 
 454             rowoff
[x
] = sx0 
>> 3; 
 455             fw 
= sx 
- sx0
;              /* width */ 
 456             b 
= (fw 
< 8) ? 0xff<<(8-fw
) : 0xff; 
 457             src0
[x
] = b 
>> (sx0
&7); 
 463             src2
[x
] = 0xff << (8-fw
); 
 471 setrow(uint8
* row
, uint32 nrows
, const uint8
* rows
[]) 
 474     uint32 area 
= nrows 
* filterWidth
; 
 475     for (x 
= 0; x 
< tnw
; x
++) { 
 476         uint32 mask0 
= src0
[x
]; 
 478         uint32 mask1 
= src1
[x
]; 
 479         uint32 off 
= rowoff
[x
]; 
 482         for (y 
= 0; y 
< nrows
; y
++) { 
 483             const uint8
* src 
= rows
[y
] + off
; 
 484             acc 
+= bits
[*src
++ & mask0
]; 
 487                 for (i 
= fw
; i 
> 8; i
--) 
 490             case 8: acc 
+= bits
[*src
++]; 
 491             case 7: acc 
+= bits
[*src
++]; 
 492             case 6: acc 
+= bits
[*src
++]; 
 493             case 5: acc 
+= bits
[*src
++]; 
 494             case 4: acc 
+= bits
[*src
++]; 
 495             case 3: acc 
+= bits
[*src
++]; 
 496             case 2: acc 
+= bits
[*src
++]; 
 497             case 1: acc 
+= bits
[*src
++]; 
 500             acc 
+= bits
[*src 
& mask1
]; 
 502         *row
++ = cmap
[(255*acc
)/area
]; 
 507  * Install the specified image.  The 
 508  * image is resized to fit the display page using 
 509  * a box filter.  The resultant pixels are mapped 
 510  * with a user-selectable contrast curve. 
 513 setImage1(const uint8
* br
, uint32 rw
, uint32 rh
) 
 518     int bpr 
= TIFFhowmany8(rw
); 
 520     uint8
* row 
= thumbnail
; 
 522     for (dy 
= 0; dy 
< tnh
; dy
++) { 
 523         const uint8
* rows
[256]; 
 525         fprintf(stderr
, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr
, sy
, bpr
*sy
); 
 526         rows
[0] = br 
+ bpr
*sy
; 
 528         while (err 
>= limit
) { 
 532                 rows
[nrows
++] = br 
+ bpr
*sy
; 
 534         setrow(row
, nrows
, rows
); 
 540 setImage(const uint8
* br
, uint32 rw
, uint32 rh
) 
 542     filterWidth 
= (uint16
) ceil((double) rw 
/ (double) tnw
); 
 544     setImage1(br
, rw
, rh
); 
 548 generateThumbnail(TIFF
* in
, TIFF
* out
) 
 550     unsigned char* raster
; 
 554     tsize_t rowsize
, rastersize
; 
 555     tstrip_t s
, ns 
= TIFFNumberOfStrips(in
); 
 558     TIFFGetField(in
, TIFFTAG_IMAGEWIDTH
, &sw
); 
 559     TIFFGetField(in
, TIFFTAG_IMAGELENGTH
, &sh
); 
 560     TIFFGetFieldDefaulted(in
, TIFFTAG_BITSPERSAMPLE
, &bps
); 
 561     TIFFGetFieldDefaulted(in
, TIFFTAG_SAMPLESPERPIXEL
, &spp
); 
 562     TIFFGetFieldDefaulted(in
, TIFFTAG_ROWSPERSTRIP
, &rps
); 
 563     if (spp 
!= 1 || bps 
!= 1) 
 565     rowsize 
= TIFFScanlineSize(in
); 
 566     rastersize 
= sh 
* rowsize
; 
 567     fprintf(stderr
, "rastersize=%u\n", (unsigned int)rastersize
); 
 568     raster 
= (unsigned char*)_TIFFmalloc(rastersize
); 
 570             TIFFError(TIFFFileName(in
), 
 571                       "Can't allocate space for raster buffer."); 
 575     for (s 
= 0; s 
< ns
; s
++) { 
 576         (void) TIFFReadEncodedStrip(in
, s
, rp
, -1); 
 579     TIFFGetField(in
, TIFFTAG_PHOTOMETRIC
, &photometric
); 
 581     setImage(raster
, sw
, sh
); 
 584     TIFFSetField(out
, TIFFTAG_SUBFILETYPE
, FILETYPE_REDUCEDIMAGE
); 
 585     TIFFSetField(out
, TIFFTAG_IMAGEWIDTH
, (uint32
) tnw
); 
 586     TIFFSetField(out
, TIFFTAG_IMAGELENGTH
, (uint32
) tnh
); 
 587     TIFFSetField(out
, TIFFTAG_BITSPERSAMPLE
, (uint16
) 8); 
 588     TIFFSetField(out
, TIFFTAG_SAMPLESPERPIXEL
, (uint16
) 1); 
 589     TIFFSetField(out
, TIFFTAG_COMPRESSION
, COMPRESSION_PACKBITS
); 
 590     TIFFSetField(out
, TIFFTAG_PHOTOMETRIC
, PHOTOMETRIC_MINISWHITE
); 
 591     TIFFSetField(out
, TIFFTAG_PLANARCONFIG
, PLANARCONFIG_CONTIG
); 
 592     TIFFSetField(out
, TIFFTAG_ORIENTATION
, ORIENTATION_TOPLEFT
); 
 593     cpTag(in
, out
, TIFFTAG_SOFTWARE
,            (uint16
) -1, TIFF_ASCII
); 
 594     cpTag(in
, out
, TIFFTAG_IMAGEDESCRIPTION
,    (uint16
) -1, TIFF_ASCII
); 
 595     cpTag(in
, out
, TIFFTAG_DATETIME
,            (uint16
) -1, TIFF_ASCII
); 
 596     cpTag(in
, out
, TIFFTAG_HOSTCOMPUTER
,        (uint16
) -1, TIFF_ASCII
); 
 598     TIFFSetField(out
, TIFFTAG_SUBIFD
, 1, diroff
); 
 599     return (TIFFWriteEncodedStrip(out
, 0, thumbnail
, tnw
*tnh
) != -1 && 
 600             TIFFWriteDirectory(out
) != -1); 
 604 "usage: thumbnail [options] input.tif output.tif", 
 605 "where options are:", 
 606 " -h #          specify thumbnail image height (default is 274)", 
 607 " -w #          specify thumbnail image width (default is 216)", 
 609 " -c linear     use linear contrast curve", 
 610 " -c exp50      use 50% exponential contrast curve", 
 611 " -c exp60      use 60% exponential contrast curve", 
 612 " -c exp70      use 70% exponential contrast curve", 
 613 " -c exp80      use 80% exponential contrast curve", 
 614 " -c exp90      use 90% exponential contrast curve", 
 615 " -c exp                use pure exponential contrast curve", 
 626         fprintf(stderr
, "%s\n\n", TIFFGetVersion()); 
 627         for (i 
= 0; stuff
[i
] != NULL
; i
++) 
 628                 fprintf(stderr
, "%s\n", stuff
[i
]); 
 632 /* vim: set ts=8 sts=8 sw=8 noet: */