985 lines
23 KiB
C
985 lines
23 KiB
C
/* $Header: /cvs/maptools/cvsroot/libtiff/tools/Attic/sgigt.c,v 1.1 1999-07-27 21:50:28 mike Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1988-1997 Sam Leffler
|
|
* Copyright (c) 1991-1997 Silicon Graphics, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and
|
|
* its documentation for any purpose is hereby granted without fee, provided
|
|
* that (i) the above copyright notices and this permission notice appear in
|
|
* all copies of the software and related documentation, and (ii) the names of
|
|
* Sam Leffler and Silicon Graphics may not be used in any advertising or
|
|
* publicity relating to the software without the specific, prior written
|
|
* permission of Sam Leffler and Silicon Graphics.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
|
|
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
|
|
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
|
|
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
|
|
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
|
* OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <gl.h>
|
|
#include <device.h>
|
|
|
|
#include "tiffio.h"
|
|
|
|
#ifndef TRUE
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
/* XXX fudge adjustment for window borders */
|
|
#define YFUDGE 20
|
|
#define XFUDGE 20
|
|
|
|
static tileContigRoutine putContig;
|
|
static tileSeparateRoutine putSeparate;
|
|
static uint32 width, height; /* window width & height */
|
|
static uint32* raster = NULL; /* displayable image */
|
|
|
|
extern Colorindex greyi(int);
|
|
static void setupColormapSupport(TIFFRGBAImage*);
|
|
static void putContigAndDraw(TIFFRGBAImage*, uint32*,
|
|
uint32, uint32, uint32, uint32, int32, int32, unsigned char*);
|
|
static void putSeparateAndDraw(TIFFRGBAImage*, uint32*,
|
|
uint32, uint32, uint32, uint32, int32, int32,
|
|
unsigned char*, unsigned char*, unsigned char*, unsigned char*);
|
|
|
|
static int prevImage(char* argv[], int ix, int b, int e, int wrap);
|
|
static int nextImage(char* argv[], int ix, int b, int e, int wrap);
|
|
static void usage(void);
|
|
static uint16 photoArg(const char*);
|
|
static void beep(void);
|
|
|
|
extern char* optarg;
|
|
extern int optind;
|
|
|
|
int
|
|
main(int argc, char* argv[])
|
|
{
|
|
static Cursor hourglass = {
|
|
0x1ff0, 0x1ff0, 0x0820, 0x0820,
|
|
0x0820, 0x0c60, 0x06c0, 0x0100,
|
|
0x0100, 0x06c0, 0x0c60, 0x0820,
|
|
0x0820, 0x0820, 0x1ff0, 0x1ff0
|
|
};
|
|
int isRGB0 = -1, isRGB;
|
|
int verbose = 0;
|
|
int stoponerr = 0; /* stop on read error */
|
|
char* filename;
|
|
TIFF* tif = NULL;
|
|
int fg = 0;
|
|
int c;
|
|
int dirnum = -1;
|
|
int order0 = 0, order;
|
|
uint32 diroff = 0;
|
|
uint16 photo0 = (uint16) -1, photo;
|
|
long x, y, xmax, ymax;
|
|
int ix, nix;
|
|
TIFFErrorHandler oerror = TIFFSetErrorHandler(NULL);
|
|
TIFFErrorHandler owarning = TIFFSetWarningHandler(NULL);
|
|
uint32 w, h;
|
|
long wid = -1;
|
|
|
|
while ((c = getopt(argc, argv, "d:o:p:cerflmsvw")) != -1)
|
|
switch (c) {
|
|
case 'c':
|
|
isRGB0 = 0;
|
|
break;
|
|
case 'd':
|
|
dirnum = atoi(optarg);
|
|
break;
|
|
case 'e':
|
|
oerror = TIFFSetErrorHandler(oerror);
|
|
break;
|
|
case 'f':
|
|
fg = 1;
|
|
break;
|
|
case 'l':
|
|
order0 = FILLORDER_LSB2MSB;
|
|
break;
|
|
case 'm':
|
|
order0 = FILLORDER_MSB2LSB;
|
|
break;
|
|
case 'o':
|
|
diroff = strtoul(optarg, NULL, 0);
|
|
break;
|
|
case 'p':
|
|
photo0 = photoArg(optarg);
|
|
break;
|
|
case 'r':
|
|
isRGB0 = 1;
|
|
break;
|
|
case 's':
|
|
stoponerr = 1;
|
|
break;
|
|
case 'w':
|
|
owarning = TIFFSetWarningHandler(owarning);
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case '?':
|
|
usage();
|
|
/*NOTREACHED*/
|
|
}
|
|
if (argc - optind < 1)
|
|
usage();
|
|
xmax = getgdesc(GD_XPMAX) - XFUDGE;
|
|
ymax = getgdesc(GD_YPMAX) - YFUDGE;
|
|
ix = optind;
|
|
do {
|
|
tif = TIFFOpen(argv[ix], "r");
|
|
} while (tif == NULL && (ix = nextImage(argv, ix, optind, argc, FALSE)));
|
|
if (tif == NULL)
|
|
exit(0);
|
|
if (ix == optind) {
|
|
/*
|
|
* Set initial directory if user-specified
|
|
* file was opened successfully.
|
|
*/
|
|
if (dirnum != -1 && !TIFFSetDirectory(tif, dirnum))
|
|
TIFFError(argv[ix], "Error, seeking to directory %d", dirnum);
|
|
if (diroff != 0 && !TIFFSetSubDirectory(tif, diroff))
|
|
TIFFError(argv[ix], "Error, setting subdirectory at %#x", diroff);
|
|
}
|
|
isRGB = isRGB0;
|
|
order = order0;
|
|
photo = photo0;
|
|
goto newfile0;
|
|
for (;;) {
|
|
TIFFRGBAImage img;
|
|
char title[1024]; /* window title line */
|
|
const char* cp;
|
|
int isrgb;
|
|
|
|
if (order)
|
|
TIFFSetField(tif, TIFFTAG_FILLORDER, order);
|
|
if (photo != (uint16) -1)
|
|
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photo);
|
|
if (!TIFFRGBAImageBegin(&img, tif, stoponerr, title)) {
|
|
TIFFError(filename, title);
|
|
goto bad2;
|
|
}
|
|
/*
|
|
* Use a full-color window if the image is
|
|
* full color or a palette image and the
|
|
* hardware support is present.
|
|
*/
|
|
isrgb = isRGB;
|
|
if (isrgb == -1)
|
|
isrgb = (img.bitspersample >= 8 &&
|
|
(img.photometric == PHOTOMETRIC_RGB ||
|
|
img.photometric == PHOTOMETRIC_YCBCR ||
|
|
img.photometric == PHOTOMETRIC_SEPARATED ||
|
|
img.photometric == PHOTOMETRIC_PALETTE ||
|
|
img.photometric == PHOTOMETRIC_LOGLUV));
|
|
/*
|
|
* Check to see if the hardware can display 24-bit RGB.
|
|
*/
|
|
if (isrgb && getgdesc(GD_BITS_NORM_SNG_RED) < img.bitspersample &&
|
|
!getgdesc(GD_DITHER)) {
|
|
if (verbose)
|
|
printf("Warning, display is incapable of full RGB,%s\n",
|
|
" using dithered colormap");
|
|
isrgb = 0;
|
|
}
|
|
/*
|
|
* Colormap-based display is done by overriding the put
|
|
* routine to install a private method that understands
|
|
* how to convert RGBA values to suitable colormap indices.
|
|
*/
|
|
if (!isrgb)
|
|
setupColormapSupport(&img);
|
|
/*
|
|
* Override default ``put routine'' with private
|
|
* routine that also draws the raster on the display.
|
|
*/
|
|
if (img.put.any == 0) {
|
|
TIFFError(filename,
|
|
"No \"put\" routine; must not handle image format");
|
|
goto bad3;
|
|
}
|
|
if (img.isContig) {
|
|
putContig = img.put.contig;
|
|
img.put.contig = putContigAndDraw;
|
|
} else {
|
|
putSeparate = img.put.separate;
|
|
img.put.separate = putSeparateAndDraw;
|
|
}
|
|
/*
|
|
* Setup the image raster as required.
|
|
*/
|
|
if ((w = img.width) > xmax)
|
|
w = xmax;
|
|
if ((h = img.height) > ymax)
|
|
h = ymax;
|
|
if (w != width || h != height) {
|
|
if (raster != NULL)
|
|
_TIFFfree(raster), raster = NULL;
|
|
raster = (uint32*) _TIFFmalloc(w * h * sizeof (uint32));
|
|
if (raster == NULL) {
|
|
width = height = 0;
|
|
TIFFError(filename, "No space for raster buffer");
|
|
goto bad3;
|
|
}
|
|
width = w;
|
|
height = h;
|
|
}
|
|
/*
|
|
* Create a new window or reconfigure an existing
|
|
* one to suit the image to be displayed.
|
|
*/
|
|
if (wid < 0) {
|
|
x = (xmax+XFUDGE-width)/2;
|
|
y = (ymax+YFUDGE-height)/2;
|
|
prefposition(x, x+width-1, y, y+height-1);
|
|
cp = strrchr(filename, '/');
|
|
sprintf(title, "%s [%u] %s",
|
|
cp == NULL ? filename : cp+1,
|
|
(unsigned int) TIFFCurrentDirectory(tif),
|
|
isrgb ? " rgb" : " cmap");
|
|
if (fg)
|
|
foreground();
|
|
wid = winopen(title);
|
|
if (wid < 0) {
|
|
TIFFError(filename, "Can not create window");
|
|
TIFFRGBAImageEnd(&img);
|
|
break;
|
|
}
|
|
curstype(C16X1);
|
|
defcursor(1, hourglass);
|
|
qdevice(LEFTMOUSE);
|
|
qdevice(MIDDLEMOUSE);
|
|
qdevice(RIGHTMOUSE);
|
|
qdevice(KEYBD);
|
|
qdevice(PAGEUPKEY);
|
|
qdevice(PAGEDOWNKEY);
|
|
qdevice(HOMEKEY);
|
|
qdevice(ENDKEY);
|
|
} else {
|
|
x = (xmax+XFUDGE-width)/2;
|
|
y = (ymax+YFUDGE-height)/2;
|
|
winposition(x, x+width-1, y, y+height-1);
|
|
viewport(0, width-1, 0, height-1);
|
|
cp = strrchr(filename, '/');
|
|
sprintf(title, "%s [%u] %s",
|
|
cp == NULL ? filename : cp+1,
|
|
(unsigned int) TIFFCurrentDirectory(tif),
|
|
isrgb ? " rgb" : " cmap");
|
|
wintitle(title);
|
|
}
|
|
singlebuffer();
|
|
if (isrgb) {
|
|
RGBmode();
|
|
gconfig();
|
|
} else {
|
|
cmode();
|
|
gconfig();
|
|
}
|
|
/*
|
|
* Fetch the image.
|
|
*/
|
|
setcursor(1, 0, 0);
|
|
greyi(225);
|
|
clear();
|
|
(void) TIFFRGBAImageGet(&img, raster, width, height);
|
|
setcursor(0, 0, 0);
|
|
/*
|
|
* Process input.
|
|
*/
|
|
for (;;) {
|
|
short val;
|
|
switch (qread(&val)) {
|
|
case KEYBD:
|
|
switch (val) {
|
|
case 'b': /* photometric MinIsBlack */
|
|
photo = PHOTOMETRIC_MINISBLACK;
|
|
goto newpage;
|
|
case 'l': /* lsb-to-msb FillOrder */
|
|
order = FILLORDER_LSB2MSB;
|
|
goto newpage;
|
|
case 'm': /* msb-to-lsb FillOrder */
|
|
order = FILLORDER_MSB2LSB;
|
|
goto newpage;
|
|
case 'c': /* colormap visual */
|
|
isRGB = 0;
|
|
goto newpage;
|
|
case 'r': /* RGB visual */
|
|
isRGB = 1;
|
|
goto newpage;
|
|
case 'w': /* photometric MinIsWhite */
|
|
photo = PHOTOMETRIC_MINISWHITE;
|
|
goto newpage;
|
|
case 'W': /* toggle warnings */
|
|
owarning = TIFFSetWarningHandler(owarning);
|
|
goto newpage;
|
|
case 'E': /* toggle errors */
|
|
oerror = TIFFSetErrorHandler(oerror);
|
|
goto newpage;
|
|
case 'z': /* reset to defaults */
|
|
case 'Z':
|
|
order = order0;
|
|
photo = photo0;
|
|
isRGB = isRGB0;
|
|
if (owarning == NULL)
|
|
owarning = TIFFSetWarningHandler(NULL);
|
|
if (oerror == NULL)
|
|
oerror = TIFFSetErrorHandler(NULL);
|
|
goto newpage;
|
|
case 'q': /* exit */
|
|
case '\033':
|
|
TIFFRGBAImageEnd(&img);
|
|
goto done;
|
|
}
|
|
break;
|
|
case PAGEUPKEY: /* previous logical image */
|
|
if (val) {
|
|
if (TIFFCurrentDirectory(tif) > 0) {
|
|
if (TIFFSetDirectory(tif, TIFFCurrentDirectory(tif)-1))
|
|
goto newpage;
|
|
beep(); /* XXX */
|
|
} else {
|
|
ix = prevImage(argv, ix, optind, argc, TRUE);
|
|
/* XXX set directory to last image in new file */
|
|
goto newfile;
|
|
}
|
|
}
|
|
break;
|
|
case PAGEDOWNKEY: /* next logical image */
|
|
if (val) {
|
|
if (!TIFFLastDirectory(tif)) {
|
|
if (TIFFReadDirectory(tif))
|
|
goto newpage;
|
|
beep(); /* XXX */
|
|
} else {
|
|
ix = nextImage(argv, ix, optind, argc, TRUE);
|
|
goto newfile;
|
|
}
|
|
}
|
|
break;
|
|
case HOMEKEY: /* 1st image in current file */
|
|
if (val) {
|
|
if (TIFFSetDirectory(tif, 0))
|
|
goto newpage;
|
|
beep();
|
|
}
|
|
break;
|
|
case ENDKEY: /* last image in current file */
|
|
if (val) {
|
|
/* XXX */
|
|
beep();
|
|
}
|
|
break;
|
|
case RIGHTMOUSE: /* previous file */
|
|
if (val) {
|
|
if (nix = prevImage(argv, ix, optind, argc, FALSE)) {
|
|
ix = nix;
|
|
goto newfile;
|
|
}
|
|
beep();
|
|
}
|
|
break;
|
|
case LEFTMOUSE: /* next file */
|
|
if (val) {
|
|
if (nix = nextImage(argv, ix, optind, argc, FALSE)) {
|
|
ix = nix;
|
|
goto newfile;
|
|
}
|
|
beep();
|
|
}
|
|
break;
|
|
case MIDDLEMOUSE: /* first file */
|
|
if (val) {
|
|
if (nix = nextImage(argv, optind-1, optind, argc, FALSE)) {
|
|
ix = nix;
|
|
goto newfile;
|
|
}
|
|
beep();
|
|
}
|
|
break;
|
|
case REDRAW:
|
|
lrectwrite(0, 0, width-1, height-1, raster);
|
|
break;
|
|
}
|
|
}
|
|
newfile:
|
|
TIFFRGBAImageEnd(&img);
|
|
if (tif != NULL && argv[ix] != filename)
|
|
TIFFClose(tif), tif = NULL;
|
|
/* fall thru... */
|
|
newfile0:
|
|
if (argv[ix] == NULL)
|
|
break;
|
|
filename = argv[ix];
|
|
if (tif == NULL) {
|
|
tif = TIFFOpen(filename, "r");
|
|
if (tif == NULL)
|
|
goto bad1;
|
|
isRGB = isRGB0;
|
|
order = order0;
|
|
photo = photo0;
|
|
}
|
|
continue;
|
|
newpage:
|
|
TIFFRGBAImageEnd(&img);
|
|
continue;
|
|
bad3:
|
|
TIFFRGBAImageEnd(&img);
|
|
bad2:
|
|
TIFFClose(tif), tif = NULL;
|
|
bad1:
|
|
argv[ix] = NULL; /* don't revisit file */
|
|
ix = nextImage(argv, ix, optind, argc, TRUE);
|
|
goto newfile0;
|
|
}
|
|
done:
|
|
if (wid >= 0)
|
|
winclose(wid);
|
|
if (raster != NULL)
|
|
_TIFFfree(raster);
|
|
if (tif != NULL)
|
|
TIFFClose(tif);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
prevImage(char* argv[], int ix, int b, int e, int wrap)
|
|
{
|
|
int i;
|
|
|
|
for (i = ix-1; i >= b && argv[i] == NULL; i--)
|
|
;
|
|
if (i < b) {
|
|
if (wrap) {
|
|
for (i = e-1; i > ix && argv[i] == NULL; i--)
|
|
;
|
|
} else
|
|
i = 0;
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
static int
|
|
nextImage(char* argv[], int ix, int b, int e, int wrap)
|
|
{
|
|
int i;
|
|
|
|
for (i = ix+1; i < e && argv[i] == NULL; i++)
|
|
;
|
|
if (i >= e) {
|
|
if (wrap) {
|
|
for (i = b; i < ix && argv[i] == NULL; i++)
|
|
;
|
|
} else
|
|
i = 0;
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
static void
|
|
beep(void)
|
|
{
|
|
greyi(0);
|
|
clear();
|
|
sginap(5);
|
|
lrectwrite(0, 0, width-1, height-1, raster);
|
|
}
|
|
|
|
char* stuff[] = {
|
|
"usage: tiffgt [options] file.tif",
|
|
"where options are:",
|
|
" -c use colormap visual",
|
|
" -d dirnum set initial directory (default is 0)",
|
|
" -e enable display of TIFF error messages",
|
|
" -f run program in the foreground",
|
|
" -l force lsb-to-msb FillOrder",
|
|
" -m force msb-to-lsb FillOrder",
|
|
" -o offset set initial directory offset",
|
|
" -p photo override photometric interpretation",
|
|
" -r use fullcolor visual",
|
|
" -s stop decoding on first error (default is ignore errors)",
|
|
" -v enable verbose mode",
|
|
" -w enable display of TIFF warning messages",
|
|
NULL
|
|
};
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
char buf[BUFSIZ];
|
|
int i;
|
|
|
|
setbuf(stderr, buf);
|
|
for (i = 0; stuff[i] != NULL; i++)
|
|
fprintf(stderr, "%s\n", stuff[i]);
|
|
exit(-1);
|
|
}
|
|
|
|
static uint16
|
|
photoArg(const char* arg)
|
|
{
|
|
if (strcmp(arg, "miniswhite") == 0)
|
|
return (PHOTOMETRIC_MINISWHITE);
|
|
else if (strcmp(arg, "minisblack") == 0)
|
|
return (PHOTOMETRIC_MINISBLACK);
|
|
else if (strcmp(arg, "rgb") == 0)
|
|
return (PHOTOMETRIC_RGB);
|
|
else if (strcmp(arg, "palette") == 0)
|
|
return (PHOTOMETRIC_PALETTE);
|
|
else if (strcmp(arg, "mask") == 0)
|
|
return (PHOTOMETRIC_MASK);
|
|
else if (strcmp(arg, "separated") == 0)
|
|
return (PHOTOMETRIC_SEPARATED);
|
|
else if (strcmp(arg, "ycbcr") == 0)
|
|
return (PHOTOMETRIC_YCBCR);
|
|
else if (strcmp(arg, "cielab") == 0)
|
|
return (PHOTOMETRIC_CIELAB);
|
|
else if (strcmp(arg, "logl") == 0)
|
|
return (PHOTOMETRIC_LOGL);
|
|
else if (strcmp(arg, "logluv") == 0)
|
|
return (PHOTOMETRIC_LOGLUV);
|
|
else
|
|
return ((uint16) -1);
|
|
}
|
|
|
|
static void
|
|
putContigAndDraw(TIFFRGBAImage* img, uint32* raster,
|
|
uint32 x, uint32 y, uint32 w, uint32 h,
|
|
int32 fromskew, int32 toskew,
|
|
unsigned char* cp)
|
|
{
|
|
(*putContig)(img, raster, x, y, w, h, fromskew, toskew, cp);
|
|
if (x+w == width) {
|
|
w = width;
|
|
if (img->orientation == ORIENTATION_TOPLEFT)
|
|
lrectwrite(0, y-(h-1), w-1, y, raster-x-(h-1)*w);
|
|
else
|
|
lrectwrite(0, y, w-1, y+h-1, raster);
|
|
}
|
|
}
|
|
|
|
static void
|
|
putSeparateAndDraw(TIFFRGBAImage* img, uint32* raster,
|
|
uint32 x, uint32 y, uint32 w, uint32 h,
|
|
int32 fromskew, int32 toskew,
|
|
unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a)
|
|
{
|
|
(*putSeparate)(img, raster, x, y, w, h, fromskew, toskew, r, g, b, a);
|
|
if (x+w == width) {
|
|
w = width;
|
|
if (img->orientation == ORIENTATION_TOPLEFT)
|
|
lrectwrite(0, y-(h-1), w-1, y, raster-x-(h-1)*w);
|
|
else
|
|
lrectwrite(0, y, w-1, y+h-1, raster);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* {red,green,blue}_inverse are tables in libgutil.a that
|
|
* do an inverse map from (r,g,b) to the closest colormap
|
|
* index in the "standard" GL colormap. grey_inverse is
|
|
* the equivalent map for mapping greyscale values to
|
|
* colormap indices. We access these maps directly instead
|
|
* of through the rgbi and greyi functions to avoid the
|
|
* additional overhead of the color calls that they make.
|
|
*/
|
|
extern u_char red_inverse[256];
|
|
extern u_char green_inverse[256];
|
|
extern u_char blue_inverse[256];
|
|
extern u_char grey_inverse[256];
|
|
#define greyi(g) grey_inverse[g]
|
|
|
|
static u_char
|
|
rgbi(u_char r, u_char g, u_char b)
|
|
{
|
|
return (r == g && g == b ? grey_inverse[r] :
|
|
red_inverse[r] + green_inverse[g] + blue_inverse[b]);
|
|
}
|
|
|
|
/*
|
|
* The following routines move decoded data returned
|
|
* from the TIFF library into rasters that are suitable
|
|
* for passing to lrecwrite. They do the necessary
|
|
* conversions for when a colormap drawing mode is used.
|
|
*/
|
|
#define REPEAT8(op) REPEAT4(op); REPEAT4(op)
|
|
#define REPEAT4(op) REPEAT2(op); REPEAT2(op)
|
|
#define REPEAT2(op) op; op
|
|
#define CASE8(x,op) \
|
|
switch (x) { \
|
|
case 7: op; case 6: op; case 5: op; \
|
|
case 4: op; case 3: op; case 2: op; \
|
|
case 1: op; \
|
|
}
|
|
#define CASE4(x,op) switch (x) { case 3: op; case 2: op; case 1: op; }
|
|
#define NOP
|
|
|
|
#define UNROLL8(w, op1, op2) { \
|
|
uint32 _x; \
|
|
for (_x = w; _x >= 8; _x -= 8) { \
|
|
op1; \
|
|
REPEAT8(op2); \
|
|
} \
|
|
if (_x > 0) { \
|
|
op1; \
|
|
CASE8(_x,op2); \
|
|
} \
|
|
}
|
|
#define UNROLL4(w, op1, op2) { \
|
|
uint32 _x; \
|
|
for (_x = w; _x >= 4; _x -= 4) { \
|
|
op1; \
|
|
REPEAT4(op2); \
|
|
} \
|
|
if (_x > 0) { \
|
|
op1; \
|
|
CASE4(_x,op2); \
|
|
} \
|
|
}
|
|
#define UNROLL2(w, op1, op2) { \
|
|
uint32 _x; \
|
|
for (_x = w; _x >= 2; _x -= 2) { \
|
|
op1; \
|
|
REPEAT2(op2); \
|
|
} \
|
|
if (_x) { \
|
|
op1; \
|
|
op2; \
|
|
} \
|
|
}
|
|
|
|
#define SKEW(r,g,b,skew) { r += skew; g += skew; b += skew; }
|
|
|
|
#define DECLAREContigPutFunc(name) \
|
|
static void name(\
|
|
TIFFRGBAImage* img, \
|
|
uint32* cp, \
|
|
uint32 x, uint32 y, \
|
|
uint32 w, uint32 h, \
|
|
int32 fromskew, int32 toskew, \
|
|
u_char* pp \
|
|
)
|
|
|
|
#define DECLARESepPutFunc(name) \
|
|
static void name(\
|
|
TIFFRGBAImage* img,\
|
|
uint32* cp,\
|
|
uint32 x, uint32 y, \
|
|
uint32 w, uint32 h,\
|
|
int32 fromskew, int32 toskew,\
|
|
u_char* r, u_char* g, u_char* b, u_char* a\
|
|
)
|
|
|
|
static tileContigRoutine libput;
|
|
|
|
/*
|
|
* 8-bit packed samples => colormap
|
|
*/
|
|
DECLAREContigPutFunc(putcontig8bittile)
|
|
{
|
|
int samplesperpixel = img->samplesperpixel;
|
|
TIFFRGBValue* Map = img->Map;
|
|
|
|
(void) y;
|
|
fromskew *= samplesperpixel;
|
|
if (Map) {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;) {
|
|
*cp++ = rgbi(Map[pp[0]], Map[pp[1]], Map[pp[2]]);
|
|
pp += samplesperpixel;
|
|
}
|
|
cp += toskew;
|
|
pp += fromskew;
|
|
}
|
|
} else {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;) {
|
|
*cp++ = rgbi(pp[0], pp[1], pp[2]);
|
|
pp += samplesperpixel;
|
|
}
|
|
cp += toskew;
|
|
pp += fromskew;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert 8-bit packed samples => colormap
|
|
*/
|
|
DECLAREContigPutFunc(cvtcontig8bittile)
|
|
{
|
|
(*libput)(img, cp, x, y, w, h, fromskew, toskew, pp);
|
|
while (h-- > 0) {
|
|
UNROLL8(w, NOP,
|
|
cp[0] = rgbi(TIFFGetR(cp[0]),TIFFGetG(cp[0]),TIFFGetB(cp[0])); cp++
|
|
);
|
|
cp += toskew;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 16-bit packed samples => colormap
|
|
*/
|
|
DECLAREContigPutFunc(putcontig16bittile)
|
|
{
|
|
int samplesperpixel = img->samplesperpixel;
|
|
TIFFRGBValue* Map = img->Map;
|
|
|
|
(void) y;
|
|
fromskew *= samplesperpixel;
|
|
if (Map) {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;) {
|
|
*cp++ = rgbi(Map[pp[0]], Map[pp[1]], Map[pp[2]]);
|
|
pp += samplesperpixel;
|
|
}
|
|
cp += toskew;
|
|
pp += fromskew;
|
|
}
|
|
} else {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;) {
|
|
*cp++ = rgbi(pp[0], pp[1], pp[2]);
|
|
pp += samplesperpixel;
|
|
}
|
|
cp += toskew;
|
|
pp += fromskew;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 8-bit unpacked samples => colormap
|
|
*/
|
|
DECLARESepPutFunc(putseparate8bittile)
|
|
{
|
|
TIFFRGBValue* Map = img->Map;
|
|
|
|
(void) y; (void) a;
|
|
if (Map) {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;)
|
|
*cp++ = rgbi(Map[*r++], Map[*g++], Map[*b++]);
|
|
SKEW(r, g, b, fromskew);
|
|
cp += toskew;
|
|
}
|
|
} else {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;)
|
|
*cp++ = rgbi(*r++, *g++, *b++);
|
|
SKEW(r, g, b, fromskew);
|
|
cp += toskew;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 16-bit unpacked samples => colormap
|
|
*/
|
|
DECLARESepPutFunc(putseparate16bittile)
|
|
{
|
|
TIFFRGBValue* Map = img->Map;
|
|
|
|
(void) y; (void) a;
|
|
if (Map) {
|
|
while (h-- > 0) {
|
|
for (x = 0; x < w; x++)
|
|
*cp++ = rgbi(Map[*r++], Map[*g++], Map[*b++]);
|
|
SKEW(r, g, b, fromskew);
|
|
cp += toskew;
|
|
}
|
|
} else {
|
|
while (h-- > 0) {
|
|
for (x = 0; x < w; x++)
|
|
*cp++ = rgbi(*r++, *g++, *b++);
|
|
SKEW(r, g, b, fromskew);
|
|
cp += toskew;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 8-bit packed CMYK samples => cmap
|
|
*
|
|
* NB: The conversion of CMYK->RGB is *very* crude.
|
|
*/
|
|
DECLAREContigPutFunc(putcontig8bitCMYKtile)
|
|
{
|
|
int samplesperpixel = img->samplesperpixel;
|
|
TIFFRGBValue* Map = img->Map;
|
|
uint16 r, g, b, k;
|
|
|
|
(void) y;
|
|
fromskew *= samplesperpixel;
|
|
if (Map) {
|
|
while (h-- > 0) {
|
|
for (x = w; x-- > 0;) {
|
|
k = 255 - pp[3];
|
|
r = (k*(255-pp[0]))/255;
|
|
g = (k*(255-pp[1]))/255;
|
|
b = (k*(255-pp[2]))/255;
|
|
*cp++ = rgbi(Map[r], Map[g], Map[b]);
|
|
pp += samplesperpixel;
|
|
}
|
|
pp += fromskew;
|
|
cp += toskew;
|
|
}
|
|
} else {
|
|
while (h-- > 0) {
|
|
UNROLL8(w, NOP,
|
|
k = 255 - pp[3];
|
|
r = (k*(255-pp[0]))/255;
|
|
g = (k*(255-pp[1]))/255;
|
|
b = (k*(255-pp[2]))/255;
|
|
*cp++ = rgbi(r, g, b);
|
|
pp += samplesperpixel);
|
|
cp += toskew;
|
|
pp += fromskew;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define YCbCrtoRGB(dst, yc) { \
|
|
int Y = (yc); \
|
|
dst = rgbi( \
|
|
clamptab[Y+Crrtab[Cr]], \
|
|
clamptab[Y + (int)((Cbgtab[Cb]+Crgtab[Cr])>>16)], \
|
|
clamptab[Y+Cbbtab[Cb]]); \
|
|
}
|
|
#define YCbCrSetup \
|
|
TIFFYCbCrToRGB* ycbcr = img->ycbcr; \
|
|
int* Crrtab = ycbcr->Cr_r_tab; \
|
|
int* Cbbtab = ycbcr->Cb_b_tab; \
|
|
int32* Crgtab = ycbcr->Cr_g_tab; \
|
|
int32* Cbgtab = ycbcr->Cb_g_tab; \
|
|
TIFFRGBValue* clamptab = ycbcr->clamptab
|
|
|
|
/*
|
|
* 8-bit packed YCbCr samples w/ 2,2 subsampling => RGB
|
|
*/
|
|
DECLAREContigPutFunc(putcontig8bitYCbCr22tile)
|
|
{
|
|
YCbCrSetup;
|
|
uint32* cp1 = cp+w+toskew;
|
|
int32 incr = 2*toskew+w;
|
|
|
|
(void) y;
|
|
/* XXX adjust fromskew */
|
|
for (; h >= 2; h -= 2) {
|
|
x = w>>1;
|
|
do {
|
|
int Cb = pp[4];
|
|
int Cr = pp[5];
|
|
|
|
YCbCrtoRGB(cp [0], pp[0]);
|
|
YCbCrtoRGB(cp [1], pp[1]);
|
|
YCbCrtoRGB(cp1[0], pp[2]);
|
|
YCbCrtoRGB(cp1[1], pp[3]);
|
|
|
|
cp += 2, cp1 += 2;
|
|
pp += 6;
|
|
} while (--x);
|
|
cp += incr, cp1 += incr;
|
|
pp += fromskew;
|
|
}
|
|
}
|
|
#undef YCbCrSetup
|
|
#undef YCbCrtoRGB
|
|
|
|
/*
|
|
* Setup to handle conversion for display in a colormap
|
|
* window. Many cases are handled by massaging the mapping
|
|
* tables used by the normal library code to convert 32-bit
|
|
* packed RGBA samples into colormap indices. Other cases
|
|
* are handled with special-case routines that replace the
|
|
* normal ``put routine'' installed by the library.
|
|
*/
|
|
static void
|
|
setupColormapSupport(TIFFRGBAImage* img)
|
|
{
|
|
int bitspersample = img->bitspersample;
|
|
int i;
|
|
|
|
if (img->BWmap) {
|
|
i = 255;
|
|
do {
|
|
uint32* p = img->BWmap[i];
|
|
switch (bitspersample) {
|
|
#define GREY(x) p[x] = greyi(TIFFGetR(p[x]))
|
|
case 1: GREY(7); GREY(6); GREY(5); GREY(4);
|
|
case 2: GREY(3); GREY(2);
|
|
case 4: GREY(1);
|
|
case 8: GREY(0);
|
|
}
|
|
#undef GREY
|
|
} while (i--);
|
|
} else if (img->PALmap) {
|
|
i = 255;
|
|
do {
|
|
uint32 rgb;
|
|
uint32* p = img->PALmap[i];
|
|
#define CMAP(x) \
|
|
(rgb = p[x], p[x] = rgbi(TIFFGetR(rgb),TIFFGetG(rgb),TIFFGetB(rgb)))
|
|
switch (bitspersample) {
|
|
case 1: CMAP(7); CMAP(6); CMAP(5); CMAP(4);
|
|
case 2: CMAP(3); CMAP(2);
|
|
case 4: CMAP(1);
|
|
case 8: CMAP(0);
|
|
}
|
|
#undef CMAP
|
|
} while (i--);
|
|
} else if (img->isContig) {
|
|
switch (img->photometric) {
|
|
case PHOTOMETRIC_RGB:
|
|
case PHOTOMETRIC_LOGLUV:
|
|
switch (bitspersample) {
|
|
case 8: img->put.contig = putcontig8bittile; break;
|
|
case 16: img->put.contig = putcontig16bittile; break;
|
|
}
|
|
break;
|
|
case PHOTOMETRIC_SEPARATED:
|
|
switch (bitspersample) {
|
|
case 8: img->put.contig = putcontig8bitCMYKtile; break;
|
|
}
|
|
break;
|
|
case PHOTOMETRIC_YCBCR:
|
|
if (img->bitspersample == 8) {
|
|
uint16 hs, vs;
|
|
TIFFGetFieldDefaulted(img->tif, TIFFTAG_YCBCRSUBSAMPLING,
|
|
&hs, &vs);
|
|
switch ((hs<<4)|vs) {
|
|
case 0x22: /* most common case */
|
|
img->put.contig = putcontig8bitYCbCr22tile;
|
|
break;
|
|
default: /* all others cost more */
|
|
libput = img->put.contig;
|
|
img->put.contig = cvtcontig8bittile;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch (img->photometric) {
|
|
case PHOTOMETRIC_RGB:
|
|
switch (img->bitspersample) {
|
|
case 8: img->put.separate = putseparate8bittile; break;
|
|
case 16: img->put.separate = putseparate16bittile; break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|