xref: /libtiff-4.0.7/tools/thumbnail.c (revision d21d2b30)
1 /* $Id: thumbnail.c,v 1.21 2015-06-21 01:09:10 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 #if !HAVE_DECL_OPTARG
75 extern	char* optarg;
76 extern	int optind;
77 #endif
78 
79 int
main(int argc,char * argv[])80 main(int argc, char* argv[])
81 {
82     TIFF* in;
83     TIFF* out;
84     int c;
85 
86     while ((c = getopt(argc, argv, "w:h:c:")) != -1) {
87 	switch (c) {
88 	case 'w':	tnw = strtoul(optarg, NULL, 0); break;
89 	case 'h':	tnh = strtoul(optarg, NULL, 0); break;
90 	case 'c':	contrast = streq(optarg, "exp50") ? EXP50 :
91 				   streq(optarg, "exp60") ? EXP60 :
92 				   streq(optarg, "exp70") ? EXP70 :
93 				   streq(optarg, "exp80") ? EXP80 :
94 				   streq(optarg, "exp90") ? EXP90 :
95 				   streq(optarg, "exp")   ? EXP :
96 				   streq(optarg, "linear")? LINEAR :
97 							    EXP;
98 			break;
99 	default:	usage();
100 	}
101     }
102     if (argc-optind != 2)
103 	usage();
104 
105     out = TIFFOpen(argv[optind+1], "w");
106     if (out == NULL)
107 	return 2;
108     in = TIFFOpen(argv[optind], "r");
109     if( in == NULL )
110         return 2;
111 
112     thumbnail = (uint8*) _TIFFmalloc(tnw * tnh);
113     if (!thumbnail) {
114 	    TIFFError(TIFFFileName(in),
115 		      "Can't allocate space for thumbnail buffer.");
116 	    return 1;
117     }
118 
119     if (in != NULL) {
120 	initScale();
121 	do {
122 	    if (!generateThumbnail(in, out))
123 		goto bad;
124 	    if (!cpIFD(in, out) || !TIFFWriteDirectory(out))
125 		goto bad;
126 	} while (TIFFReadDirectory(in));
127 	(void) TIFFClose(in);
128     }
129     (void) TIFFClose(out);
130     return 0;
131 bad:
132     (void) TIFFClose(out);
133     return 1;
134 }
135 
136 #define	CopyField(tag, v) \
137     if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
138 #define	CopyField2(tag, v1, v2) \
139     if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2)
140 #define	CopyField3(tag, v1, v2, v3) \
141     if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
142 #define	CopyField4(tag, v1, v2, v3, v4) \
143     if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4)
144 
145 static void
cpTag(TIFF * in,TIFF * out,uint16 tag,uint16 count,TIFFDataType type)146 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
147 {
148 	switch (type) {
149 	case TIFF_SHORT:
150 		if (count == 1) {
151 			uint16 shortv;
152 			CopyField(tag, shortv);
153 		} else if (count == 2) {
154 			uint16 shortv1, shortv2;
155 			CopyField2(tag, shortv1, shortv2);
156 		} else if (count == 4) {
157 			uint16 *tr, *tg, *tb, *ta;
158 			CopyField4(tag, tr, tg, tb, ta);
159 		} else if (count == (uint16) -1) {
160 			uint16 shortv1;
161 			uint16* shortav;
162 			CopyField2(tag, shortv1, shortav);
163 		}
164 		break;
165 	case TIFF_LONG:
166 		{ uint32 longv;
167 		  CopyField(tag, longv);
168 		}
169 		break;
170 	case TIFF_LONG8:
171 		{ uint64 longv8;
172 		  CopyField(tag, longv8);
173 		}
174 		break;
175 	case TIFF_SLONG8:
176 		{ int64 longv8;
177 		  CopyField(tag, longv8);
178 		}
179 		break;
180 	case TIFF_RATIONAL:
181 		if (count == 1) {
182 			float floatv;
183 			CopyField(tag, floatv);
184 		} else if (count == (uint16) -1) {
185 			float* floatav;
186 			CopyField(tag, floatav);
187 		}
188 		break;
189 	case TIFF_ASCII:
190 		{ char* stringv;
191 		  CopyField(tag, stringv);
192 		}
193 		break;
194 	case TIFF_DOUBLE:
195 		if (count == 1) {
196 			double doublev;
197 			CopyField(tag, doublev);
198 		} else if (count == (uint16) -1) {
199 			double* doubleav;
200 			CopyField(tag, doubleav);
201 		}
202 		break;
203 	case TIFF_IFD8:
204 		{ toff_t ifd8;
205 		  CopyField(tag, ifd8);
206 		}
207 		break;          default:
208                 TIFFError(TIFFFileName(in),
209                           "Data type %d is not supported, tag %d skipped.",
210                           tag, type);
211 	}
212 }
213 
214 #undef CopyField4
215 #undef CopyField3
216 #undef CopyField2
217 #undef CopyField
218 
219 static struct cpTag {
220     uint16	tag;
221     uint16	count;
222     TIFFDataType type;
223 } tags[] = {
224     { TIFFTAG_IMAGEWIDTH,		1, TIFF_LONG },
225     { TIFFTAG_IMAGELENGTH,		1, TIFF_LONG },
226     { TIFFTAG_BITSPERSAMPLE,		1, TIFF_SHORT },
227     { TIFFTAG_COMPRESSION,		1, TIFF_SHORT },
228     { TIFFTAG_FILLORDER,		1, TIFF_SHORT },
229     { TIFFTAG_SAMPLESPERPIXEL,		1, TIFF_SHORT },
230     { TIFFTAG_ROWSPERSTRIP,		1, TIFF_LONG },
231     { TIFFTAG_PLANARCONFIG,		1, TIFF_SHORT },
232     { TIFFTAG_GROUP3OPTIONS,		1, TIFF_LONG },
233     { TIFFTAG_SUBFILETYPE,		1, TIFF_LONG },
234     { TIFFTAG_PHOTOMETRIC,		1, TIFF_SHORT },
235     { TIFFTAG_THRESHHOLDING,		1, TIFF_SHORT },
236     { TIFFTAG_DOCUMENTNAME,		1, TIFF_ASCII },
237     { TIFFTAG_IMAGEDESCRIPTION,		1, TIFF_ASCII },
238     { TIFFTAG_MAKE,			1, TIFF_ASCII },
239     { TIFFTAG_MODEL,			1, TIFF_ASCII },
240     { TIFFTAG_ORIENTATION,		1, TIFF_SHORT },
241     { TIFFTAG_MINSAMPLEVALUE,		1, TIFF_SHORT },
242     { TIFFTAG_MAXSAMPLEVALUE,		1, TIFF_SHORT },
243     { TIFFTAG_XRESOLUTION,		1, TIFF_RATIONAL },
244     { TIFFTAG_YRESOLUTION,		1, TIFF_RATIONAL },
245     { TIFFTAG_PAGENAME,			1, TIFF_ASCII },
246     { TIFFTAG_XPOSITION,		1, TIFF_RATIONAL },
247     { TIFFTAG_YPOSITION,		1, TIFF_RATIONAL },
248     { TIFFTAG_GROUP4OPTIONS,		1, TIFF_LONG },
249     { TIFFTAG_RESOLUTIONUNIT,		1, TIFF_SHORT },
250     { TIFFTAG_PAGENUMBER,		2, TIFF_SHORT },
251     { TIFFTAG_SOFTWARE,			1, TIFF_ASCII },
252     { TIFFTAG_DATETIME,			1, TIFF_ASCII },
253     { TIFFTAG_ARTIST,			1, TIFF_ASCII },
254     { TIFFTAG_HOSTCOMPUTER,		1, TIFF_ASCII },
255     { TIFFTAG_WHITEPOINT,		2, TIFF_RATIONAL },
256     { TIFFTAG_PRIMARYCHROMATICITIES,	(uint16) -1,TIFF_RATIONAL },
257     { TIFFTAG_HALFTONEHINTS,		2, TIFF_SHORT },
258     { TIFFTAG_BADFAXLINES,		1, TIFF_LONG },
259     { TIFFTAG_CLEANFAXDATA,		1, TIFF_SHORT },
260     { TIFFTAG_CONSECUTIVEBADFAXLINES,	1, TIFF_LONG },
261     { TIFFTAG_INKSET,			1, TIFF_SHORT },
262     /*{ TIFFTAG_INKNAMES,			1, TIFF_ASCII },*/ /* Needs much more complicated logic. See tiffcp */
263     { TIFFTAG_DOTRANGE,			2, TIFF_SHORT },
264     { TIFFTAG_TARGETPRINTER,		1, TIFF_ASCII },
265     { TIFFTAG_SAMPLEFORMAT,		1, TIFF_SHORT },
266     { TIFFTAG_YCBCRCOEFFICIENTS,	(uint16) -1,TIFF_RATIONAL },
267     { TIFFTAG_YCBCRSUBSAMPLING,		2, TIFF_SHORT },
268     { TIFFTAG_YCBCRPOSITIONING,		1, TIFF_SHORT },
269     { TIFFTAG_REFERENCEBLACKWHITE,	(uint16) -1,TIFF_RATIONAL },
270     { TIFFTAG_EXTRASAMPLES,		(uint16) -1, TIFF_SHORT },
271 };
272 #define	NTAGS	(sizeof (tags) / sizeof (tags[0]))
273 
274 static void
cpTags(TIFF * in,TIFF * out)275 cpTags(TIFF* in, TIFF* out)
276 {
277     struct cpTag *p;
278     for (p = tags; p < &tags[NTAGS]; p++)
279 	{
280 		/* Horrible: but TIFFGetField() expects 2 arguments to be passed */
281 		/* if we request a tag that is defined in a codec, but that codec */
282 		/* isn't used */
283 		if( p->tag == TIFFTAG_GROUP3OPTIONS )
284 		{
285 			uint16 compression;
286 			if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
287 				compression != COMPRESSION_CCITTFAX3 )
288 				continue;
289 		}
290 		if( p->tag == TIFFTAG_GROUP4OPTIONS )
291 		{
292 			uint16 compression;
293 			if( !TIFFGetField(in, TIFFTAG_COMPRESSION, &compression) ||
294 				compression != COMPRESSION_CCITTFAX4 )
295 				continue;
296 		}
297 		cpTag(in, out, p->tag, p->count, p->type);
298 	}
299 }
300 #undef NTAGS
301 
302 static int
cpStrips(TIFF * in,TIFF * out)303 cpStrips(TIFF* in, TIFF* out)
304 {
305     tsize_t bufsize  = TIFFStripSize(in);
306     unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
307 
308     if (buf) {
309 	tstrip_t s, ns = TIFFNumberOfStrips(in);
310 	uint64 *bytecounts;
311 
312 	TIFFGetField(in, TIFFTAG_STRIPBYTECOUNTS, &bytecounts);
313 	for (s = 0; s < ns; s++) {
314 	  if (bytecounts[s] > (uint64) bufsize) {
315 		buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[s]);
316 		if (!buf)
317 		    goto bad;
318 		bufsize = (tmsize_t)bytecounts[s];
319 	    }
320 	    if (TIFFReadRawStrip(in, s, buf, (tmsize_t)bytecounts[s]) < 0 ||
321 		TIFFWriteRawStrip(out, s, buf, (tmsize_t)bytecounts[s]) < 0) {
322 		_TIFFfree(buf);
323 		return 0;
324 	    }
325 	}
326 	_TIFFfree(buf);
327 	return 1;
328     }
329 
330 bad:
331 	TIFFError(TIFFFileName(in),
332 		  "Can't allocate space for strip buffer.");
333 	return 0;
334 }
335 
336 static int
cpTiles(TIFF * in,TIFF * out)337 cpTiles(TIFF* in, TIFF* out)
338 {
339     tsize_t bufsize = TIFFTileSize(in);
340     unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
341 
342     if (buf) {
343 	ttile_t t, nt = TIFFNumberOfTiles(in);
344 	uint64 *bytecounts;
345 
346 	TIFFGetField(in, TIFFTAG_TILEBYTECOUNTS, &bytecounts);
347 	for (t = 0; t < nt; t++) {
348 	    if (bytecounts[t] > (uint64) bufsize) {
349 		buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[t]);
350 		if (!buf)
351 		    goto bad;
352 		bufsize = (tmsize_t)bytecounts[t];
353 	    }
354 	    if (TIFFReadRawTile(in, t, buf, (tmsize_t)bytecounts[t]) < 0 ||
355 		TIFFWriteRawTile(out, t, buf, (tmsize_t)bytecounts[t]) < 0) {
356 		_TIFFfree(buf);
357 		return 0;
358 	    }
359 	}
360 	_TIFFfree(buf);
361 	return 1;
362     }
363 
364 bad:
365     TIFFError(TIFFFileName(in),
366 		  "Can't allocate space for tile buffer.");
367 	return (0);
368 }
369 
370 static int
cpIFD(TIFF * in,TIFF * out)371 cpIFD(TIFF* in, TIFF* out)
372 {
373     cpTags(in, out);
374     if (TIFFIsTiled(in)) {
375 	if (!cpTiles(in, out))
376 	    return (0);
377     } else {
378 	if (!cpStrips(in, out))
379 	    return (0);
380     }
381     return (1);
382 }
383 
384 static	uint16	photometric;		/* current photometric of raster */
385 static	uint16	filterWidth;		/* filter width in pixels */
386 static	uint32	stepSrcWidth;		/* src image stepping width */
387 static	uint32	stepDstWidth;		/* dest stepping width */
388 static	uint8* src0;			/* horizontal bit stepping (start) */
389 static	uint8* src1;			/* horizontal bit stepping (middle) */
390 static	uint8* src2;			/* horizontal bit stepping (end) */
391 static	uint32* rowoff;			/* row offset for stepping */
392 static	uint8 cmap[256];		/* colormap indexes */
393 static	uint8 bits[256];		/* count of bits set */
394 
395 static void
setupBitsTables()396 setupBitsTables()
397 {
398     int i;
399     for (i = 0; i < 256; i++) {
400 	int n = 0;
401 	if (i&0x01) n++;
402 	if (i&0x02) n++;
403 	if (i&0x04) n++;
404 	if (i&0x08) n++;
405 	if (i&0x10) n++;
406 	if (i&0x20) n++;
407 	if (i&0x40) n++;
408 	if (i&0x80) n++;
409 	bits[i] = n;
410     }
411 }
412 
clamp(float v,int low,int high)413 static int clamp(float v, int low, int high)
414     { return (v < low ? low : v > high ? high : (int)v); }
415 
416 #ifndef M_E
417 #define M_E		2.7182818284590452354
418 #endif
419 
420 static void
expFill(float pct[],uint32 p,uint32 n)421 expFill(float pct[], uint32 p, uint32 n)
422 {
423     uint32 i;
424     uint32 c = (p * n) / 100;
425     for (i = 1; i < c; i++)
426 	pct[i] = (float) (1-exp(i/((double)(n-1)))/ M_E);
427     for (; i < n; i++)
428 	pct[i] = 0.;
429 }
430 
431 static void
setupCmap()432 setupCmap()
433 {
434     float pct[256];			/* known to be large enough */
435     uint32 i;
436     pct[0] = 1;				/* force white */
437     switch (contrast) {
438     case EXP50: expFill(pct, 50, 256); break;
439     case EXP60:	expFill(pct, 60, 256); break;
440     case EXP70:	expFill(pct, 70, 256); break;
441     case EXP80:	expFill(pct, 80, 256); break;
442     case EXP90:	expFill(pct, 90, 256); break;
443     case EXP:	expFill(pct, 100, 256); break;
444     case LINEAR:
445 	for (i = 1; i < 256; i++)
446 	    pct[i] = 1-((float)i)/(256-1);
447 	break;
448     }
449     switch (photometric) {
450     case PHOTOMETRIC_MINISWHITE:
451 	for (i = 0; i < 256; i++)
452 	    cmap[i] = clamp(255*pct[(256-1)-i], 0, 255);
453 	break;
454     case PHOTOMETRIC_MINISBLACK:
455 	for (i = 0; i < 256; i++)
456 	    cmap[i] = clamp(255*pct[i], 0, 255);
457 	break;
458     }
459 }
460 
461 static void
initScale()462 initScale()
463 {
464     src0 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
465     src1 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
466     src2 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
467     rowoff = (uint32*) _TIFFmalloc(sizeof (uint32) * tnw);
468     filterWidth = 0;
469     stepDstWidth = stepSrcWidth = 0;
470     setupBitsTables();
471 }
472 
473 /*
474  * Calculate the horizontal accumulation parameteres
475  * according to the widths of the src and dst images.
476  */
477 static void
setupStepTables(uint32 sw)478 setupStepTables(uint32 sw)
479 {
480     if (stepSrcWidth != sw || stepDstWidth != tnw) {
481 	int step = sw;
482 	int limit = tnw;
483 	int err = 0;
484 	uint32 sx = 0;
485 	uint32 x;
486 	int fw;
487 	uint8 b;
488 	for (x = 0; x < tnw; x++) {
489 	    uint32 sx0 = sx;
490 	    err += step;
491 	    while (err >= limit) {
492 		err -= limit;
493 		sx++;
494 	    }
495 	    rowoff[x] = sx0 >> 3;
496 	    fw = sx - sx0;		/* width */
497 	    b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
498 	    src0[x] = b >> (sx0&7);
499 	    fw -= 8 - (sx0&7);
500 	    if (fw < 0)
501 		fw = 0;
502 	    src1[x] = fw >> 3;
503 	    fw -= (fw>>3)<<3;
504 	    src2[x] = 0xff << (8-fw);
505 	}
506 	stepSrcWidth = sw;
507 	stepDstWidth = tnw;
508     }
509 }
510 
511 static void
setrow(uint8 * row,uint32 nrows,const uint8 * rows[])512 setrow(uint8* row, uint32 nrows, const uint8* rows[])
513 {
514     uint32 x;
515     uint32 area = nrows * filterWidth;
516     for (x = 0; x < tnw; x++) {
517 	uint32 mask0 = src0[x];
518 	uint32 fw = src1[x];
519 	uint32 mask1 = src1[x];
520 	uint32 off = rowoff[x];
521 	uint32 acc = 0;
522 	uint32 y, i;
523 	for (y = 0; y < nrows; y++) {
524 	    const uint8* src = rows[y] + off;
525 	    acc += bits[*src++ & mask0];
526 	    switch (fw) {
527 	    default:
528 		for (i = fw; i > 8; i--)
529 		    acc += bits[*src++];
530 		/* fall thru... */
531 	    case 8: acc += bits[*src++];
532 	    case 7: acc += bits[*src++];
533 	    case 6: acc += bits[*src++];
534 	    case 5: acc += bits[*src++];
535 	    case 4: acc += bits[*src++];
536 	    case 3: acc += bits[*src++];
537 	    case 2: acc += bits[*src++];
538 	    case 1: acc += bits[*src++];
539 	    case 0: break;
540 	    }
541 	    acc += bits[*src & mask1];
542 	}
543 	*row++ = cmap[(255*acc)/area];
544     }
545 }
546 
547 /*
548  * Install the specified image.  The
549  * image is resized to fit the display page using
550  * a box filter.  The resultant pixels are mapped
551  * with a user-selectable contrast curve.
552  */
553 static void
setImage1(const uint8 * br,uint32 rw,uint32 rh)554 setImage1(const uint8* br, uint32 rw, uint32 rh)
555 {
556     int step = rh;
557     int limit = tnh;
558     int err = 0;
559     int bpr = TIFFhowmany8(rw);
560     int sy = 0;
561     uint8* row = thumbnail;
562     uint32 dy;
563     for (dy = 0; dy < tnh; dy++) {
564 	const uint8* rows[256];
565 	uint32 nrows = 1;
566 	fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
567 	rows[0] = br + bpr*sy;
568 	err += step;
569 	while (err >= limit) {
570 	    err -= limit;
571 	    sy++;
572 	    if (err >= limit)
573 		{
574 			/* We should perhaps error loudly, but I can't make sense of that */
575 			/* code... */
576 			if( nrows == 256 )
577 				break;
578 			rows[nrows++] = br + bpr*sy;
579 		}
580 	}
581 	setrow(row, nrows, rows);
582 	row += tnw;
583     }
584 }
585 
586 static void
setImage(const uint8 * br,uint32 rw,uint32 rh)587 setImage(const uint8* br, uint32 rw, uint32 rh)
588 {
589     filterWidth = (uint16) ceil((double) rw / (double) tnw);
590     setupStepTables(rw);
591     setImage1(br, rw, rh);
592 }
593 
594 static int
generateThumbnail(TIFF * in,TIFF * out)595 generateThumbnail(TIFF* in, TIFF* out)
596 {
597     unsigned char* raster;
598     unsigned char* rp;
599     uint32 sw, sh, rps;
600     uint16 bps, spp;
601     tsize_t rowsize, rastersize;
602     tstrip_t s, ns = TIFFNumberOfStrips(in);
603     toff_t diroff[1];
604 
605     TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &sw);
606     TIFFGetField(in, TIFFTAG_IMAGELENGTH, &sh);
607     TIFFGetFieldDefaulted(in, TIFFTAG_BITSPERSAMPLE, &bps);
608     TIFFGetFieldDefaulted(in, TIFFTAG_SAMPLESPERPIXEL, &spp);
609     TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &rps);
610     if (spp != 1 || bps != 1)
611 	return 0;
612     rowsize = TIFFScanlineSize(in);
613     rastersize = sh * rowsize;
614     fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
615 	/* +3 : add a few guard bytes since setrow() can read a bit */
616 	/* outside buffer */
617     raster = (unsigned char*)_TIFFmalloc(rastersize+3);
618     if (!raster) {
619 	    TIFFError(TIFFFileName(in),
620 		      "Can't allocate space for raster buffer.");
621 	    return 0;
622     }
623     raster[rastersize] = 0;
624     raster[rastersize+1] = 0;
625     raster[rastersize+2] = 0;
626     rp = raster;
627     for (s = 0; s < ns; s++) {
628 	(void) TIFFReadEncodedStrip(in, s, rp, -1);
629 	rp += rps * rowsize;
630     }
631     TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
632     setupCmap();
633     setImage(raster, sw, sh);
634     _TIFFfree(raster);
635 
636     TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE);
637     TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) tnw);
638     TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) tnh);
639     TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (uint16) 8);
640     TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (uint16) 1);
641     TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
642     TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
643     TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
644     TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
645     cpTag(in, out, TIFFTAG_SOFTWARE,		(uint16) -1, TIFF_ASCII);
646     cpTag(in, out, TIFFTAG_IMAGEDESCRIPTION,	(uint16) -1, TIFF_ASCII);
647     cpTag(in, out, TIFFTAG_DATETIME,		(uint16) -1, TIFF_ASCII);
648     cpTag(in, out, TIFFTAG_HOSTCOMPUTER,	(uint16) -1, TIFF_ASCII);
649     diroff[0] = 0UL;
650     TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
651     return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
652             TIFFWriteDirectory(out) != -1);
653 }
654 
655 char* stuff[] = {
656 "usage: thumbnail [options] input.tif output.tif",
657 "where options are:",
658 " -h #		specify thumbnail image height (default is 274)",
659 " -w #		specify thumbnail image width (default is 216)",
660 "",
661 " -c linear	use linear contrast curve",
662 " -c exp50	use 50% exponential contrast curve",
663 " -c exp60	use 60% exponential contrast curve",
664 " -c exp70	use 70% exponential contrast curve",
665 " -c exp80	use 80% exponential contrast curve",
666 " -c exp90	use 90% exponential contrast curve",
667 " -c exp		use pure exponential contrast curve",
668 NULL
669 };
670 
671 static void
usage(void)672 usage(void)
673 {
674 	char buf[BUFSIZ];
675 	int i;
676 
677 	setbuf(stderr, buf);
678         fprintf(stderr, "%s\n\n", TIFFGetVersion());
679 	for (i = 0; stuff[i] != NULL; i++)
680 		fprintf(stderr, "%s\n", stuff[i]);
681 	exit(-1);
682 }
683 
684 /* vim: set ts=8 sts=8 sw=8 noet: */
685 /*
686  * Local Variables:
687  * mode: c
688  * c-basic-offset: 8
689  * fill-column: 78
690  * End:
691  */
692