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