roentgen b75cab
/* $Id: thumbnail.c,v 1.16 2010-07-02 12:02:56 dron Exp $ */
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Copyright (c) 1994-1997 Sam Leffler
roentgen b75cab
 * Copyright (c) 1994-1997 Silicon Graphics, Inc.
roentgen b75cab
 *
roentgen b75cab
 * Permission to use, copy, modify, distribute, and sell this software and 
roentgen b75cab
 * its documentation for any purpose is hereby granted without fee, provided
roentgen b75cab
 * that (i) the above copyright notices and this permission notice appear in
roentgen b75cab
 * all copies of the software and related documentation, and (ii) the names of
roentgen b75cab
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
roentgen b75cab
 * publicity relating to the software without the specific, prior written
roentgen b75cab
 * permission of Sam Leffler and Silicon Graphics.
roentgen b75cab
 * 
roentgen b75cab
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
roentgen b75cab
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
roentgen b75cab
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
roentgen b75cab
 * 
roentgen b75cab
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
roentgen b75cab
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
roentgen b75cab
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
roentgen b75cab
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
roentgen b75cab
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
roentgen b75cab
 * OF THIS SOFTWARE.
roentgen b75cab
 */
roentgen b75cab
roentgen b75cab
#include "tif_config.h"
roentgen b75cab
roentgen b75cab
#include <stdio.h></stdio.h>
roentgen b75cab
#include <stdlib.h></stdlib.h>
roentgen b75cab
#include <string.h></string.h>
roentgen b75cab
#include <math.h></math.h>
roentgen b75cab
roentgen b75cab
#ifdef HAVE_UNISTD_H
roentgen b75cab
# include <unistd.h></unistd.h>
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
#ifdef NEED_LIBPORT
roentgen b75cab
# include "libport.h"
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
#include "tiffio.h"
roentgen b75cab
roentgen b75cab
#ifndef HAVE_GETOPT
roentgen b75cab
extern int getopt(int, char**, char*);
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
#define	streq(a,b)	(strcmp(a,b) == 0)
roentgen b75cab
roentgen b75cab
#ifndef TIFFhowmany8
roentgen b75cab
# define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
typedef enum {
roentgen b75cab
    EXP50,
roentgen b75cab
    EXP60,
roentgen b75cab
    EXP70,
roentgen b75cab
    EXP80,
roentgen b75cab
    EXP90,
roentgen b75cab
    EXP,
roentgen b75cab
    LINEAR
roentgen b75cab
} Contrast;
roentgen b75cab
roentgen b75cab
static	uint32 tnw = 216;		/* thumbnail width */
roentgen b75cab
static	uint32 tnh = 274;		/* thumbnail height */
roentgen b75cab
static	Contrast contrast = LINEAR;	/* current contrast */
roentgen b75cab
static	uint8* thumbnail;
roentgen b75cab
roentgen b75cab
static	int cpIFD(TIFF*, TIFF*);
roentgen b75cab
static	int generateThumbnail(TIFF*, TIFF*);
roentgen b75cab
static	void initScale();
roentgen b75cab
static	void usage(void);
roentgen b75cab
roentgen b75cab
extern	char* optarg;
roentgen b75cab
extern	int optind;
roentgen b75cab
roentgen b75cab
int
roentgen b75cab
main(int argc, char* argv[])
roentgen b75cab
{
roentgen b75cab
    TIFF* in;
roentgen b75cab
    TIFF* out;
roentgen b75cab
    int c;
roentgen b75cab
roentgen b75cab
    while ((c = getopt(argc, argv, "w:h:c:")) != -1) {
roentgen b75cab
	switch (c) {
roentgen b75cab
	case 'w':	tnw = strtoul(optarg, NULL, 0); break;
roentgen b75cab
	case 'h':	tnh = strtoul(optarg, NULL, 0); break;
roentgen b75cab
	case 'c':	contrast = streq(optarg, "exp50") ? EXP50 :
roentgen b75cab
				   streq(optarg, "exp60") ? EXP60 :
roentgen b75cab
				   streq(optarg, "exp70") ? EXP70 :
roentgen b75cab
				   streq(optarg, "exp80") ? EXP80 :
roentgen b75cab
				   streq(optarg, "exp90") ? EXP90 :
roentgen b75cab
				   streq(optarg, "exp")   ? EXP :
roentgen b75cab
				   streq(optarg, "linear")? LINEAR :
roentgen b75cab
							    EXP;
roentgen b75cab
			break;
roentgen b75cab
	default:	usage();
roentgen b75cab
	}
roentgen b75cab
    }
roentgen b75cab
    if (argc-optind != 2)
roentgen b75cab
	usage();
roentgen b75cab
roentgen b75cab
    out = TIFFOpen(argv[optind+1], "w");
roentgen b75cab
    if (out == NULL)
roentgen b75cab
	return 2;
roentgen b75cab
    in = TIFFOpen(argv[optind], "r");
roentgen b75cab
    if( in == NULL )
roentgen b75cab
        return 2;
roentgen b75cab
roentgen b75cab
    thumbnail = (uint8*) _TIFFmalloc(tnw * tnh);
roentgen b75cab
    if (!thumbnail) {
roentgen b75cab
	    TIFFError(TIFFFileName(in),
roentgen b75cab
		      "Can't allocate space for thumbnail buffer.");
roentgen b75cab
	    return 1;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
    if (in != NULL) {
roentgen b75cab
	initScale();
roentgen b75cab
	do {
roentgen b75cab
	    if (!generateThumbnail(in, out))
roentgen b75cab
		goto bad;
roentgen b75cab
	    if (!cpIFD(in, out) || !TIFFWriteDirectory(out))
roentgen b75cab
		goto bad;
roentgen b75cab
	} while (TIFFReadDirectory(in));
roentgen b75cab
	(void) TIFFClose(in);
roentgen b75cab
    }
roentgen b75cab
    (void) TIFFClose(out);
roentgen b75cab
    return 0;
roentgen b75cab
bad:
roentgen b75cab
    (void) TIFFClose(out);
roentgen b75cab
    return 1;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
#define	CopyField(tag, v) \
roentgen b75cab
    if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)
roentgen b75cab
#define	CopyField2(tag, v1, v2) \
roentgen b75cab
    if (TIFFGetField(in, tag, &v1, &v2)) TIFFSetField(out, tag, v1, v2)
roentgen b75cab
#define	CopyField3(tag, v1, v2, v3) \
roentgen b75cab
    if (TIFFGetField(in, tag, &v1, &v2, &v3)) TIFFSetField(out, tag, v1, v2, v3)
roentgen b75cab
#define	CopyField4(tag, v1, v2, v3, v4) \
roentgen b75cab
    if (TIFFGetField(in, tag, &v1, &v2, &v3, &v4)) TIFFSetField(out, tag, v1, v2, v3, v4)
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
cpTag(TIFF* in, TIFF* out, uint16 tag, uint16 count, TIFFDataType type)
roentgen b75cab
{
roentgen b75cab
	switch (type) {
roentgen b75cab
	case TIFF_SHORT:
roentgen b75cab
		if (count == 1) {
roentgen b75cab
			uint16 shortv;
roentgen b75cab
			CopyField(tag, shortv);
roentgen b75cab
		} else if (count == 2) {
roentgen b75cab
			uint16 shortv1, shortv2;
roentgen b75cab
			CopyField2(tag, shortv1, shortv2);
roentgen b75cab
		} else if (count == 4) {
roentgen b75cab
			uint16 *tr, *tg, *tb, *ta;
roentgen b75cab
			CopyField4(tag, tr, tg, tb, ta);
roentgen b75cab
		} else if (count == (uint16) -1) {
roentgen b75cab
			uint16 shortv1;
roentgen b75cab
			uint16* shortav;
roentgen b75cab
			CopyField2(tag, shortv1, shortav);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_LONG:
roentgen b75cab
		{ uint32 longv;
roentgen b75cab
		  CopyField(tag, longv);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_LONG8:
roentgen b75cab
		{ uint64 longv8;
roentgen b75cab
		  CopyField(tag, longv8);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_SLONG8:
roentgen b75cab
		{ int64 longv8;
roentgen b75cab
		  CopyField(tag, longv8);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_RATIONAL:
roentgen b75cab
		if (count == 1) {
roentgen b75cab
			float floatv;
roentgen b75cab
			CopyField(tag, floatv);
roentgen b75cab
		} else if (count == (uint16) -1) {
roentgen b75cab
			float* floatav;
roentgen b75cab
			CopyField(tag, floatav);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_ASCII:
roentgen b75cab
		{ char* stringv;
roentgen b75cab
		  CopyField(tag, stringv);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_DOUBLE:
roentgen b75cab
		if (count == 1) {
roentgen b75cab
			double doublev;
roentgen b75cab
			CopyField(tag, doublev);
roentgen b75cab
		} else if (count == (uint16) -1) {
roentgen b75cab
			double* doubleav;
roentgen b75cab
			CopyField(tag, doubleav);
roentgen b75cab
		}
roentgen b75cab
		break;
roentgen b75cab
	case TIFF_IFD8:
roentgen b75cab
		{ toff_t ifd8;
roentgen b75cab
		  CopyField(tag, ifd8);
roentgen b75cab
		}
roentgen b75cab
		break;          default:
roentgen b75cab
                TIFFError(TIFFFileName(in),
roentgen b75cab
                          "Data type %d is not supported, tag %d skipped.",
roentgen b75cab
                          tag, type);
roentgen b75cab
	}
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
#undef CopyField4
roentgen b75cab
#undef CopyField3
roentgen b75cab
#undef CopyField2
roentgen b75cab
#undef CopyField
roentgen b75cab
roentgen b75cab
static struct cpTag {
roentgen b75cab
    uint16	tag;
roentgen b75cab
    uint16	count;
roentgen b75cab
    TIFFDataType type;
roentgen b75cab
} tags[] = {
roentgen b75cab
    { TIFFTAG_IMAGEWIDTH,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_IMAGELENGTH,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_BITSPERSAMPLE,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_COMPRESSION,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_FILLORDER,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_SAMPLESPERPIXEL,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_ROWSPERSTRIP,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_PLANARCONFIG,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_GROUP3OPTIONS,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_SUBFILETYPE,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_PHOTOMETRIC,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_THRESHHOLDING,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_DOCUMENTNAME,		1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_IMAGEDESCRIPTION,		1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_MAKE,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_MODEL,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_ORIENTATION,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_MINSAMPLEVALUE,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_MAXSAMPLEVALUE,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_XRESOLUTION,		1, TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_YRESOLUTION,		1, TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_PAGENAME,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_XPOSITION,		1, TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_YPOSITION,		1, TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_GROUP4OPTIONS,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_RESOLUTIONUNIT,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_PAGENUMBER,		2, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_SOFTWARE,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_DATETIME,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_ARTIST,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_HOSTCOMPUTER,		1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_WHITEPOINT,		2, TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_PRIMARYCHROMATICITIES,	(uint16) -1,TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_HALFTONEHINTS,		2, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_BADFAXLINES,		1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_CLEANFAXDATA,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_CONSECUTIVEBADFAXLINES,	1, TIFF_LONG },
roentgen b75cab
    { TIFFTAG_INKSET,			1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_INKNAMES,			1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_DOTRANGE,			2, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_TARGETPRINTER,		1, TIFF_ASCII },
roentgen b75cab
    { TIFFTAG_SAMPLEFORMAT,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_YCBCRCOEFFICIENTS,	(uint16) -1,TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_YCBCRSUBSAMPLING,		2, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_YCBCRPOSITIONING,		1, TIFF_SHORT },
roentgen b75cab
    { TIFFTAG_REFERENCEBLACKWHITE,	(uint16) -1,TIFF_RATIONAL },
roentgen b75cab
    { TIFFTAG_EXTRASAMPLES,		(uint16) -1, TIFF_SHORT },
roentgen b75cab
};
roentgen b75cab
#define	NTAGS	(sizeof (tags) / sizeof (tags[0]))
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
cpTags(TIFF* in, TIFF* out)
roentgen b75cab
{
roentgen b75cab
    struct cpTag *p;
roentgen b75cab
    for (p = tags; p < &tags[NTAGS]; p++)
roentgen b75cab
	cpTag(in, out, p->tag, p->count, p->type);
roentgen b75cab
}
roentgen b75cab
#undef NTAGS
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
cpStrips(TIFF* in, TIFF* out)
roentgen b75cab
{
roentgen b75cab
    tsize_t bufsize  = TIFFStripSize(in);
roentgen b75cab
    unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
roentgen b75cab
roentgen b75cab
    if (buf) {
roentgen b75cab
	tstrip_t s, ns = TIFFNumberOfStrips(in);
roentgen b75cab
	uint64 *bytecounts;
roentgen b75cab
roentgen b75cab
	TIFFGetField(in, TIFFTAG_STRIPBYTECOUNTS, &bytecounts);
roentgen b75cab
	for (s = 0; s < ns; s++) {
roentgen b75cab
	  if (bytecounts[s] > (uint64) bufsize) {
roentgen b75cab
		buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[s]);
roentgen b75cab
		if (!buf)
roentgen b75cab
		    goto bad;
roentgen b75cab
		bufsize = (tmsize_t)bytecounts[s];
roentgen b75cab
	    }
roentgen b75cab
	    if (TIFFReadRawStrip(in, s, buf, (tmsize_t)bytecounts[s]) < 0 ||
roentgen b75cab
		TIFFWriteRawStrip(out, s, buf, (tmsize_t)bytecounts[s]) < 0) {
roentgen b75cab
		_TIFFfree(buf);
roentgen b75cab
		return 0;
roentgen b75cab
	    }
roentgen b75cab
	}
roentgen b75cab
	_TIFFfree(buf);
roentgen b75cab
	return 1;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
bad:
roentgen b75cab
	TIFFError(TIFFFileName(in),
roentgen b75cab
		  "Can't allocate space for strip buffer.");
roentgen b75cab
	return 0;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
cpTiles(TIFF* in, TIFF* out)
roentgen b75cab
{
roentgen b75cab
    tsize_t bufsize = TIFFTileSize(in);
roentgen b75cab
    unsigned char *buf = (unsigned char *)_TIFFmalloc(bufsize);
roentgen b75cab
roentgen b75cab
    if (buf) {
roentgen b75cab
	ttile_t t, nt = TIFFNumberOfTiles(in);
roentgen b75cab
	uint64 *bytecounts;
roentgen b75cab
roentgen b75cab
	TIFFGetField(in, TIFFTAG_TILEBYTECOUNTS, &bytecounts);
roentgen b75cab
	for (t = 0; t < nt; t++) {
roentgen b75cab
	    if (bytecounts[t] > (uint64) bufsize) {
roentgen b75cab
		buf = (unsigned char *)_TIFFrealloc(buf, (tmsize_t)bytecounts[t]);
roentgen b75cab
		if (!buf)
roentgen b75cab
		    goto bad;
roentgen b75cab
		bufsize = (tmsize_t)bytecounts[t];
roentgen b75cab
	    }
roentgen b75cab
	    if (TIFFReadRawTile(in, t, buf, (tmsize_t)bytecounts[t]) < 0 ||
roentgen b75cab
		TIFFWriteRawTile(out, t, buf, (tmsize_t)bytecounts[t]) < 0) {
roentgen b75cab
		_TIFFfree(buf);
roentgen b75cab
		return 0;
roentgen b75cab
	    }
roentgen b75cab
	}
roentgen b75cab
	_TIFFfree(buf);
roentgen b75cab
	return 1;
roentgen b75cab
    }
roentgen b75cab
roentgen b75cab
bad:
roentgen b75cab
    TIFFError(TIFFFileName(in),
roentgen b75cab
		  "Can't allocate space for tile buffer.");
roentgen b75cab
	return (0);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
cpIFD(TIFF* in, TIFF* out)
roentgen b75cab
{
roentgen b75cab
    cpTags(in, out);
roentgen b75cab
    if (TIFFIsTiled(in)) {
roentgen b75cab
	if (!cpTiles(in, out))
roentgen b75cab
	    return (0);
roentgen b75cab
    } else {
roentgen b75cab
	if (!cpStrips(in, out))
roentgen b75cab
	    return (0);
roentgen b75cab
    }
roentgen b75cab
    return (1);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static	uint16	photometric;		/* current photometric of raster */
roentgen b75cab
static	uint16	filterWidth;		/* filter width in pixels */
roentgen b75cab
static	uint32	stepSrcWidth;		/* src image stepping width */
roentgen b75cab
static	uint32	stepDstWidth;		/* dest stepping width */
roentgen b75cab
static	uint8* src0;			/* horizontal bit stepping (start) */
roentgen b75cab
static	uint8* src1;			/* horizontal bit stepping (middle) */
roentgen b75cab
static	uint8* src2;			/* horizontal bit stepping (end) */
roentgen b75cab
static	uint32* rowoff;			/* row offset for stepping */
roentgen b75cab
static	uint8 cmap[256];		/* colormap indexes */
roentgen b75cab
static	uint8 bits[256];		/* count of bits set */
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
setupBitsTables()
roentgen b75cab
{
roentgen b75cab
    int i;
roentgen b75cab
    for (i = 0; i < 256; i++) {
roentgen b75cab
	int n = 0;
roentgen b75cab
	if (i&0x01) n++;
roentgen b75cab
	if (i&0x02) n++;
roentgen b75cab
	if (i&0x04) n++;
roentgen b75cab
	if (i&0x08) n++;
roentgen b75cab
	if (i&0x10) n++;
roentgen b75cab
	if (i&0x20) n++;
roentgen b75cab
	if (i&0x40) n++;
roentgen b75cab
	if (i&0x80) n++;
roentgen b75cab
	bits[i] = n;
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int clamp(float v, int low, int high)
roentgen b75cab
    { return (v < low ? low : v > high ? high : (int)v); }
roentgen b75cab
roentgen b75cab
#ifndef M_E
roentgen b75cab
#define M_E		2.7182818284590452354
roentgen b75cab
#endif
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
expFill(float pct[], uint32 p, uint32 n)
roentgen b75cab
{
roentgen b75cab
    uint32 i;
roentgen b75cab
    uint32 c = (p * n) / 100;
roentgen b75cab
    for (i = 1; i < c; i++)
roentgen b75cab
	pct[i] = (float) (1-exp(i/((double)(n-1)))/ M_E);
roentgen b75cab
    for (; i < n; i++)
roentgen b75cab
	pct[i] = 0.;
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
setupCmap()
roentgen b75cab
{
roentgen b75cab
    float pct[256];			/* known to be large enough */
roentgen b75cab
    uint32 i;
roentgen b75cab
    pct[0] = 1;				/* force white */
roentgen b75cab
    switch (contrast) {
roentgen b75cab
    case EXP50: expFill(pct, 50, 256); break;
roentgen b75cab
    case EXP60:	expFill(pct, 60, 256); break;
roentgen b75cab
    case EXP70:	expFill(pct, 70, 256); break;
roentgen b75cab
    case EXP80:	expFill(pct, 80, 256); break;
roentgen b75cab
    case EXP90:	expFill(pct, 90, 256); break;
roentgen b75cab
    case EXP:	expFill(pct, 100, 256); break;
roentgen b75cab
    case LINEAR:
roentgen b75cab
	for (i = 1; i < 256; i++)
roentgen b75cab
	    pct[i] = 1-((float)i)/(256-1);
roentgen b75cab
	break;
roentgen b75cab
    }
roentgen b75cab
    switch (photometric) {
roentgen b75cab
    case PHOTOMETRIC_MINISWHITE:
roentgen b75cab
	for (i = 0; i < 256; i++)
roentgen b75cab
	    cmap[i] = clamp(255*pct[(256-1)-i], 0, 255);
roentgen b75cab
	break;
roentgen b75cab
    case PHOTOMETRIC_MINISBLACK:
roentgen b75cab
	for (i = 0; i < 256; i++)
roentgen b75cab
	    cmap[i] = clamp(255*pct[i], 0, 255);
roentgen b75cab
	break;
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
initScale()
roentgen b75cab
{
roentgen b75cab
    src0 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
roentgen b75cab
    src1 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
roentgen b75cab
    src2 = (uint8*) _TIFFmalloc(sizeof (uint8) * tnw);
roentgen b75cab
    rowoff = (uint32*) _TIFFmalloc(sizeof (uint32) * tnw);
roentgen b75cab
    filterWidth = 0;
roentgen b75cab
    stepDstWidth = stepSrcWidth = 0;
roentgen b75cab
    setupBitsTables();
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Calculate the horizontal accumulation parameteres
roentgen b75cab
 * according to the widths of the src and dst images.
roentgen b75cab
 */
roentgen b75cab
static void
roentgen b75cab
setupStepTables(uint32 sw)
roentgen b75cab
{
roentgen b75cab
    if (stepSrcWidth != sw || stepDstWidth != tnw) {
roentgen b75cab
	int step = sw;
roentgen b75cab
	int limit = tnw;
roentgen b75cab
	int err = 0;
roentgen b75cab
	uint32 sx = 0;
roentgen b75cab
	uint32 x;
roentgen b75cab
	int fw;
roentgen b75cab
	uint8 b;
roentgen b75cab
	for (x = 0; x < tnw; x++) {
roentgen b75cab
	    uint32 sx0 = sx;
roentgen b75cab
	    err += step;
roentgen b75cab
	    while (err >= limit) {
roentgen b75cab
		err -= limit;
roentgen b75cab
		sx++;
roentgen b75cab
	    }
roentgen b75cab
	    rowoff[x] = sx0 >> 3;
roentgen b75cab
	    fw = sx - sx0;		/* width */
roentgen b75cab
	    b = (fw < 8) ? 0xff<<(8-fw) : 0xff;
roentgen b75cab
	    src0[x] = b >> (sx0&7);
roentgen b75cab
	    fw -= 8 - (sx0&7);
roentgen b75cab
	    if (fw < 0)
roentgen b75cab
		fw = 0;
roentgen b75cab
	    src1[x] = fw >> 3;
roentgen b75cab
	    fw -= (fw>>3)<<3;
roentgen b75cab
	    src2[x] = 0xff << (8-fw);
roentgen b75cab
	}
roentgen b75cab
	stepSrcWidth = sw;
roentgen b75cab
	stepDstWidth = tnw;
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
setrow(uint8* row, uint32 nrows, const uint8* rows[])
roentgen b75cab
{
roentgen b75cab
    uint32 x;
roentgen b75cab
    uint32 area = nrows * filterWidth;
roentgen b75cab
    for (x = 0; x < tnw; x++) {
roentgen b75cab
	uint32 mask0 = src0[x];
roentgen b75cab
	uint32 fw = src1[x];
roentgen b75cab
	uint32 mask1 = src1[x];
roentgen b75cab
	uint32 off = rowoff[x];
roentgen b75cab
	uint32 acc = 0;
roentgen b75cab
	uint32 y, i;
roentgen b75cab
	for (y = 0; y < nrows; y++) {
roentgen b75cab
	    const uint8* src = rows[y] + off;
roentgen b75cab
	    acc += bits[*src++ & mask0];
roentgen b75cab
	    switch (fw) {
roentgen b75cab
	    default:
roentgen b75cab
		for (i = fw; i > 8; i--)
roentgen b75cab
		    acc += bits[*src++];
roentgen b75cab
		/* fall thru... */
roentgen b75cab
	    case 8: acc += bits[*src++];
roentgen b75cab
	    case 7: acc += bits[*src++];
roentgen b75cab
	    case 6: acc += bits[*src++];
roentgen b75cab
	    case 5: acc += bits[*src++];
roentgen b75cab
	    case 4: acc += bits[*src++];
roentgen b75cab
	    case 3: acc += bits[*src++];
roentgen b75cab
	    case 2: acc += bits[*src++];
roentgen b75cab
	    case 1: acc += bits[*src++];
roentgen b75cab
	    case 0: break;
roentgen b75cab
	    }
roentgen b75cab
	    acc += bits[*src & mask1];
roentgen b75cab
	}
roentgen b75cab
	*row++ = cmap[(255*acc)/area];
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/*
roentgen b75cab
 * Install the specified image.  The
roentgen b75cab
 * image is resized to fit the display page using
roentgen b75cab
 * a box filter.  The resultant pixels are mapped
roentgen b75cab
 * with a user-selectable contrast curve.
roentgen b75cab
 */
roentgen b75cab
static void
roentgen b75cab
setImage1(const uint8* br, uint32 rw, uint32 rh)
roentgen b75cab
{
roentgen b75cab
    int step = rh;
roentgen b75cab
    int limit = tnh;
roentgen b75cab
    int err = 0;
roentgen b75cab
    int bpr = TIFFhowmany8(rw);
roentgen b75cab
    int sy = 0;
roentgen b75cab
    uint8* row = thumbnail;
roentgen b75cab
    uint32 dy;
roentgen b75cab
    for (dy = 0; dy < tnh; dy++) {
roentgen b75cab
	const uint8* rows[256];
roentgen b75cab
	uint32 nrows = 1;
roentgen b75cab
	fprintf(stderr, "bpr=%d, sy=%d, bpr*sy=%d\n", bpr, sy, bpr*sy);
roentgen b75cab
	rows[0] = br + bpr*sy;
roentgen b75cab
	err += step;
roentgen b75cab
	while (err >= limit) {
roentgen b75cab
	    err -= limit;
roentgen b75cab
	    sy++;
roentgen b75cab
	    if (err >= limit)
roentgen b75cab
		rows[nrows++] = br + bpr*sy;
roentgen b75cab
	}
roentgen b75cab
	setrow(row, nrows, rows);
roentgen b75cab
	row += tnw;
roentgen b75cab
    }
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
setImage(const uint8* br, uint32 rw, uint32 rh)
roentgen b75cab
{
roentgen b75cab
    filterWidth = (uint16) ceil((double) rw / (double) tnw);
roentgen b75cab
    setupStepTables(rw);
roentgen b75cab
    setImage1(br, rw, rh);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
static int
roentgen b75cab
generateThumbnail(TIFF* in, TIFF* out)
roentgen b75cab
{
roentgen b75cab
    unsigned char* raster;
roentgen b75cab
    unsigned char* rp;
roentgen b75cab
    uint32 sw, sh, rps;
roentgen b75cab
    uint16 bps, spp;
roentgen b75cab
    tsize_t rowsize, rastersize;
roentgen b75cab
    tstrip_t s, ns = TIFFNumberOfStrips(in);
roentgen b75cab
    toff_t diroff[1];
roentgen b75cab
roentgen b75cab
    TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &sw);
roentgen b75cab
    TIFFGetField(in, TIFFTAG_IMAGELENGTH, &sh);
roentgen b75cab
    TIFFGetFieldDefaulted(in, TIFFTAG_BITSPERSAMPLE, &bps);
roentgen b75cab
    TIFFGetFieldDefaulted(in, TIFFTAG_SAMPLESPERPIXEL, &spp);
roentgen b75cab
    TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &rps);
roentgen b75cab
    if (spp != 1 || bps != 1)
roentgen b75cab
	return 0;
roentgen b75cab
    rowsize = TIFFScanlineSize(in);
roentgen b75cab
    rastersize = sh * rowsize;
roentgen b75cab
    fprintf(stderr, "rastersize=%u\n", (unsigned int)rastersize);
roentgen b75cab
    raster = (unsigned char*)_TIFFmalloc(rastersize);
roentgen b75cab
    if (!raster) {
roentgen b75cab
	    TIFFError(TIFFFileName(in),
roentgen b75cab
		      "Can't allocate space for raster buffer.");
roentgen b75cab
	    return 0;
roentgen b75cab
    }
roentgen b75cab
    rp = raster;
roentgen b75cab
    for (s = 0; s < ns; s++) {
roentgen b75cab
	(void) TIFFReadEncodedStrip(in, s, rp, -1);
roentgen b75cab
	rp += rps * rowsize;
roentgen b75cab
    }
roentgen b75cab
    TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric);
roentgen b75cab
    setupCmap();
roentgen b75cab
    setImage(raster, sw, sh);
roentgen b75cab
    _TIFFfree(raster);
roentgen b75cab
roentgen b75cab
    TIFFSetField(out, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_IMAGEWIDTH, (uint32) tnw);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_IMAGELENGTH, (uint32) tnh);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, (uint16) 8);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, (uint16) 1);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
roentgen b75cab
    TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
roentgen b75cab
    cpTag(in, out, TIFFTAG_SOFTWARE,		(uint16) -1, TIFF_ASCII);
roentgen b75cab
    cpTag(in, out, TIFFTAG_IMAGEDESCRIPTION,	(uint16) -1, TIFF_ASCII);
roentgen b75cab
    cpTag(in, out, TIFFTAG_DATETIME,		(uint16) -1, TIFF_ASCII);
roentgen b75cab
    cpTag(in, out, TIFFTAG_HOSTCOMPUTER,	(uint16) -1, TIFF_ASCII);
roentgen b75cab
    diroff[0] = 0UL;
roentgen b75cab
    TIFFSetField(out, TIFFTAG_SUBIFD, 1, diroff);
roentgen b75cab
    return (TIFFWriteEncodedStrip(out, 0, thumbnail, tnw*tnh) != -1 &&
roentgen b75cab
            TIFFWriteDirectory(out) != -1);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
char* stuff[] = {
roentgen b75cab
"usage: thumbnail [options] input.tif output.tif",
roentgen b75cab
"where options are:",
roentgen b75cab
" -h #		specify thumbnail image height (default is 274)",
roentgen b75cab
" -w #		specify thumbnail image width (default is 216)",
roentgen b75cab
"",
roentgen b75cab
" -c linear	use linear contrast curve",
roentgen b75cab
" -c exp50	use 50% exponential contrast curve",
roentgen b75cab
" -c exp60	use 60% exponential contrast curve",
roentgen b75cab
" -c exp70	use 70% exponential contrast curve",
roentgen b75cab
" -c exp80	use 80% exponential contrast curve",
roentgen b75cab
" -c exp90	use 90% exponential contrast curve",
roentgen b75cab
" -c exp		use pure exponential contrast curve",
roentgen b75cab
NULL
roentgen b75cab
};
roentgen b75cab
roentgen b75cab
static void
roentgen b75cab
usage(void)
roentgen b75cab
{
roentgen b75cab
	char buf[BUFSIZ];
roentgen b75cab
	int i;
roentgen b75cab
roentgen b75cab
	setbuf(stderr, buf);
roentgen b75cab
        fprintf(stderr, "%s\n\n", TIFFGetVersion());
roentgen b75cab
	for (i = 0; stuff[i] != NULL; i++)
roentgen b75cab
		fprintf(stderr, "%s\n", stuff[i]);
roentgen b75cab
	exit(-1);
roentgen b75cab
}
roentgen b75cab
roentgen b75cab
/* vim: set ts=8 sts=8 sw=8 noet: */
roentgen b75cab
/*
roentgen b75cab
 * Local Variables:
roentgen b75cab
 * mode: c
roentgen b75cab
 * c-basic-offset: 8
roentgen b75cab
 * fill-column: 78
roentgen b75cab
 * End:
roentgen b75cab
 */