Skip to content
Snippets Groups Projects
mgtexture.c 12.8 KiB
Newer Older
teuben's avatar
teuben committed
/* Copyright (c) 1992 The Geometry Center; University of Minnesota
   1300 South Second Street;  Minneapolis, MN  55454, USA;
 
This file is part of geomview/OOGL. geomview/OOGL is free software;
you can redistribute it and/or modify it only under the terms given in
the file COPYING.geomview, which you should have received along with this file.
teuben's avatar
teuben committed
This and other related software may be obtained via anonymous ftp from
geom.umn.edu; email: software@geom.umn.edu. */
/* Copyright (C) 1992 The Geometry Center */
teuben's avatar
teuben committed
/* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */

 * Texture library, adapted from geomview for partiview by...
 * Stuart Levy, slevy@ncsa.uiuc.edu
 * National Center for Supercomputing Applications,
 * University of Illinois 2001.
 * This file is part of partiview, released under the
 * Illinois Open Source License; see the file LICENSE.partiview for details.
teuben's avatar
teuben committed
#include <ctype.h>
#undef isalnum		/* Hacks for Irix 6.5.x */
#undef isspace
#undef isascii

#include <stdio.h>
#include <stdlib.h>
#include "textures.h"
#include "findfile.h"
slevy's avatar
slevy committed
#include "futil.h"
teuben's avatar
teuben committed

#include "shmem.h"

#include <errno.h>


#if defined(unix) || defined(__unix)
# include <unistd.h>
#elif defined(_WIN32) || defined(WIN32)
slevy's avatar
slevy committed
# include "winjunk.h"
teuben's avatar
teuben committed
# define pclose fclose
#endif

#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <string.h>

teuben's avatar
teuben committed
	TF_BYTE,
	TF_BIT,
	TF_ASCII,
	TF_SGIRLE,		/* SGI RLE image */
	TF_SGIRAW		/* for(z) for(y=bottom to top) { data } */
slevy's avatar
slevy committed
    int xsize, ysize, zsize, channels;
teuben's avatar
teuben committed
    int maxval;		/* For PNM TF_ASCII files */
    int *rleoff;	/* For TF_SGIRLE: offsets[z][y], then lengths[z][y] */
    char *rledata;	/* For TF_SGIRLE: all data (don't fseek so we can read from pipe)*/
};

