xref: /libtiff-4.0.7/tools/thumbnail.c (revision bfbc7176)
1 /* $Id: thumbnail.c,v 1.15 2010-03-10 18:56:49 bfriesen Exp $ */
2 
3 /*
4  * Copyright (c) 1994-1997 Sam Leffler
5  * Copyright (c) 1994-1997 Silicon Graphics, Inc.
6  *
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.
14  *
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.
18  *
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
24  * OF THIS SOFTWARE.
25  */
26 
27 #include "tif_config.h"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33 
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef NEED_LIBPORT
39 # include "libport.h"
40 #endif
41 
42 #include "tiffio.h"
43 
44 #ifndef HAVE_GETOPT
45 extern int getopt(int, char**, char*);
46 #endif
47 
48 #define	streq(a,b)	(strcmp(a,b) == 0)
49 
50 #ifndef TIFFhowmany8
51 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
52 #endif
53 
54 typedef enum {
55     EXP50,
56     EXP60,
57     EXP70,
58     EXP80,
59     EXP90,
60     EXP,
61     LINEAR
62 } Contrast;
63 
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;
68 
69 static	int cpIFD(TIFF*, TIFF*);
70 static	int generateThumbnail(TIFF*, TIFF*);
71 static	void initScale();
72 static	void usage(void);
73 
74 extern	char* optarg;
75 extern	int optind;
76 
77 int
78 main(int argc, char* argv[])
79 {
80     TIFF* in;
81     TIFF* out;
82     int c;
83 
84     while ((c = getopt(argc, argv, "w:h:c:")) != -1) {
85 	switch (c) {
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 :
95 							    EXP;
96 			break;
97 	default:	usage();
98 	}
99     }
100     if (argc-optind != 2)
101 	usage();
102 
103     out = TIFFOpen(argv[optind+1], "w");
104     if (out == NULL)
105 	return 2;
106     in = TIFFOpen(argv[optind], "r");
107     if( in == NULL )
108         return 2;
109 
110     thumbnail = (uint8*) _TIFFmalloc(tnw * tnh);
111     if (!thumbnail) {
112 	    TIFFError(TIFFFileName(in),
113 		      "Can't allocate space for thumbnail buffer.");
114 	    return 1;
115     }
116 
117     if (in != NULL) {
118 	initScale();
119 	do {
120 	    if (!generateThumbnail(in, out))
121 		goto bad;
122 	    if (!cpIFD(in, out) || !TIFFWriteDirectory(out))
123 		goto bad;
124 	} while (TIFFReadDirectory(in));
125 	(void) TIFFClose(in);
126     }
127     (void) TIFFClose(out);
128     return 0;
129 bad:
130     (void) TIFFClose(out);
131     return 1;
132 }
133 
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)
142 
143 static void
144 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
145 {
146 	switch (type) {
147 	case TIFF_SHORT:
148 		if (count == 1) {
149 			uint16 shortv;
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) {
158 			uint16 shortv1;
159 			uint16* shortav;
160 			CopyField2(tag, shortv1, shortav);
161 		}
162 		break;
163 	case TIFF_LONG:
164 		{ uint32 longv;
165 		  CopyField(tag, longv);
166 		}
167 		break;
168 	case TIFF_LONG8:
169 		{ uint64 longv8;
170 		  CopyField(tag, longv8);
171 		}
172 		break;
173 	case TIFF_SLONG8:
174 		{ int64 longv8;
175 		  CopyField(tag, longv8);
176 		}
177 		break;
178 	case TIFF_RATIONAL:
179 		if (count == 1) {
180 			float floatv;
181 			CopyField(tag, floatv);
182 		} else if (count == (uint16) -1) {
183 			float* floatav;
184 			CopyField(tag, floatav);
185 		}
186 		break;
187 	case TIFF_ASCII:
188 		{ char* stringv;
189 		  CopyField(tag, stringv);
190 		}
191 		break;
192 	case TIFF_DOUBLE:
193 		if (count == 1) {
194 			double doublev;
195 			CopyField(tag, doublev);
196 		} else if (count == (uint16) -1) {
197 			double* doubleav;
198 			CopyField(tag, doubleav);
199 		}
200 		break;
201 	case TIFF_IFD8:
202 		{ toff_t ifd8;
203 		  CopyField(tag, ifd8);
204 		}
205 		break;          default:
206                 TIFFError(TIFFFileName(in),
207                           "Data type %d is not supported, tag %d skipped.",
208                           tag, type);
209 	}
210 }
211 
212 #undef CopyField4
213 #undef CopyField3
214 #undef CopyField2
215 #undef CopyField
216 
217 static struct cpTag {
218     uint16	tag;
219     uint16	count;
220     TIFFDataType type;
221 } tags[] = {
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,		1, 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 },
269 };
270 #define	NTAGS	(sizeof (tags) / sizeof (tags[0]))
271 
272 static void
273 cpTags(TIFF* in, TIFF* out)
274 {
275     struct cpTag *p;
276     for (p = tags; p < &tags[NTAGS]; p++)
277 	cpTag(in, out, p->tag, p->count, p->type);
278 }
279 #undef NTAGS
280 
281 static int
282 cpStrips(TIFF* in, TIFF* out)
283 {
284     tsize_t bufsize  = TIFFStripSize(in);
285     unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
286 
287     if (buf) {
288 	tstrip_t s, ns = TIFFNumberOfStrips(in);
289 	uint64 *bytecounts;
290 
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]);
295 		if (!buf)
296 		    goto bad;
297 		bufsize = (tmsize_t)bytecounts[s];
298 	    }
299 	    if (TIFFReadRawStrip(in, s, buf, (tmsize_t)bytecounts[s]) < 0 ||
300 		TIFFWriteRawStrip(out, s, buf, (tmsize_t)bytecounts[s]) < 0) {
301 		_TIFFfree(buf);
302 		return 0;
303 	    }
304 	}
305 	_TIFFfree(buf);
306 	return 1;
307     }
308 
309 bad:
310 	TIFFError(TIFFFileName(in),
311 		  "Can't allocate space for strip buffer.");
312 	return 0;
313 }
314 
315 static int
316 cpTiles(TIFF* in, TIFF* out)
317 {
318     tsize_t bufsize = TIFFTileSize(in);
319     unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
320 
321     if (buf) {
322 	ttile_t t, nt = TIFFNumberOfTiles(in);
323 	uint64 *bytecounts;
324 
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]);
329 		if (!buf)
330 		    goto bad;
331 		bufsize = (tmsize_t)bytecounts[t];
332 	    }
333 	    if (TIFFReadRawTile(in, t, buf, (tmsize_t)bytecounts[t]) < 0 ||
334 		TIFFWriteRawTile(out, t, buf, (tmsize_t)bytecounts[t]) < 0) {
335 		_TIFFfree(buf);
336 		return 0;
337 	    }
338 	}
339 	_TIFFfree(buf);
340 	return 1;
341     }
342 
343 bad:
344     TIFFError(TIFFFileName(in),
345 		  "Can't allocate space for tile buffer.");
346 	return (0);
347 }
348 
349 static int
350 cpIFD(TIFF* in, TIFF* out)
351 {
352     cpTags(in, out);
353     if (TIFFIsTiled(in)) {
354 	if (!cpTiles(in, out))
355 	    return (0);
356     } else {
357 	if (!cpStrips(in, out))
358 	    return (0);
359     }
360     return (1);
361 }
362 
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 */
373 
374 static void
375 setupBitsTables()
376 {
377     int i;
378     for (i = 0; i < 256; i++) {
379 	int n = 0;
380 	if (i&0x01) n++;
381 	if (i&0x02) n++;
382 	if (i&0x04) n++;
383 	if (i&0x08) n++;
384 	if (i&0x10) n++;
385 	if (i&0x20) n++;
386 	if (i&0x40) n++;
387 	if (i&0x80) n++;
388 	bits[i] = n;
389     }
390 }
391 
392 static int clamp(float v, int low, int high)
393     { return (v < low ? low : v > high ? high : (int)v); }
394 
395 #ifndef M_E
396 #define M_E		2.7182818284590452354
397 #endif
398 
399 static void
400 expFill(float pct[], uint32 p, uint32 n)
401 {
402     uint32 i;
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);
406     for (; i < n; i++)
407 	pct[i] = 0.;
408 }
409 
410 static void
411 setupCmap()
412 {
413     float pct[256];			/* known to be large enough */
414     uint32 i;
415     pct[0] = 1;				/* force white */
416     switch (contrast) {
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;
423     case LINEAR:
424 	for (i = 1; i < 256; i++)
425 	    pct[i] = 1-((float)i)/(256-1);
426 	break;
427     }
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);
432 	break;
433     case PHOTOMETRIC_MINISBLACK:
434 	for (i = 0; i < 256; i++)
435 	    cmap[i] = clamp(255*pct[i], 0, 255);
436 	break;
437     }
438 }
439 
440 static void
441 initScale()
442 {
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);
447     filterWidth = 0;
448     stepDstWidth = stepSrcWidth = 0;
449     setupBitsTables();
450 }
451 
452 /*
453  * Calculate the horizontal accumulation parameteres
454  * according to the widths of the src and dst images.
455  */
456 static void
457 setupStepTables(uint32 sw)
458 {
459     if (stepSrcWidth != sw || stepDstWidth != tnw) {
460 	int step = sw;
461 	int limit = tnw;
462 	int err = 0;
463 	uint32 sx = 0;
464 	uint32 x;
465 	int fw;
466 	uint8 b;
467 	for (x = 0; x < tnw; x++) {
468 	    uint32 sx0 = sx;
469 	    err += step;
470 	    while (err >= limit) {
471 		err -= limit;
472 		sx++;
473 	    }
474 	    rowoff[x] = sx0 >> 3;
475 	    fw = sx - sx0;		/* width */
476 	    b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
477 	    src0[x] = b >> (sx0&7);
478 	    fw -= 8 - (sx0&7);
479 	    if (fw < 0)
480 		fw = 0;
481 	    src1[x] = fw >> 3;
482 	    fw -= (fw>>3)<<3;
483 	    src2[x] = 0xff << (8-fw);
484 	}
485 	stepSrcWidth = sw;
486 	stepDstWidth = tnw;
487     }
488 }
489 
490 static void
491 setrow(uint8* row, uint32 nrows, const uint8* rows[])
492 {
493     uint32 x;
494     uint32 area = nrows * filterWidth;
495     for (x = 0; x < tnw; x++) {
496 	uint32 mask0 = src0[x];
497 	uint32 fw = src1[x];
498 	uint32 mask1 = src1[x];
499 	uint32 off = rowoff[x];
500 	uint32 acc = 0;
501 	uint32 y, i;
502 	for (y = 0; y < nrows; y++) {
503 	    const uint8* src = rows[y] + off;
504 	    acc += bits[*src++ & mask0];
505 	    switch (fw) {
506 	    default:
507 		for (i = fw; i > 8; i--)
508 		    acc += bits[*src++];
509 		/* fall thru... */
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++];
518 	    case 0: break;
519 	    }
520 	    acc += bits[*src & mask1];
521 	}
522 	*row++ = cmap[(255*acc)/area];
523     }
524 }
525 
526 /*
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.
531  */
532 static void
533 setImage1(const uint8* br, uint32 rw, uint32 rh)
534 {
535     int step = rh;
536     int limit = tnh;
537     int err = 0;
538     int bpr = TIFFhowmany8(rw);
539     int sy = 0;
540     uint8* row = thumbnail;
541     uint32 dy;
542     for (dy = 0; dy < tnh; dy++) {
543 	const uint8* rows[256];
544 	uint32 nrows = 1;
545 	fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
546 	rows[0] = br + bpr*sy;
547 	err += step;
548 	while (err >= limit) {
549 	    err -= limit;
550 	    sy++;
551 	    if (err >= limit)
552 		rows[nrows++] = br + bpr*sy;
553 	}
554 	setrow(row, nrows, rows);
555 	row += tnw;
556     }
557 }
558 
559 static void
560 setImage(const uint8* br, uint32 rw, uint32 rh)
561 {
562     filterWidth = (uint16) ceil((double) rw / (double) tnw);
563     setupStepTables(rw);
564     setImage1(br, rw, rh);
565 }
566 
567 static int
568 generateThumbnail(TIFF* in, TIFF* out)
569 {
570     unsigned char* raster;
571     unsigned char* rp;
572     uint32 sw, sh, rps;
573     uint16 bps, spp;
574     tsize_t rowsize, rastersize;
575     tstrip_t s, ns = TIFFNumberOfStrips(in);
576     toff_t diroff[1];
577 
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)
584 	return 0;
585     rowsize = TIFFScanlineSize(in);
586     rastersize = sh * rowsize;
587     fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
588     raster = (unsigned char*)_TIFFmalloc(rastersize);
589     if (!raster) {
590 	    TIFFError(TIFFFileName(in),
591 		      "Can't allocate space for raster buffer.");
592 	    return 0;
593     }
594     rp = raster;
595     for (s = 0; s < ns; s++) {
596 	(void) TIFFReadEncodedStrip(in, s, rp, -1);
597 	rp += rps * rowsize;
598     }
599     TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
600     setupCmap();
601     setImage(raster, sw, sh);
602     _TIFFfree(raster);
603 
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);
617     diroff[0] = 0UL;
618     TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
619     return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
620             TIFFWriteDirectory(out) != -1);
621 }
622 
623 char* stuff[] = {
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)",
628 "",
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",
636 NULL
637 };
638 
639 static void
640 usage(void)
641 {
642 	char buf[BUFSIZ];
643 	int i;
644 
645 	setbuf(stderr, buf);
646         fprintf(stderr, "%s\n\n", TIFFGetVersion());
647 	for (i = 0; stuff[i] != NULL; i++)
648 		fprintf(stderr, "%s\n", stuff[i]);
649 	exit(-1);
650 }
651 
652 /* vim: set ts=8 sts=8 sw=8 noet: */
653 /*
654  * Local Variables:
655  * mode: c
656  * c-basic-offset: 8
657  * fill-column: 78
658  * End:
659  */
660