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