static FILE *
gimme(char *fname, int *dopclose, struct xyc *size)
{
    char cmd[2048];
    char *suf, *p, *q;
    int i, c, len, slen;
    char *prefix = NULL;
    char *msg = NULL;
    FILE *f = NULL;
    static char *suffixes[] = {
	"\0zcat ", "Z",
	"\0gzip -dc ", "z", "gz",
	"\0tifftopnm ", "tiff", "tif",
	"\0giftoppm ", "gif",
	"\0",
	NULL
    };
    

slevy's avatar
slevy committed
    size->zsize = 1;
teuben's avatar
teuben committed
    size->rleoff = NULL;  size->rledata = NULL;
    if(fname == NULL)
	goto nope;
    len = strlen(fname);
    for(i = 0; (suf = suffixes[i]) != NULL; i++) {
	if(suf[0] == '\0') {
	    prefix = suf+1;
	    continue;
	} else {
	    slen = strlen(suf);
	    if(slen < len && fname[len-slen-1] == '.' &&
			    strcmp(fname+len-slen, suf) == 0)
		break;
	}
    }

    if(access(fname, R_OK) < 0) {
	fprintf(stderr, "%s: Can't read texture image: %s", fname, strerror(errno));
	goto nope;
    }
    if(prefix == NULL || prefix[0] == '\0') {
	*dopclose = 0;
	f = fopen(fname, "rb");
#if defined(unix) || defined(__unix)
    } else {
	strcpy(cmd, prefix);
	for(p = cmd+strlen(cmd), q = fname; *q && p < &cmd[sizeof(cmd)-10]; ) {
	    if(!(isalnum(*q) || *q == '/' || *q=='.'))
		*p++ = '\\';	/* Quote anything remotely suspicious */
	    *p++ = *q++;
	}
	*p = '\0';
	*dopclose = 1;
teuben's avatar
teuben committed
#endif
    }
    if(f == NULL) {
	fprintf(stderr, "mg_inhaletexture: Can't %s %s: %s",
			*dopclose ? "invoke" : "open", cmd, strerror(errno));
	goto nope;
    }
    c = fgetc(f);
    if(c == 0x01 && (c = fgetc(f)) == 0xDA) {
	/* SGI image file */
	short shorts[3];
	int storage = fgetc(f);
	int bpp = fgetc(f);
	if(bpp != 1) {
	    msg = "%s: must have 8-bit image values";
	    goto nope;
	}
	fgetc(f); fgetc(f);		/* Skip "dimension" */
	fgetns(f, 3, shorts, 1);	/* Read big-endian 16-bit values */
	size->xsize = shorts[0];
	size->ysize = shorts[1];
	size->channels = shorts[2];
	for(i = 0; i < 4+4+492; i++)	/* Skip min, max, pad data */
	    getc(f);
	size->format = (storage==0x01) ? TF_SGIRLE : TF_SGIRAW;
	if(size->format == TF_SGIRLE) {
	    /* Inhale offset&length table */
	    int n = size->ysize*size->channels;
	    int max = 0;
	    size->rleoff = OOGLNewNE(int, n*2, "TF_SGIRLE offsets");
	    msg = "%s: can't read RLE offsets";
	    if(fgetni(f, n*2, size->rleoff, 1) != n*2)
		goto nope;
	    if(ftell(f) < 0) {
		for(i = 0; i < n; i++)
		    if(max < size->rleoff[i])
			max = size->rleoff[i] + size->rleoff[i+n];
		size->rledata = OOGLNewNE(char, max+1, "TF_SGIRLE data");
		if(fread(size->rledata, max, 1, f) <= 0)
		    goto nope;
	    }
	}
slevy's avatar
slevy committed
    } else if(c == 'P') {
        if((c = fgetc(f)) >= '1' && c <= '6') {
	    msg = "%s: Bad header on PNM image";
	    size->channels = (c == '3' || c == '6') ? 3 : 1;
	    if(fgetni(f, 2, &size->xsize, 0) != 2)
		goto nope;
	    size->maxval = 1;
	    if(c != '1' && c != '4')
		if(fgetni(f, 1, &size->maxval, 0) <= 0)
		    goto nope;
	    switch(c) {
	    case '1': case '2': case '3':	size->format = TF_ASCII; break;
	    case '4':			size->format = TF_BIT; break;
	    case '5': case '6':		size->format = TF_BYTE; break;
	    }
	    while((c = fgetc(f)) != '\n' && c != EOF)
		;
	} else if(c >= '7' && c <= '9') {
	    /* 3D image */
	    msg = "%s: Bad header on PNM-3D image";
	    size->channels = (c == '9') ? 3 : 1;
	    if(fgetni(f, 3, &size->xsize, 0) != 3)
teuben's avatar
teuben committed
		goto nope;
slevy's avatar
slevy committed
	    size->maxval = 1;
	    if(c != '1' && c != '4')
		if(fgetni(f, 1, &size->maxval, 0) <= 0)
		    goto nope;
	    size->format = TF_BYTE;
	    while((c = fgetc(f)) != '\n' && c != EOF)
		;
teuben's avatar
teuben committed
	}
    } else {
	msg = "%s: Unknown texture image file format";
	goto nope;
    }
    return f;

  nope:
    size->xsize = size->ysize = size->channels = 0;
    if(f)
#if defined(unix) || defined(__unix)
	if(*dopclose)
	    pclose(f);
	else
#endif
	    fclose(f);
    if(msg)
	fprintf(stderr, msg, fname);

    return NULL;
}

