]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/tools/thumbnail.c
no real change: just rename "near" into "onNearPart" (MSVC doesn't like 'near' as...
[wxWidgets.git] / src / tiff / tools / thumbnail.c
1 /* $Id$ */
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 #include "tiffio.h"
39
40 #ifndef HAVE_GETOPT
41 extern int getopt(int, char**, char*);
42 #endif
43
44 #define streq(a,b) (strcmp(a,b) == 0)
45
46 #ifndef TIFFhowmany8
47 # define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
48 #endif
49
50 typedef enum {
51 EXP50,
52 EXP60,
53 EXP70,
54 EXP80,
55 EXP90,
56 EXP,
57 LINEAR
58 } Contrast;
59
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;
64
65 static int cpIFD(TIFF*, TIFF*);
66 static int generateThumbnail(TIFF*, TIFF*);
67 static void initScale();
68 static void usage(void);
69
70 extern char* optarg;
71 extern int optind;
72
73 int
74 main(int argc, char* argv[])
75 {
76 TIFF* in;
77 TIFF* out;
78 int c;
79
80 while ((c = getopt(argc, argv, "w:h:c:")) != -1) {
81 switch (c) {
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 :
91 EXP;
92 break;
93 default: usage();
94 }
95 }
96 if (argc-optind != 2)
97 usage();
98
99 out = TIFFOpen(argv[optind+1], "w");
100 if (out == NULL)
101 return 2;
102 in = TIFFOpen(argv[optind], "r");
103
104 thumbnail = (uint8*) _TIFFmalloc(tnw * tnh);
105 if (!thumbnail) {
106 TIFFError(TIFFFileName(in),
107 "Can't allocate space for thumbnail buffer.");
108 return 1;
109 }
110
111 if (in != NULL) {
112 initScale();
113 do {
114 if (!generateThumbnail(in, out))
115 goto bad;
116 if (!cpIFD(in, out) || !TIFFWriteDirectory(out))
117 goto bad;
118 } while (TIFFReadDirectory(in));
119 (void) TIFFClose(in);
120 }
121 (void) TIFFClose(out);
122 return 0;
123 bad:
124 (void) TIFFClose(out);
125 return 1;
126 }
127
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)
136
137 static void
138 cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
139 {
140 switch (type) {
141 case TIFF_SHORT:
142 if (count == 1) {
143 uint16 shortv;
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) {
152 uint16 shortv1;
153 uint16* shortav;
154 CopyField2(tag, shortv1, shortav);
155 }
156 break;
157 case TIFF_LONG:
158 { uint32 longv;
159 CopyField(tag, longv);
160 }
161 break;
162 case TIFF_RATIONAL:
163 if (count == 1) {
164 float floatv;
165 CopyField(tag, floatv);
166 } else if (count == (uint16) -1) {
167 float* floatav;
168 CopyField(tag, floatav);
169 }
170 break;
171 case TIFF_ASCII:
172 { char* stringv;
173 CopyField(tag, stringv);
174 }
175 break;
176 case TIFF_DOUBLE:
177 if (count == 1) {
178 double doublev;
179 CopyField(tag, doublev);
180 } else if (count == (uint16) -1) {
181 double* doubleav;
182 CopyField(tag, doubleav);
183 }
184 break;
185 default:
186 TIFFError(TIFFFileName(in),
187 "Data type %d is not supported, tag %d skipped.",
188 tag, type);
189 }
190 }
191
192 #undef CopyField4
193 #undef CopyField3
194 #undef CopyField2
195 #undef CopyField
196
197 static struct cpTag {
198 uint16 tag;
199 uint16 count;
200 TIFFDataType type;
201 } tags[] = {
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, 1, 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 },
249 };
250 #define NTAGS (sizeof (tags) / sizeof (tags[0]))
251
252 static void
253 cpTags(TIFF* in, TIFF* out)
254 {
255 struct cpTag *p;
256 for (p = tags; p < &tags[NTAGS]; p++)
257 cpTag(in, out, p->tag, p->count, p->type);
258 }
259 #undef NTAGS
260
261 static int
262 cpStrips(TIFF* in, TIFF* out)
263 {
264 tsize_t bufsize = TIFFStripSize(in);
265 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
266
267 if (buf) {
268 tstrip_t s, ns = TIFFNumberOfStrips(in);
269 tsize_t *bytecounts;
270
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]);
275 if (!buf)
276 goto bad;
277 bufsize = bytecounts[s];
278 }
279 if (TIFFReadRawStrip(in, s, buf, bytecounts[s]) < 0 ||
280 TIFFWriteRawStrip(out, s, buf, bytecounts[s]) < 0) {
281 _TIFFfree(buf);
282 return 0;
283 }
284 }
285 _TIFFfree(buf);
286 return 1;
287 }
288
289 bad:
290 TIFFError(TIFFFileName(in),
291 "Can't allocate space for strip buffer.");
292 return 0;
293 }
294
295 static int
296 cpTiles(TIFF* in, TIFF* out)
297 {
298 tsize_t bufsize = TIFFTileSize(in);
299 unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
300
301 if (buf) {
302 ttile_t t, nt = TIFFNumberOfTiles(in);
303 tsize_t *bytecounts;
304
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]);
309 if (!buf)
310 goto bad;
311 bufsize = bytecounts[t];
312 }
313 if (TIFFReadRawTile(in, t, buf, bytecounts[t]) < 0 ||
314 TIFFWriteRawTile(out, t, buf, bytecounts[t]) < 0) {
315 _TIFFfree(buf);
316 return 0;
317 }
318 }
319 _TIFFfree(buf);
320 return 1;
321 }
322
323 bad:
324 TIFFError(TIFFFileName(in),
325 "Can't allocate space for tile buffer.");
326 return (0);
327 }
328
329 static int
330 cpIFD(TIFF* in, TIFF* out)
331 {
332 cpTags(in, out);
333 if (TIFFIsTiled(in)) {
334 if (!cpTiles(in, out))
335 return (0);
336 } else {
337 if (!cpStrips(in, out))
338 return (0);
339 }
340 return (1);
341 }
342
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 */
353
354 static void
355 setupBitsTables()
356 {
357 int i;
358 for (i = 0; i < 256; i++) {
359 int n = 0;
360 if (i&0x01) n++;
361 if (i&0x02) n++;
362 if (i&0x04) n++;
363 if (i&0x08) n++;
364 if (i&0x10) n++;
365 if (i&0x20) n++;
366 if (i&0x40) n++;
367 if (i&0x80) n++;
368 bits[i] = n;
369 }
370 }
371
372 static int clamp(float v, int low, int high)
373 { return (v < low ? low : v > high ? high : (int)v); }
374
375 #ifndef M_E
376 #define M_E 2.7182818284590452354
377 #endif
378
379 static void
380 expFill(float pct[], uint32 p, uint32 n)
381 {
382 uint32 i;
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);
386 for (; i < n; i++)
387 pct[i] = 0.;
388 }
389
390 static void
391 setupCmap()
392 {
393 float pct[256]; /* known to be large enough */
394 uint32 i;
395 pct[0] = 1; /* force white */
396 switch (contrast) {
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;
403 case LINEAR:
404 for (i = 1; i < 256; i++)
405 pct[i] = 1-((float)i)/(256-1);
406 break;
407 }
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);
412 break;
413 case PHOTOMETRIC_MINISBLACK:
414 for (i = 0; i < 256; i++)
415 cmap[i] = clamp(255*pct[i], 0, 255);
416 break;
417 }
418 }
419
420 static void
421 initScale()
422 {
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);
427 filterWidth = 0;
428 stepDstWidth = stepSrcWidth = 0;
429 setupBitsTables();
430 }
431
432 /*
433 * Calculate the horizontal accumulation parameteres
434 * according to the widths of the src and dst images.
435 */
436 static void
437 setupStepTables(uint32 sw)
438 {
439 if (stepSrcWidth != sw || stepDstWidth != tnw) {
440 int step = sw;
441 int limit = tnw;
442 int err = 0;
443 uint32 sx = 0;
444 uint32 x;
445 int fw;
446 uint8 b;
447 for (x = 0; x < tnw; x++) {
448 uint32 sx0 = sx;
449 err += step;
450 while (err >= limit) {
451 err -= limit;
452 sx++;
453 }
454 rowoff[x] = sx0 >> 3;
455 fw = sx - sx0; /* width */
456 b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
457 src0[x] = b >> (sx0&7);
458 fw -= 8 - (sx0&7);
459 if (fw < 0)
460 fw = 0;
461 src1[x] = fw >> 3;
462 fw -= (fw>>3)<<3;
463 src2[x] = 0xff << (8-fw);
464 }
465 stepSrcWidth = sw;
466 stepDstWidth = tnw;
467 }
468 }
469
470 static void
471 setrow(uint8* row, uint32 nrows, const uint8* rows[])
472 {
473 uint32 x;
474 uint32 area = nrows * filterWidth;
475 for (x = 0; x < tnw; x++) {
476 uint32 mask0 = src0[x];
477 uint32 fw = src1[x];
478 uint32 mask1 = src1[x];
479 uint32 off = rowoff[x];
480 uint32 acc = 0;
481 uint32 y, i;
482 for (y = 0; y < nrows; y++) {
483 const uint8* src = rows[y] + off;
484 acc += bits[*src++ & mask0];
485 switch (fw) {
486 default:
487 for (i = fw; i > 8; i--)
488 acc += bits[*src++];
489 /* fall thru... */
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++];
498 case 0: break;
499 }
500 acc += bits[*src & mask1];
501 }
502 *row++ = cmap[(255*acc)/area];
503 }
504 }
505
506 /*
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.
511 */
512 static void
513 setImage1(const uint8* br, uint32 rw, uint32 rh)
514 {
515 int step = rh;
516 int limit = tnh;
517 int err = 0;
518 int bpr = TIFFhowmany8(rw);
519 int sy = 0;
520 uint8* row = thumbnail;
521 uint32 dy;
522 for (dy = 0; dy < tnh; dy++) {
523 const uint8* rows[256];
524 uint32 nrows = 1;
525 fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
526 rows[0] = br + bpr*sy;
527 err += step;
528 while (err >= limit) {
529 err -= limit;
530 sy++;
531 if (err >= limit)
532 rows[nrows++] = br + bpr*sy;
533 }
534 setrow(row, nrows, rows);
535 row += tnw;
536 }
537 }
538
539 static void
540 setImage(const uint8* br, uint32 rw, uint32 rh)
541 {
542 filterWidth = (uint16) ceil((double) rw / (double) tnw);
543 setupStepTables(rw);
544 setImage1(br, rw, rh);
545 }
546
547 static int
548 generateThumbnail(TIFF* in, TIFF* out)
549 {
550 unsigned char* raster;
551 unsigned char* rp;
552 uint32 sw, sh, rps;
553 uint16 bps, spp;
554 tsize_t rowsize, rastersize;
555 tstrip_t s, ns = TIFFNumberOfStrips(in);
556 uint32 diroff[1];
557
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)
564 return 0;
565 rowsize = TIFFScanlineSize(in);
566 rastersize = sh * rowsize;
567 fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
568 raster = (unsigned char*)_TIFFmalloc(rastersize);
569 if (!raster) {
570 TIFFError(TIFFFileName(in),
571 "Can't allocate space for raster buffer.");
572 return 0;
573 }
574 rp = raster;
575 for (s = 0; s < ns; s++) {
576 (void) TIFFReadEncodedStrip(in, s, rp, -1);
577 rp += rps * rowsize;
578 }
579 TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
580 setupCmap();
581 setImage(raster, sw, sh);
582 _TIFFfree(raster);
583
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);
597 diroff[0] = 0;
598 TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
599 return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
600 TIFFWriteDirectory(out) != -1);
601 }
602
603 char* stuff[] = {
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)",
608 "",
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",
616 NULL
617 };
618
619 static void
620 usage(void)
621 {
622 char buf[BUFSIZ];
623 int i;
624
625 setbuf(stderr, buf);
626 fprintf(stderr, "%s\n\n", TIFFGetVersion());
627 for (i = 0; stuff[i] != NULL; i++)
628 fprintf(stderr, "%s\n", stuff[i]);
629 exit(-1);
630 }
631
632 /* vim: set ts=8 sts=8 sw=8 noet: */