]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/tools/thumbnail.c
No changes, just use wxRecursionGuard instead of manual boolean flag.
[wxWidgets.git] / src / tiff / tools / thumbnail.c
1
2 /*
3 * Copyright (c) 1994-1997 Sam Leffler
4 * Copyright (c) 1994-1997 Silicon Graphics, Inc.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and
7 * its documentation for any purpose is hereby granted without fee, provided
8 * that (i) the above copyright notices and this permission notice appear in
9 * all copies of the software and related documentation, and (ii) the names of
10 * Sam Leffler and Silicon Graphics may not be used in any advertising or
11 * publicity relating to the software without the specific, prior written
12 * permission of Sam Leffler and Silicon Graphics.
13 *
14 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
16 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
17 *
18 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
19 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
20 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
22 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23 * OF THIS SOFTWARE.
24 */
25
26 #include "tif_config.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36
37 #ifdef NEED_LIBPORT
38 # include "libport.h"
39 #endif
40
41 #include "tiffio.h"
42
43 #ifndef HAVE_GETOPT
44 extern int getopt(int, char**, char*);
45 #endif
46
47 #define streq(a,b) (strcmp(a,b) == 0)
48
49 #ifndef TIFFhowmany8
50 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
51 #endif
52
53 typedef enum {
54 EXP50,
55 EXP60,
56 EXP70,
57 EXP80,
58 EXP90,
59 EXP,
60 LINEAR
61 } Contrast;
62
63 static uint32 tnw = 216; /* thumbnail width */
64 static uint32 tnh = 274; /* thumbnail height */
65 static Contrast contrast = LINEAR; /* current contrast */
66 static uint8* thumbnail;
67
68 static int cpIFD(TIFF*, TIFF*);
69 static int generateThumbnail(TIFF*, TIFF*);
70 static void initScale();
71 static void usage(void);
72
73 extern char* optarg;
74 extern int optind;
75
76 int
77 main(int argc, char* argv[])
78 {
79 TIFF* in;
80 TIFF* out;
81 int c;
82
83 while ((c = getopt(argc, argv, "w:h:c:")) != -1) {
84 switch (c) {
85 case 'w': tnw = strtoul(optarg, NULL, 0); break;
86 case 'h': tnh = strtoul(optarg, NULL, 0); break;
87 case 'c': contrast = streq(optarg, "exp50") ? EXP50 :
88 streq(optarg, "exp60") ? EXP60 :
89 streq(optarg, "exp70") ? EXP70 :
90 streq(optarg, "exp80") ? EXP80 :
91 streq(optarg, "exp90") ? EXP90 :
92 streq(optarg, "exp") ? EXP :
93 streq(optarg, "linear")? LINEAR :
94 EXP;
95 break;
96 default: usage();
97 }
98 }
99 if (argc-optind != 2)
100 usage();
101
102 out = TIFFOpen(argv[optind+1], "w");
103 if (out == NULL)
104 return 2;
105 in = TIFFOpen(argv[optind], "r");
106 if( in == NULL )
107 return 2;
108
109 thumbnail = (uint8*) _TIFFmalloc(tnw * tnh);
110 if (!thumbnail) {
111 TIFFError(TIFFFileName(in),
112 "Can't allocate space for thumbnail buffer.");
113 return 1;
114 }
115
116 if (in != NULL) {
117 initScale();
118 do {
119 if (!generateThumbnail(in, out))
120 goto bad;
121 if (!cpIFD(in, out) || !TIFFWriteDirectory(out))
122 goto bad;
123 } while (TIFFReadDirectory(in));
124 (void) TIFFClose(in);
125 }
126 (void) TIFFClose(out);
127 return 0;
128 bad:
129 (void) TIFFClose(out);
130 return 1;
131 }
132
133 #define CopyField(tag, v) \
134 if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
135 #define CopyField2(tag, v1, v2) \
136 if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2)
137 #define CopyField3(tag, v1, v2, v3) \
138 if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
139 #define CopyField4(tag, v1, v2, v3, v4) \
140 if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4)
141
142 static void
143 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
144 {
145 switch (type) {
146 case TIFF_SHORT:
147 if (count == 1) {
148 uint16 shortv;
149 CopyField(tag, shortv);
150 } else if (count == 2) {
151 uint16 shortv1, shortv2;
152 CopyField2(tag, shortv1, shortv2);
153 } else if (count == 4) {
154 uint16 *tr, *tg, *tb, *ta;
155 CopyField4(tag, tr, tg, tb, ta);
156 } else if (count == (uint16) -1) {
157 uint16 shortv1;
158 uint16* shortav;
159 CopyField2(tag, shortv1, shortav);
160 }
161 break;
162 case TIFF_LONG:
163 { uint32 longv;
164 CopyField(tag, longv);
165 }
166 break;
167 case TIFF_LONG8:
168 { uint64 longv8;
169 CopyField(tag, longv8);
170 }
171 break;
172 case TIFF_SLONG8:
173 { int64 longv8;
174 CopyField(tag, longv8);
175 }
176 break;
177 case TIFF_RATIONAL:
178 if (count == 1) {
179 float floatv;
180 CopyField(tag, floatv);
181 } else if (count == (uint16) -1) {
182 float* floatav;
183 CopyField(tag, floatav);
184 }
185 break;
186 case TIFF_ASCII:
187 { char* stringv;
188 CopyField(tag, stringv);
189 }
190 break;
191 case TIFF_DOUBLE:
192 if (count == 1) {
193 double doublev;
194 CopyField(tag, doublev);
195 } else if (count == (uint16) -1) {
196 double* doubleav;
197 CopyField(tag, doubleav);
198 }
199 break;
200 case TIFF_IFD8:
201 { toff_t ifd8;
202 CopyField(tag, ifd8);
203 }
204 break; default:
205 TIFFError(TIFFFileName(in),
206 "Data type %d is not supported, tag %d skipped.",
207 tag, type);
208 }
209 }
210
211 #undef CopyField4
212 #undef CopyField3
213 #undef CopyField2
214 #undef CopyField
215
216 static struct cpTag {
217 uint16 tag;
218 uint16 count;
219 TIFFDataType type;
220 } tags[] = {
221 { TIFFTAG_IMAGEWIDTH, 1, TIFF_LONG },
222 { TIFFTAG_IMAGELENGTH, 1, TIFF_LONG },
223 { TIFFTAG_BITSPERSAMPLE, 1, TIFF_SHORT },
224 { TIFFTAG_COMPRESSION, 1, TIFF_SHORT },
225 { TIFFTAG_FILLORDER, 1, TIFF_SHORT },
226 { TIFFTAG_SAMPLESPERPIXEL, 1, TIFF_SHORT },
227 { TIFFTAG_ROWSPERSTRIP, 1, TIFF_LONG },
228 { TIFFTAG_PLANARCONFIG, 1, TIFF_SHORT },
229 { TIFFTAG_GROUP3OPTIONS, 1, TIFF_LONG },
230 { TIFFTAG_SUBFILETYPE, 1, TIFF_LONG },
231 { TIFFTAG_PHOTOMETRIC, 1, TIFF_SHORT },
232 { TIFFTAG_THRESHHOLDING, 1, TIFF_SHORT },
233 { TIFFTAG_DOCUMENTNAME, 1, TIFF_ASCII },
234 { TIFFTAG_IMAGEDESCRIPTION, 1, TIFF_ASCII },
235 { TIFFTAG_MAKE, 1, TIFF_ASCII },
236 { TIFFTAG_MODEL, 1, TIFF_ASCII },
237 { TIFFTAG_ORIENTATION, 1, TIFF_SHORT },
238 { TIFFTAG_MINSAMPLEVALUE, 1, TIFF_SHORT },
239 { TIFFTAG_MAXSAMPLEVALUE, 1, TIFF_SHORT },
240 { TIFFTAG_XRESOLUTION, 1, TIFF_RATIONAL },
241 { TIFFTAG_YRESOLUTION, 1, TIFF_RATIONAL },
242 { TIFFTAG_PAGENAME, 1, TIFF_ASCII },
243 { TIFFTAG_XPOSITION, 1, TIFF_RATIONAL },
244 { TIFFTAG_YPOSITION, 1, TIFF_RATIONAL },
245 { TIFFTAG_GROUP4OPTIONS, 1, TIFF_LONG },
246 { TIFFTAG_RESOLUTIONUNIT, 1, TIFF_SHORT },
247 { TIFFTAG_PAGENUMBER, 2, TIFF_SHORT },
248 { TIFFTAG_SOFTWARE, 1, TIFF_ASCII },
249 { TIFFTAG_DATETIME, 1, TIFF_ASCII },
250 { TIFFTAG_ARTIST, 1, TIFF_ASCII },
251 { TIFFTAG_HOSTCOMPUTER, 1, TIFF_ASCII },
252 { TIFFTAG_WHITEPOINT, 2, TIFF_RATIONAL },
253 { TIFFTAG_PRIMARYCHROMATICITIES, (uint16) -1,TIFF_RATIONAL },
254 { TIFFTAG_HALFTONEHINTS, 2, TIFF_SHORT },
255 { TIFFTAG_BADFAXLINES, 1, TIFF_LONG },
256 { TIFFTAG_CLEANFAXDATA, 1, TIFF_SHORT },
257 { TIFFTAG_CONSECUTIVEBADFAXLINES, 1, TIFF_LONG },
258 { TIFFTAG_INKSET, 1, TIFF_SHORT },
259 { TIFFTAG_INKNAMES, 1, TIFF_ASCII },
260 { TIFFTAG_DOTRANGE, 2, TIFF_SHORT },
261 { TIFFTAG_TARGETPRINTER, 1, TIFF_ASCII },
262 { TIFFTAG_SAMPLEFORMAT, 1, TIFF_SHORT },
263 { TIFFTAG_YCBCRCOEFFICIENTS, (uint16) -1,TIFF_RATIONAL },
264 { TIFFTAG_YCBCRSUBSAMPLING, 2, TIFF_SHORT },
265 { TIFFTAG_YCBCRPOSITIONING, 1, TIFF_SHORT },
266 { TIFFTAG_REFERENCEBLACKWHITE, (uint16) -1,TIFF_RATIONAL },
267 { TIFFTAG_EXTRASAMPLES, (uint16) -1, TIFF_SHORT },
268 };
269 #define NTAGS (sizeof (tags) / sizeof (tags[0]))
270
271 static void
272 cpTags(TIFF* in, TIFF* out)
273 {
274 struct cpTag *p;
275 for (p = tags; p < &tags[NTAGS]; p++)
276 cpTag(in, out, p->tag, p->count, p->type);
277 }
278 #undef NTAGS
279
280 static int
281 cpStrips(TIFF* in, TIFF* out)
282 {
283 tsize_t bufsize = TIFFStripSize(in);
284 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
285
286 if (buf) {
287 tstrip_t s, ns = TIFFNumberOfStrips(in);
288 uint64 *bytecounts;
289
290 TIFFGetField(in, TIFFTAG_STRIPBYTECOUNTS, &bytecounts);
291 for (s = 0; s < ns; s++) {
292 if (bytecounts[s] > (uint64) bufsize) {
293 buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[s]);
294 if (!buf)
295 goto bad;
296 bufsize = (tmsize_t)bytecounts[s];
297 }
298 if (TIFFReadRawStrip(in, s, buf, (tmsize_t)bytecounts[s]) < 0 ||
299 TIFFWriteRawStrip(out, s, buf, (tmsize_t)bytecounts[s]) < 0) {
300 _TIFFfree(buf);
301 return 0;
302 }
303 }
304 _TIFFfree(buf);
305 return 1;
306 }
307
308 bad:
309 TIFFError(TIFFFileName(in),
310 "Can't allocate space for strip buffer.");
311 return 0;
312 }
313
314 static int
315 cpTiles(TIFF* in, TIFF* out)
316 {
317 tsize_t bufsize = TIFFTileSize(in);
318 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
319
320 if (buf) {
321 ttile_t t, nt = TIFFNumberOfTiles(in);
322 uint64 *bytecounts;
323
324 TIFFGetField(in, TIFFTAG_TILEBYTECOUNTS, &bytecounts);
325 for (t = 0; t < nt; t++) {
326 if (bytecounts[t] > (uint64) bufsize) {
327 buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[t]);
328 if (!buf)
329 goto bad;
330 bufsize = (tmsize_t)bytecounts[t];
331 }
332 if (TIFFReadRawTile(in, t, buf, (tmsize_t)bytecounts[t]) < 0 ||
333 TIFFWriteRawTile(out, t, buf, (tmsize_t)bytecounts[t]) < 0) {
334 _TIFFfree(buf);
335 return 0;
336 }
337 }
338 _TIFFfree(buf);
339 return 1;
340 }
341
342 bad:
343 TIFFError(TIFFFileName(in),
344 "Can't allocate space for tile buffer.");
345 return (0);
346 }
347
348 static int
349 cpIFD(TIFF* in, TIFF* out)
350 {
351 cpTags(in, out);
352 if (TIFFIsTiled(in)) {
353 if (!cpTiles(in, out))
354 return (0);
355 } else {
356 if (!cpStrips(in, out))
357 return (0);
358 }
359 return (1);
360 }
361
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 */
372
373 static void
374 setupBitsTables()
375 {
376 int i;
377 for (i = 0; i < 256; i++) {
378 int n = 0;
379 if (i&0x01) n++;
380 if (i&0x02) n++;
381 if (i&0x04) n++;
382 if (i&0x08) n++;
383 if (i&0x10) n++;
384 if (i&0x20) n++;
385 if (i&0x40) n++;
386 if (i&0x80) n++;
387 bits[i] = n;
388 }
389 }
390
391 static int clamp(float v, int low, int high)
392 { return (v < low ? low : v > high ? high : (int)v); }
393
394 #ifndef M_E
395 #define M_E 2.7182818284590452354
396 #endif
397
398 static void
399 expFill(float pct[], uint32 p, uint32 n)
400 {
401 uint32 i;
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);
405 for (; i < n; i++)
406 pct[i] = 0.;
407 }
408
409 static void
410 setupCmap()
411 {
412 float pct[256]; /* known to be large enough */
413 uint32 i;
414 pct[0] = 1; /* force white */
415 switch (contrast) {
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;
422 case LINEAR:
423 for (i = 1; i < 256; i++)
424 pct[i] = 1-((float)i)/(256-1);
425 break;
426 }
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);
431 break;
432 case PHOTOMETRIC_MINISBLACK:
433 for (i = 0; i < 256; i++)
434 cmap[i] = clamp(255*pct[i], 0, 255);
435 break;
436 }
437 }
438
439 static void
440 initScale()
441 {
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);
446 filterWidth = 0;
447 stepDstWidth = stepSrcWidth = 0;
448 setupBitsTables();
449 }
450
451 /*
452 * Calculate the horizontal accumulation parameteres
453 * according to the widths of the src and dst images.
454 */
455 static void
456 setupStepTables(uint32 sw)
457 {
458 if (stepSrcWidth != sw || stepDstWidth != tnw) {
459 int step = sw;
460 int limit = tnw;
461 int err = 0;
462 uint32 sx = 0;
463 uint32 x;
464 int fw;
465 uint8 b;
466 for (x = 0; x < tnw; x++) {
467 uint32 sx0 = sx;
468 err += step;
469 while (err >= limit) {
470 err -= limit;
471 sx++;
472 }
473 rowoff[x] = sx0 >> 3;
474 fw = sx - sx0; /* width */
475 b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
476 src0[x] = b >> (sx0&7);
477 fw -= 8 - (sx0&7);
478 if (fw < 0)
479 fw = 0;
480 src1[x] = fw >> 3;
481 fw -= (fw>>3)<<3;
482 src2[x] = 0xff << (8-fw);
483 }
484 stepSrcWidth = sw;
485 stepDstWidth = tnw;
486 }
487 }
488
489 static void
490 setrow(uint8* row, uint32 nrows, const uint8* rows[])
491 {
492 uint32 x;
493 uint32 area = nrows * filterWidth;
494 for (x = 0; x < tnw; x++) {
495 uint32 mask0 = src0[x];
496 uint32 fw = src1[x];
497 uint32 mask1 = src1[x];
498 uint32 off = rowoff[x];
499 uint32 acc = 0;
500 uint32 y, i;
501 for (y = 0; y < nrows; y++) {
502 const uint8* src = rows[y] + off;
503 acc += bits[*src++ & mask0];
504 switch (fw) {
505 default:
506 for (i = fw; i > 8; i--)
507 acc += bits[*src++];
508 /* fall thru... */
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++];
517 case 0: break;
518 }
519 acc += bits[*src & mask1];
520 }
521 *row++ = cmap[(255*acc)/area];
522 }
523 }
524
525 /*
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.
530 */
531 static void
532 setImage1(const uint8* br, uint32 rw, uint32 rh)
533 {
534 int step = rh;
535 int limit = tnh;
536 int err = 0;
537 int bpr = TIFFhowmany8(rw);
538 int sy = 0;
539 uint8* row = thumbnail;
540 uint32 dy;
541 for (dy = 0; dy < tnh; dy++) {
542 const uint8* rows[256];
543 uint32 nrows = 1;
544 fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
545 rows[0] = br + bpr*sy;
546 err += step;
547 while (err >= limit) {
548 err -= limit;
549 sy++;
550 if (err >= limit)
551 rows[nrows++] = br + bpr*sy;
552 }
553 setrow(row, nrows, rows);
554 row += tnw;
555 }
556 }
557
558 static void
559 setImage(const uint8* br, uint32 rw, uint32 rh)
560 {
561 filterWidth = (uint16) ceil((double) rw / (double) tnw);
562 setupStepTables(rw);
563 setImage1(br, rw, rh);
564 }
565
566 static int
567 generateThumbnail(TIFF* in, TIFF* out)
568 {
569 unsigned char* raster;
570 unsigned char* rp;
571 uint32 sw, sh, rps;
572 uint16 bps, spp;
573 tsize_t rowsize, rastersize;
574 tstrip_t s, ns = TIFFNumberOfStrips(in);
575 toff_t diroff[1];
576
577 TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &sw);
578 TIFFGetField(in, TIFFTAG_IMAGELENGTH, &sh);
579 TIFFGetFieldDefaulted(in, TIFFTAG_BITSPERSAMPLE, &bps);
580 TIFFGetFieldDefaulted(in, TIFFTAG_SAMPLESPERPIXEL, &spp);
581 TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &rps);
582 if (spp != 1 || bps != 1)
583 return 0;
584 rowsize = TIFFScanlineSize(in);
585 rastersize = sh * rowsize;
586 fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
587 raster = (unsigned char*)_TIFFmalloc(rastersize);
588 if (!raster) {
589 TIFFError(TIFFFileName(in),
590 "Can't allocate space for raster buffer.");
591 return 0;
592 }
593 rp = raster;
594 for (s = 0; s < ns; s++) {
595 (void) TIFFReadEncodedStrip(in, s, rp, -1);
596 rp += rps * rowsize;
597 }
598 TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
599 setupCmap();
600 setImage(raster, sw, sh);
601 _TIFFfree(raster);
602
603 TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE);
604 TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) tnw);
605 TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) tnh);
606 TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (uint16) 8);
607 TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (uint16) 1);
608 TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
609 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
610 TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
611 TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
612 cpTag(in, out, TIFFTAG_SOFTWARE, (uint16) -1, TIFF_ASCII);
613 cpTag(in, out, TIFFTAG_IMAGEDESCRIPTION, (uint16) -1, TIFF_ASCII);
614 cpTag(in, out, TIFFTAG_DATETIME, (uint16) -1, TIFF_ASCII);
615 cpTag(in, out, TIFFTAG_HOSTCOMPUTER, (uint16) -1, TIFF_ASCII);
616 diroff[0] = 0UL;
617 TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
618 return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
619 TIFFWriteDirectory(out) != -1);
620 }
621
622 char* stuff[] = {
623 "usage: thumbnail [options] input.tif output.tif",
624 "where options are:",
625 " -h # specify thumbnail image height (default is 274)",
626 " -w # specify thumbnail image width (default is 216)",
627 "",
628 " -c linear use linear contrast curve",
629 " -c exp50 use 50% exponential contrast curve",
630 " -c exp60 use 60% exponential contrast curve",
631 " -c exp70 use 70% exponential contrast curve",
632 " -c exp80 use 80% exponential contrast curve",
633 " -c exp90 use 90% exponential contrast curve",
634 " -c exp use pure exponential contrast curve",
635 NULL
636 };
637
638 static void
639 usage(void)
640 {
641 char buf[BUFSIZ];
642 int i;
643
644 setbuf(stderr, buf);
645 fprintf(stderr, "%s\n\n", TIFFGetVersion());
646 for (i = 0; stuff[i] != NULL; i++)
647 fprintf(stderr, "%s\n", stuff[i]);
648 exit(-1);
649 }
650
651 /* vim: set ts=8 sts=8 sw=8 noet: */
652 /*
653 * Local Variables:
654 * mode: c
655 * c-basic-offset: 8
656 * fill-column: 78
657 * End:
658 */