]> git.saurik.com Git - wxWidgets.git/blob - src/tiff/tools/thumbnail.c
Fix harmless unused parameter warning in !wxUSE_GRAPHICS_CONTEXT build.
[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 #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, 2, 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 */