1
/* $Id: thumbnail.c,v 1.9.2.2 2010-07-02 12:03:27 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"
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, 2, 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++)
258
/* Horrible: but TIFFGetField() expects 2 arguments to be passed */
259
/* if we request a tag that is defined in a codec, but that codec */
261
if( p->tag == TIFFTAG_GROUP3OPTIONS )
264
if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
265
compression != COMPRESSION_CCITTFAX3 )
268
if( p->tag == TIFFTAG_GROUP4OPTIONS )
271
if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
272
compression != COMPRESSION_CCITTFAX4 )
275
cpTag(in, out, p->tag, p->count, p->type);
281
cpStrips(TIFF* in, TIFF* out)
283
tsize_t bufsize = TIFFStripSize(in);
284
unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
287
tstrip_t s, ns = TIFFNumberOfStrips(in);
290
TIFFGetField(in, TIFFTAG_STRIPBYTECOUNTS, &bytecounts);
291
for (s = 0; s < ns; s++) {
292
if (bytecounts[s] > bufsize) {
293
buf = (unsigned char *)_TIFFrealloc(buf, bytecounts[s]);
296
bufsize = bytecounts[s];
298
if (TIFFReadRawStrip(in, s, buf, bytecounts[s]) < 0 ||
299
TIFFWriteRawStrip(out, s, buf, bytecounts[s]) < 0) {
309
TIFFError(TIFFFileName(in),
310
"Can't allocate space for strip buffer.");
315
cpTiles(TIFF* in, TIFF* out)
317
tsize_t bufsize = TIFFTileSize(in);
318
unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
321
ttile_t t, nt = TIFFNumberOfTiles(in);
324
TIFFGetField(in, TIFFTAG_TILEBYTECOUNTS, &bytecounts);
325
for (t = 0; t < nt; t++) {
326
if (bytecounts[t] > bufsize) {
327
buf = (unsigned char *)_TIFFrealloc(buf, bytecounts[t]);
330
bufsize = bytecounts[t];
332
if (TIFFReadRawTile(in, t, buf, bytecounts[t]) < 0 ||
333
TIFFWriteRawTile(out, t, buf, bytecounts[t]) < 0) {
343
TIFFError(TIFFFileName(in),
344
"Can't allocate space for tile buffer.");
349
cpIFD(TIFF* in, TIFF* out)
352
if (TIFFIsTiled(in)) {
353
if (!cpTiles(in, out))
356
if (!cpStrips(in, out))
362
static uint16 photometric; /* current photometric of raster */
363
static uint16 filterWidth; /* filter width in pixels */
364
static uint32 stepSrcWidth; /* src image stepping width */
365
static uint32 stepDstWidth; /* dest stepping width */
366
static uint8* src0; /* horizontal bit stepping (start) */
367
static uint8* src1; /* horizontal bit stepping (middle) */
368
static uint8* src2; /* horizontal bit stepping (end) */
369
static uint32* rowoff; /* row offset for stepping */
370
static uint8 cmap[256]; /* colormap indexes */
371
static uint8 bits[256]; /* count of bits set */
377
for (i = 0; i < 256; i++) {
391
static int clamp(float v, int low, int high)
392
{ return (v < low ? low : v > high ? high : (int)v); }
395
#define M_E 2.7182818284590452354
399
expFill(float pct[], uint32 p, uint32 n)
402
uint32 c = (p * n) / 100;
403
for (i = 1; i < c; i++)
404
pct[i] = (float) (1-exp(i/((double)(n-1)))/ M_E);
412
float pct[256]; /* known to be large enough */
414
pct[0] = 1; /* force white */
416
case EXP50: expFill(pct, 50, 256); break;
417
case EXP60: expFill(pct, 60, 256); break;
418
case EXP70: expFill(pct, 70, 256); break;
419
case EXP80: expFill(pct, 80, 256); break;
420
case EXP90: expFill(pct, 90, 256); break;
421
case EXP: expFill(pct, 100, 256); break;
423
for (i = 1; i < 256; i++)
424
pct[i] = 1-((float)i)/(256-1);
427
switch (photometric) {
428
case PHOTOMETRIC_MINISWHITE:
429
for (i = 0; i < 256; i++)
430
cmap[i] = clamp(255*pct[(256-1)-i], 0, 255);
432
case PHOTOMETRIC_MINISBLACK:
433
for (i = 0; i < 256; i++)
434
cmap[i] = clamp(255*pct[i], 0, 255);
442
src0 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
443
src1 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
444
src2 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
445
rowoff = (uint32*) _TIFFmalloc(sizeof (uint32) * tnw);
447
stepDstWidth = stepSrcWidth = 0;
452
* Calculate the horizontal accumulation parameteres
453
* according to the widths of the src and dst images.
456
setupStepTables(uint32 sw)
458
if (stepSrcWidth != sw || stepDstWidth != tnw) {
466
for (x = 0; x < tnw; x++) {
469
while (err >= limit) {
473
rowoff[x] = sx0 >> 3;
474
fw = sx - sx0; /* width */
475
b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
476
src0[x] = b >> (sx0&7);
482
src2[x] = 0xff << (8-fw);
490
setrow(uint8* row, uint32 nrows, const uint8* rows[])
493
uint32 area = nrows * filterWidth;
494
for (x = 0; x < tnw; x++) {
495
uint32 mask0 = src0[x];
497
uint32 mask1 = src1[x];
498
uint32 off = rowoff[x];
501
for (y = 0; y < nrows; y++) {
502
const uint8* src = rows[y] + off;
503
acc += bits[*src++ & mask0];
506
for (i = fw; i > 8; i--)
509
case 8: acc += bits[*src++];
510
case 7: acc += bits[*src++];
511
case 6: acc += bits[*src++];
512
case 5: acc += bits[*src++];
513
case 4: acc += bits[*src++];
514
case 3: acc += bits[*src++];
515
case 2: acc += bits[*src++];
516
case 1: acc += bits[*src++];
519
acc += bits[*src & mask1];
521
*row++ = cmap[(255*acc)/area];
526
* Install the specified image. The
527
* image is resized to fit the display page using
528
* a box filter. The resultant pixels are mapped
529
* with a user-selectable contrast curve.
532
setImage1(const uint8* br, uint32 rw, uint32 rh)
537
int bpr = TIFFhowmany8(rw);
539
uint8* row = thumbnail;
541
for (dy = 0; dy < tnh; dy++) {
542
const uint8* rows[256];
544
fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
545
rows[0] = br + bpr*sy;
547
while (err >= limit) {
552
/* We should perhaps error loudly, but I can't make sense of that */
556
rows[nrows++] = br + bpr*sy;
559
setrow(row, nrows, rows);
565
setImage(const uint8* br, uint32 rw, uint32 rh)
567
filterWidth = (uint16) ceil((double) rw / (double) tnw);
569
setImage1(br, rw, rh);
573
generateThumbnail(TIFF* in, TIFF* out)
575
unsigned char* raster;
579
tsize_t rowsize, rastersize;
580
tstrip_t s, ns = TIFFNumberOfStrips(in);
583
TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &sw);
584
TIFFGetField(in, TIFFTAG_IMAGELENGTH, &sh);
585
TIFFGetFieldDefaulted(in, TIFFTAG_BITSPERSAMPLE, &bps);
586
TIFFGetFieldDefaulted(in, TIFFTAG_SAMPLESPERPIXEL, &spp);
587
TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &rps);
588
if (spp != 1 || bps != 1)
590
rowsize = TIFFScanlineSize(in);
591
rastersize = sh * rowsize;
592
fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
593
raster = (unsigned char*)_TIFFmalloc(rastersize);
595
TIFFError(TIFFFileName(in),
596
"Can't allocate space for raster buffer.");
600
for (s = 0; s < ns; s++) {
601
(void) TIFFReadEncodedStrip(in, s, rp, -1);
604
TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
606
setImage(raster, sw, sh);
609
TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE);
610
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) tnw);
611
TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) tnh);
612
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (uint16) 8);
613
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (uint16) 1);
614
TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
615
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
616
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
617
TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
618
cpTag(in, out, TIFFTAG_SOFTWARE, (uint16) -1, TIFF_ASCII);
619
cpTag(in, out, TIFFTAG_IMAGEDESCRIPTION, (uint16) -1, TIFF_ASCII);
620
cpTag(in, out, TIFFTAG_DATETIME, (uint16) -1, TIFF_ASCII);
621
cpTag(in, out, TIFFTAG_HOSTCOMPUTER, (uint16) -1, TIFF_ASCII);
623
TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
624
return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
625
TIFFWriteDirectory(out) != -1);
629
"usage: thumbnail [options] input.tif output.tif",
630
"where options are:",
631
" -h # specify thumbnail image height (default is 274)",
632
" -w # specify thumbnail image width (default is 216)",
634
" -c linear use linear contrast curve",
635
" -c exp50 use 50% exponential contrast curve",
636
" -c exp60 use 60% exponential contrast curve",
637
" -c exp70 use 70% exponential contrast curve",
638
" -c exp80 use 80% exponential contrast curve",
639
" -c exp90 use 90% exponential contrast curve",
640
" -c exp use pure exponential contrast curve",
651
fprintf(stderr, "%s\n\n", TIFFGetVersion());
652
for (i = 0; stuff[i] != NULL; i++)
653
fprintf(stderr, "%s\n", stuff[i]);
657
/* vim: set ts=8 sts=8 sw=8 noet: */