int
readimage(Texture *tx, int offset, int rowsize, struct xyc *size, FILE *f, char *fname)
{
    int val, bit, i, j, k;
    int stride = tx->channels;
slevy's avatar
slevy committed
    int nrows = size->ysize * size->zsize;
teuben's avatar
teuben committed

    if(size->xsize <= 0 || size->ysize <= 0)
	return 0;

    if(size->format == TF_SGIRAW) {
	for(k = 0; k < size->channels; k++) {
	    for(i = 0; i < size->ysize; i++) {
		char *pix = tx->data + offset + k + rowsize * i;
		j = size->xsize;
		do {
		    *pix = getc(f);
		    pix += stride;
		} while(--j > 0);
	    }
	    if(feof(f))
		goto nope;
	}
    } else if(size->format == TF_SGIRLE) {
        int yup = size->rleoff[0] < size->rleoff[1];
	int rlebase = 512 + size->channels*size->ysize*4;
	for(k = 0; k < size->channels; k++) {
	    for(i = 0; i < size->ysize; i++) {
		char *rle = NULL;
		int row = (yup ? i : size->ysize-i-1);
		char *pix = tx->data + offset + k + rowsize * row;
		int foff = size->rleoff[k*size->ysize + row];
		int count;
		j = size->xsize;
		if(size->rledata)
		    rle = size->rledata + foff - rlebase;
		else
		    fseek(f, foff, SEEK_SET);
		while((count = rle ? *rle++ : getc(f)) > 0) {
		    if(count & 0x80) {
			count &= 0x7F;
			do {
			    *pix = rle ? *rle++ : getc(f);
			    pix += stride;
			} while(--count > 0);
		    } else {
			int val = rle ? *rle++ : getc(f);
			do {
			    *pix = val;
			    pix += stride;
			} while(--count > 0);
		    }
		}
		if(feof(f))
		    goto nope;
	    }
	}
    } else {
slevy's avatar
slevy committed
	/* PNM or new nonstandard PNM-3D */
	for(i = 0; i < nrows; i++) {
	    char *row = tx->data + rowsize * (nrows - i - 1) + offset;
teuben's avatar
teuben committed
	    if(tx->channels == size->channels && size->format == TF_BYTE) {
		j = fread(row, size->channels, size->xsize, f);
	    } else {
		register char *pix = row;
		j = size->xsize;
		switch(size->format) {
		case TF_BYTE:
		    switch(size->channels) {
		    case 1: do { *pix = getc(f); pix += stride; } while(--j); break;
		    case 3: do {
			      pix[0] = getc(f);
			      pix[1] = getc(f);
			      pix[2] = getc(f);
			      pix += stride;
			    } while(--j);
			    break;
		    }
		    break;
		case TF_BIT:
		    bit = 0;
		    do {
			if(--bit < 0) {
			    bit = 7;
			    k = getc(f);
			}
			*pix = (k >> bit) & 1;
			pix += stride;
		    } while(--j > 0);
		    break;
		case TF_ASCII:
		    do {
			for(k = 0; k < size->channels; k++) {
			    fgetni(f, 1, &val, 0);
			    pix[k] = val * 255 / size->maxval;
			}
			pix += stride;
		    } while(--j > 0);
		    break;
		}
	    }
	    if(feof(f))
		break;
	}
    }

 nope:
    if(size->rleoff)
	OOGLFree(size->rleoff);
    if(size->rledata)
	OOGLFree(size->rledata);
    size->rleoff = NULL;
    size->rledata = NULL;

slevy's avatar
slevy committed
    if(i < nrows) {
teuben's avatar
teuben committed
	fprintf(stderr, "%s: Error reading texture image row %d of %d",
		fname, i, size->ysize);
	return 0;
    }
    return 1;
}


int
mg_same_file(char *fname1, char *fname2)
{
   struct stat st1, st2;
   char *tail1, *tail2;
   if((fname1 != NULL) != (fname2 != NULL))
	return 0;
   if(fname1 == NULL || strcmp(fname1, fname2) == 0)
	return 1;
   tail1 = strrchr(fname1, '/'); if(tail1 == NULL) tail1 = fname1;
   tail2 = strrchr(fname2, '/'); if(tail2 == NULL) tail2 = fname2;
   if(strcmp(fname1, fname2) != 0)
	return 0;
   if(stat(fname1, &st1) < 0 || stat(fname2, &st2) < 0 ||
	    st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev)
	return 0;
   return 1;
}
   
int
mg_same_texture(Texture *tx1, Texture *tx2)
{
    if(tx1 == tx2)
	return 1;
    if(tx1 == NULL || tx2 == NULL)
	return 0;
    if(!mg_same_file(tx1->filename, tx2->filename))
	return 0;
    if(!mg_same_file(tx1->alphafilename, tx2->alphafilename))
	return 0;
    /* Needn't compare application style, etc. */
    return 1;
}


#ifdef _WIN32
# undef FD_ISSET	/* Use fake FD_ISSET from streampool.c for VC++ */
#endif


