1 /* $Id: thumbnail.c,v 1.16 2010-07-02 12:02:56 dron Exp $ */
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: */