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"
45 extern int getopt(int, char**, char*);
48 #define streq(a,b) (strcmp(a,b) == 0)
51 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
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
;
69 static int cpIFD(TIFF
*, TIFF
*);
70 static int generateThumbnail(TIFF
*, TIFF
*);
71 static void initScale();
72 static void usage(void);
78 main(int argc
, char* argv
[])
84 while ((c
= getopt(argc
, argv
, "w:h:c:")) != -1) {
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
:
100 if (argc
-optind
!= 2)
103 out
= TIFFOpen(argv
[optind
+1], "w");
106 in
= TIFFOpen(argv
[optind
], "r");
110 thumbnail
= (uint8
*) _TIFFmalloc(tnw
* tnh
);
112 TIFFError(TIFFFileName(in
),
113 "Can't allocate space for thumbnail buffer.");
120 if (!generateThumbnail(in
, out
))
122 if (!cpIFD(in
, out
) || !TIFFWriteDirectory(out
))
124 } while (TIFFReadDirectory(in
));
125 (void) TIFFClose(in
);
127 (void) TIFFClose(out
);
130 (void) TIFFClose(out
);
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)
144 cpTag(TIFF
* in
, TIFF
* out
, uint16 tag
, uint16 count
, TIFFDataType type
)
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) {
160 CopyField2(tag
, shortv1
, shortav
);
165 CopyField(tag
, longv
);
170 CopyField(tag
, longv8
);
175 CopyField(tag
, longv8
);
181 CopyField(tag
, floatv
);
182 } else if (count
== (uint16
) -1) {
184 CopyField(tag
, floatav
);
189 CopyField(tag
, stringv
);
195 CopyField(tag
, doublev
);
196 } else if (count
== (uint16
) -1) {
198 CopyField(tag
, doubleav
);
203 CopyField(tag
, ifd8
);
206 TIFFError(TIFFFileName(in
),
207 "Data type %d is not supported, tag %d skipped.",
217 static struct cpTag
{
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
, 2, 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
},
270 #define NTAGS (sizeof (tags) / sizeof (tags[0]))
273 cpTags(TIFF
* in
, TIFF
* out
)
276 for (p
= tags
; p
< &tags
[NTAGS
]; p
++)
277 cpTag(in
, out
, p
->tag
, p
->count
, p
->type
);
282 cpStrips(TIFF
* in
, TIFF
* out
)
284 tsize_t bufsize
= TIFFStripSize(in
);
285 unsigned char *buf
= (unsigned char *)_TIFFmalloc(bufsize
);
288 tstrip_t s
, ns
= TIFFNumberOfStrips(in
);
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
]);
297 bufsize
= (tmsize_t
)bytecounts
[s
];
299 if (TIFFReadRawStrip(in
, s
, buf
, (tmsize_t
)bytecounts
[s
]) < 0 ||
300 TIFFWriteRawStrip(out
, s
, buf
, (tmsize_t
)bytecounts
[s
]) < 0) {
310 TIFFError(TIFFFileName(in
),
311 "Can't allocate space for strip buffer.");
316 cpTiles(TIFF
* in
, TIFF
* out
)
318 tsize_t bufsize
= TIFFTileSize(in
);
319 unsigned char *buf
= (unsigned char *)_TIFFmalloc(bufsize
);
322 ttile_t t
, nt
= TIFFNumberOfTiles(in
);
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
]);
331 bufsize
= (tmsize_t
)bytecounts
[t
];
333 if (TIFFReadRawTile(in
, t
, buf
, (tmsize_t
)bytecounts
[t
]) < 0 ||
334 TIFFWriteRawTile(out
, t
, buf
, (tmsize_t
)bytecounts
[t
]) < 0) {
344 TIFFError(TIFFFileName(in
),
345 "Can't allocate space for tile buffer.");
350 cpIFD(TIFF
* in
, TIFF
* out
)
353 if (TIFFIsTiled(in
)) {
354 if (!cpTiles(in
, out
))
357 if (!cpStrips(in
, out
))
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 */
378 for (i
= 0; i
< 256; i
++) {
392 static int clamp(float v
, int low
, int high
)
393 { return (v
< low
? low
: v
> high
? high
: (int)v
); }
396 #define M_E 2.7182818284590452354
400 expFill(float pct
[], uint32 p
, uint32 n
)
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
);
413 float pct
[256]; /* known to be large enough */
415 pct
[0] = 1; /* force white */
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;
424 for (i
= 1; i
< 256; i
++)
425 pct
[i
] = 1-((float)i
)/(256-1);
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);
433 case PHOTOMETRIC_MINISBLACK
:
434 for (i
= 0; i
< 256; i
++)
435 cmap
[i
] = clamp(255*pct
[i
], 0, 255);
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
);
448 stepDstWidth
= stepSrcWidth
= 0;
453 * Calculate the horizontal accumulation parameteres
454 * according to the widths of the src and dst images.
457 setupStepTables(uint32 sw
)
459 if (stepSrcWidth
!= sw
|| stepDstWidth
!= tnw
) {
467 for (x
= 0; x
< tnw
; x
++) {
470 while (err
>= limit
) {
474 rowoff
[x
] = sx0
>> 3;
475 fw
= sx
- sx0
; /* width */
476 b
= (fw
< 8) ? 0xff<<(8-fw
) : 0xff;
477 src0
[x
] = b
>> (sx0
&7);
483 src2
[x
] = 0xff << (8-fw
);
491 setrow(uint8
* row
, uint32 nrows
, const uint8
* rows
[])
494 uint32 area
= nrows
* filterWidth
;
495 for (x
= 0; x
< tnw
; x
++) {
496 uint32 mask0
= src0
[x
];
498 uint32 mask1
= src1
[x
];
499 uint32 off
= rowoff
[x
];
502 for (y
= 0; y
< nrows
; y
++) {
503 const uint8
* src
= rows
[y
] + off
;
504 acc
+= bits
[*src
++ & mask0
];
507 for (i
= fw
; i
> 8; i
--)
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
++];
520 acc
+= bits
[*src
& mask1
];
522 *row
++ = cmap
[(255*acc
)/area
];
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.
533 setImage1(const uint8
* br
, uint32 rw
, uint32 rh
)
538 int bpr
= TIFFhowmany8(rw
);
540 uint8
* row
= thumbnail
;
542 for (dy
= 0; dy
< tnh
; dy
++) {
543 const uint8
* rows
[256];
545 fprintf(stderr
, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr
, sy
, bpr
*sy
);
546 rows
[0] = br
+ bpr
*sy
;
548 while (err
>= limit
) {
552 rows
[nrows
++] = br
+ bpr
*sy
;
554 setrow(row
, nrows
, rows
);
560 setImage(const uint8
* br
, uint32 rw
, uint32 rh
)
562 filterWidth
= (uint16
) ceil((double) rw
/ (double) tnw
);
564 setImage1(br
, rw
, rh
);
568 generateThumbnail(TIFF
* in
, TIFF
* out
)
570 unsigned char* raster
;
574 tsize_t rowsize
, rastersize
;
575 tstrip_t s
, ns
= TIFFNumberOfStrips(in
);
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)
585 rowsize
= TIFFScanlineSize(in
);
586 rastersize
= sh
* rowsize
;
587 fprintf(stderr
, "rastersize=%u\n", (unsigned int)rastersize
);
588 raster
= (unsigned char*)_TIFFmalloc(rastersize
);
590 TIFFError(TIFFFileName(in
),
591 "Can't allocate space for raster buffer.");
595 for (s
= 0; s
< ns
; s
++) {
596 (void) TIFFReadEncodedStrip(in
, s
, rp
, -1);
599 TIFFGetField(in
, TIFFTAG_PHOTOMETRIC
, &photometric
);
601 setImage(raster
, sw
, sh
);
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
);
618 TIFFSetField(out
, TIFFTAG_SUBIFD
, 1, diroff
);
619 return (TIFFWriteEncodedStrip(out
, 0, thumbnail
, tnw
*tnh
) != -1 &&
620 TIFFWriteDirectory(out
) != -1);
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)",
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",
646 fprintf(stderr
, "%s\n\n", TIFFGetVersion());
647 for (i
= 0; stuff
[i
] != NULL
; i
++)
648 fprintf(stderr
, "%s\n", stuff
[i
]);
652 /* vim: set ts=8 sts=8 sw=8 noet: */