int
mg_inhaletexture(Texture *tx, int rgba)
{
    FILE *f = NULL, *alphaf = NULL;
    struct xyc size, alphasize;
    int dopclose, alphapclose;
    int i, rowsize, ok, wantchans;
    void (*oldsigchld)();

    if(tx == NULL)
	return 0;
    if(tx->flags & TXF_LOADED)
	return (tx->data != NULL);

    tx->flags |= TXF_LOADED;		/* Even if we fail, don't retry */
    if(tx->data)
	OOGLFree(tx->data);
    tx->data = NULL;

    wantchans = tx->channels;

#if defined(unix) || defined(__unix)
    oldsigchld = (void ((*)()))signal(SIGCHLD, SIG_DFL);
#endif
    f = gimme(tx->filename, &dopclose, &size);
    alphasize.channels = 0;	/* Ensure valid for later, even if no alpha */
    alphaf = gimme(tx->alphafilename, &alphapclose, &alphasize);
    if(f == NULL) {
	if(alphaf) {
	    size = alphasize;
	    size.channels = 0;
	} else {
	    return 0;
	}
    }

    tx->xsize = size.xsize;
    tx->ysize = size.ysize;
slevy's avatar
slevy committed
    tx->zsize = size.zsize;
teuben's avatar
teuben committed
    tx->channels = size.channels;

slevy's avatar
slevy committed
    if(tx->zsize > 1)
	tx->flags |= TXF_3D;

teuben's avatar
teuben committed
    if(alphaf) {
slevy's avatar
slevy committed
	if(size.xsize != alphasize.xsize || size.ysize != alphasize.ysize
		|| size.zsize != alphasize.zsize) {
teuben's avatar
teuben committed
	    fprintf(stderr, "Texture data file (%s) is %dx%d, but alphafile (%s) is %dx%d: ignoring it",
	    tx->filename, size.xsize, size.ysize,
	    tx->alphafilename, alphasize.xsize, alphasize.ysize);
	    alphasize.channels = 0;
	} else {
	    tx->channels += alphasize.channels;
	}
    }
    
    /* Let's load the pixels in the form required by GL and Open GL. */

    if(tx->channels < wantchans)
	tx->channels = wantchans;

    rowsize = tx->xsize * tx->channels;
    rowsize = (rowsize + 3) & ~3;	/* Round up to 4-byte boundary */
slevy's avatar
slevy committed
    tx->data = OOGLNewNE(char, rowsize * tx->ysize * tx->zsize, "Texture data");
teuben's avatar
teuben committed

    ok = readimage(tx, 0, rowsize, &size, f, tx->filename);
    if(alphaf)
	ok &= readimage(tx, tx->channels - alphasize.channels, rowsize, &alphasize,
				alphaf, tx->alphafilename);
    if(!ok) {
	OOGLFree(tx->data);
	tx->data = NULL;
    }

    if(size.channels + alphasize.channels != tx->channels) {
	if(tx->channels == 4) {
slevy's avatar
slevy committed
	    int nrows = size.ysize * size.zsize;
	    for(i = 0; i < nrows; i++) {
teuben's avatar
teuben committed
		int k = size.xsize;
		register char *p = tx->data + rowsize*i;
		switch(size.channels) {
		case 1:
		    do { p[1] = p[0]; p[2] = p[0];
			 if(alphasize.channels == 0) p[3] = 255;
			 p += 4;
		    } while(--k > 0);
		    break;
		case 3:
		    do { p[3] = 255; p += 4; } while(--k > 0);
		    break;
		}
	    }
	} else {
	    fprintf(stderr,
	    "mg_inhaletexture: warning: dunno how to inhale %d+%d images into %d-chan image",
		size.channels, alphasize.channels, tx->channels);
	}
    }

    if(tx->data && (rgba & TXF_RGBA) == 0) {
slevy's avatar
slevy committed
	int nrows = size.ysize * size.zsize;
	for(i = 0; i < nrows; i++) {
teuben's avatar
teuben committed
	    /* Arrange data in each pixel in the order GL prefers.
	     * Maybe the decision on whether this is needed
	     * should be an option to mg_inhaletexture().
	     */
	    register int t;
	    int k = size.xsize;
	    register char *p = tx->data + rowsize*i;
	    switch(tx->channels) {
	    case 2:
		do {
		    t = *p; *p = p[1]; p[1] = t;
		    p += 2;
		} while(--k > 0);
		break;
	    case 3:
		do {
		    t = *p; *p = p[2]; p[2] = t;
		    p += 3;
		} while(--k > 0);
		break;
	    case 4:
		do {
		    t = *p; *p = p[3]; p[3] = t;
		    t = p[1]; p[1] = p[2]; p[2] = t;
		    p += 4;
		} while(--k > 0);
	    }
	}
    } else {
	tx->flags |= TXF_RGBA;
    }
    if(f) {
	if(dopclose) pclose(f);
	else fclose(f);
    }
    if(alphaf) {
	if(alphapclose) pclose(alphaf);
	else fclose(alphaf);
    }
#ifdef SIGCHLD
    signal(SIGCHLD, oldsigchld);
#endif
    return ok;
}