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: */