[BACK]Return to cliptexture.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfutil

File: [Development] / performer / src / lib / libpfutil / cliptexture.c (download)

Revision 1.1, Tue Nov 21 21:39:36 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 1996, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
*/

/*
 * file: cliptexture.c
 * ----------------
 *
 * Contains generic cliptexture routines. Specifically the function
 *	pfuAddMPClipTextureToPipes()
 *  which adds mpcliptextures to one or more pipes properly. 
 * $Revision: 1.1 $
 * $Date: 2000/11/21 21:39:36 $
 */

#include <stdlib.h>
#include <Performer/pfdu.h>
#include <Performer/pfutil.h>
#include <math.h>
#include <values.h>

/* 
** Connects an mpcliptexture to a pipe. If there's more than one pipe
** then one pipe is given the cliptexture, and the rest of the pipes
** get slaves pointing to that cliptexture, which becomes the master.
**
** The mpcliptexture must not be a slave. All pipes must
** exist when this routine is called.
**
** Routine works "properly", by not adding slaves to
** pipes that already have the cliptexture, or have a slave that
** points to the cliptexture. Thus, the routine will not generate
** any debug messages, even if the routine is called multiple times with
** the same cliptexture and pipes.
**
** The routine returns the address of the pipe that has the master, or NULL
** if there are problems.
**
** If _masterpipe is not null, then that pipe will have the master
** mpcliptexture added to it. If the _masterpipe is NULL, or if the master
** or one of its slaves is already attached to _masterpipe, the routine will
** assign the master (if one hasn't already been assigned) to the first pipe
** it finds without a copy of this cliptexture or one of its slaves. 
**
** If _pipes is null, then all existing pipes will be used
**
*/
pfPipe *
pfuAddMPClipTextureToPipes(pfMPClipTexture *_master, 
			   pfPipe * _masterpipe,
			   pfPipe *_pipes[])
{
    pfMPClipTexture *mpct, *master;
    pfPipe **pipearray, *masterpipe;
    pfPipe *allpipes[100]; /* assuming never more than 100 pipes */
    int cliptexi, ncliptex, npipes, pipei;
    int found;

    /* check arguments */
    if(_master == NULL)
        return NULL;

    /* if _master cliptexture is a slave, then give up */
    if(pfGetMPClipTextureMaster(_master))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT, 
		 "pfuAddMPClipTextureToPipes: pfMPClipTexture (%x) is a slave",
		 _master);
        return NULL;
    }
    if(_pipes)
        pipearray = _pipes;
    else 
    {
        pipearray = allpipes;

        npipes = pfGetMultipipe();
        for(pipei = 0; pipei < npipes; pipei++) 
	{
	    pipearray[pipei] = pfGetPipe(pipei);
	}
        pipearray[pipei] = NULL;
    }
      
    masterpipe = _masterpipe;

    /* use _masterpipe to set cliptexture master */
    if(masterpipe) {
        ncliptex = pfGetPipeNumMPClipTextures(_masterpipe);
	/* error checking */
	found = FALSE;
        for(cliptexi = 0; cliptexi < ncliptex; cliptexi++) {
	    mpct = pfGetPipeMPClipTexture(masterpipe, cliptexi);
	    if(mpct == _master) {
	        found = TRUE;
	        break; /* do nothing if master is already in this pipe */
	    }
	    master = pfGetMPClipTextureMaster(mpct);
	    if(_master == master) {
	        found = TRUE;
	        break; /* do nothing if slave of master is in this pipe */
	    }
	}
	/* if no problems found, add master to this pipe */
	if(!found)
	    pfAddMPClipTexture(masterpipe, _master);
	else 
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "pfuAddMPClipTextureToPipes: _master or slave to "
		     "master already found in masterpipe\n");
    }

    for(; *pipearray; pipearray++) {
        ncliptex = pfGetPipeNumMPClipTextures(*pipearray);
	found = FALSE;
        for(cliptexi = 0; cliptexi < ncliptex; cliptexi++) {
	    mpct = pfGetPipeMPClipTexture(*pipearray, cliptexi);
	    if(mpct == _master) {
	        masterpipe = *pipearray;
	        found = TRUE;
	        break; /* do nothing if master is already in this pipe */
	    }
	    master = pfGetMPClipTextureMaster(mpct);
	    if(_master == master) {
	        found = TRUE;
	        break; /* do nothing if slave of master is in this pipe */
	    }
	}
	/* after master added to one pipe, all other adds will create slaves */
	if(!found)
	    pfAddMPClipTexture(*pipearray, _master);
    }
    return masterpipe;
}


/*
** List version of routine above. Takes a list of mpcliptexture pointers
** and applies them to the pipe list. Routine assumes one pipe will be
** master for all cliptextures
**
** XXX TODO Should probably allow a list of masterpipes too...
*/

void
pfuAddMPClipTexturesToPipes(pfList *_mpcliptextures, 
			    pfPipe * _masterpipe,
			    pfPipe *_pipes[])
{
    int n, ncliptextures;

    ncliptextures = pfGetNum(_mpcliptextures);
    for(n = 0; n < ncliptextures; n++)
    {
        (void)pfuAddMPClipTextureToPipes((pfMPClipTexture*)
					 pfGet(_mpcliptextures, n),
					 _masterpipe,
					 _pipes);
    }
}

/* is each dimension in array a power of two ? */
static int isPow2(int val[])
{
    int i;
    for(i = PF_S; i <= PF_R;i++)
    {
	if(val[i] & (val[i] - 1))
	    return FALSE;
    }
    return TRUE;
}


static int checkClipTex(pfuClipTexConfig *config)
{

    if(config->clipSize * 2 < config->minICache[PF_S] ||
       config->clipSize * 2 < config->minICache[PF_T] ||
       config->clipSize * 2 < config->minICache[PF_R])
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: clipsize %d is too small "
		 "relative to minimum image cache size (%d, %d, %d). "
		 "Set sizes so that all clipped levels are image caches.",
		 config->clipSize,
		 config->minICache[PF_S],
		 config->minICache[PF_T],
		 config->minICache[PF_R]);
	return 1;
    }

    /* check to see if mandatory values were filled in */
    if(config->extFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "external format wasn't set");
	return 1;
    }

    if(config->intFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "internal format wasn't set");
	return 1;
    }

    if(config->imgFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "image format wasn't set");
	return 1;
    }

    if(config->components < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "number of components wasn't set");
	return 1;
    }

    if(config->imgSize[PF_S] < 0 ||
       config->imgSize[PF_T] < 0 ||
       config->imgSize[PF_R] < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "image size wasn't set");
	return 1;
    }

    if(config->imgSize[PF_S] == 0 ||
       config->imgSize[PF_T] == 0 ||
       config->imgSize[PF_R] == 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: "
		 "one or more image size dimensions are zero (%d %d %d)",
		 config->imgSize[PF_S], 
		 config->imgSize[PF_T], 
		 config->imgSize[PF_R]); 
	return 1;
    }

    if(config->clipSize < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: mandatory field "
		 "clip size wasn't set");
	return 1;
    }

    if(config->minICache[PF_S] == 0 ||
       config->minICache[PF_T] == 0 ||
       config->minICache[PF_R] == 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: "
		 "one or more minimum image cache size dimensions are zero "
		 "(%d %d %d)",
		 config->minICache[PF_S], 
		 config->minICache[PF_T], 
		 config->minICache[PF_R]); 
	return 1;
    }

    /* We'll let you do this, even though it doesn't make much sense */
    if(config->clipSize == 0)
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: clip size is zero");

    return 0; /* checks passed */
}


pfClipTexture *
pfuMakeClipTexture(pfuClipTexConfig *config)
{
    int level;
    int levelSizeT, levelSizeS;
    int minImageDim;
    int ratio[3];
    pfClipTexture *ct;
    pfImageCache *ic;
    pfImageTile *it;
    void *arena = pfGetSharedArena();

    if(checkClipTex(config))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: parameter checking failed");
	return NULL;
    }

    ct = pfNewClipTexture(arena);
    
    /* give the clip texture a name */
    /* XXX it would be nice if we could confirm it's a valid string */
    if(config->name) 
	pfTexName((pfTexture *)ct, config->name);

    if(!isPow2(config->imgSize))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: imageSize dimension(s) not power of two: "
		 "(%d, %d, %d)", 
		 config->imgSize[PF_S],
		 config->imgSize[PF_T],
		 config->imgSize[PF_R]);
	return NULL;
    }
    pfClipTextureVirtualSize(ct, 
			     config->imgSize[PF_S], 
			     config->imgSize[PF_T],
			     config->imgSize[PF_R]);


    /* Find min and max dimension size (S & T only) */
    if(config->imgSize[PF_S] < config->imgSize[PF_T])
	minImageDim = config->imgSize[PF_S];
    else
	minImageDim = config->imgSize[PF_T];

    /* make sure clip size is a power of two in each dimension */
    if(config->clipSize & (config->clipSize - 1))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeClipTexture: clipSize %d not power of two",
		 config->clipSize);
	return NULL;
    }
    pfClipTextureClipSize(ct, config->clipSize);

    pfTexImage((pfTexture *)ct, NULL, config->components, 
	       config->clipSize, config->clipSize, 1);

    pfTexFormat((pfTexture *)ct, PFTEX_EXTERNAL_FORMAT, config->extFormat);
    pfTexFormat((pfTexture *)ct, PFTEX_INTERNAL_FORMAT,	config->intFormat);
    pfTexFormat((pfTexture *)ct, PFTEX_IMAGE_FORMAT, config->imgFormat);

    if(minImageDim < config->clipSize)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "Minimum dimension of Virtual Size %d "
		 "is smaller than Clip Size %d",
		 minImageDim, config->clipSize);
	
    }

    /* XXX shouldn't this be checked from system config info? */
    if(config->invBorder < 8) {
	pfNotify(PFNFY_WARN, PFNFY_PRINT, 
		 "Invalid Border Value %d too small: setting to 8\n",
		 config->invBorder);
	config->invBorder = 8;
    }
    pfClipTextureInvalidBorder(ct, config->invBorder);

    /* XXX this should be checked from system info too, but I don't want
       to risk getting it wrong */
    pfClipTextureNumEffectiveLevels(ct, config->effLevels);
    pfClipTextureNumAllocatedLevels(ct, config->allocLevels);

    /* 
    ** If minIcache size isn't set, pick a reasonable default.
    ** Two Cases:
    ** First case; cliptexture is square.
    ** If neither are S nor T is set, we choose the default (one level
    ** bigger than clipsize). If either S or T are set, we use the set
    ** value for both.
    **
    ** Second case: cliptexture isn't square.
    **
    ** XXX We don't even know if the hardware supports non-square clipmaps
    **
    ** If S or T is set we compute the clipmap size ratio, and use it
    ** to compute the missing coordinate. If both are missing, we set
    ** the smaller coordinate to the clipsize, and the bigger coordinate
    ** to clipsize scaled by the ratio.
    */

    if(config->minICache[PF_R] < 0)
	config->minICache[PF_R] = 1;

    if(config->minICache[PF_S] < 0 ||
       config->minICache[PF_T] < 0)
    {
	if(config->imgSize[PF_S] == config->imgSize[PF_T]) /* square */
	{
	    if(config->minICache[PF_S] >= 0) /* s is set: t <- s */
		config->minICache[PF_T] = config->minICache[PF_S];
	    else if(config->minICache[PF_T] >= 0)
		/* t is set: set s <- t */
		config->minICache[PF_S] = config->minICache[PF_T];
	    else /* neither set; use the default */
		config->minICache[PF_S] = config->minICache[PF_T] =
		    config->clipSize * 2;

	}
	else /* not square */
	{
	    int ratio;
	    ratio = config->imgSize[PF_S]/config->imgSize[PF_T];
	    if(ratio) /* S > T */
	    {
		if(config->minICache[PF_S] >= 0) /* s is set */
		    config->minICache[PF_T] = config->minICache[PF_S] / ratio;
		else if(config->minICache[PF_T] >= 0) /* t is set */
		    config->minICache[PF_S] = config->minICache[PF_T] * ratio;
		else /* neither are set */
		{
		    config->minICache[PF_S] = config->clipSize * ratio;
		    config->minICache[PF_T] = config->clipSize;
		}
	    }
	    else /* T > S */
	    {
		ratio = config->imgSize[PF_T]/config->imgSize[PF_S];
		if(config->minICache[PF_S] >= 0) /* s is set */
		    config->minICache[PF_T] = config->minICache[PF_S] * ratio;
		else if(config->minICache[PF_T] >= 0) /* t is set */
		    config->minICache[PF_S] = config->minICache[PF_T] / ratio;
		else /* neither are set */
		{
		    config->minICache[PF_S] = config->clipSize;
		    config->minICache[PF_T] = config->clipSize * ratio;
		}

	    }
	}
    }

    ratio[PF_S] = config->imgSize[PF_S]/config->minICache[PF_S];
    ratio[PF_T] = config->imgSize[PF_T]/config->minICache[PF_T];

    if(ratio[PF_S] != ratio[PF_T])
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfdLoadClipTexture: smallest_icache dimensions "
		 "aren't proportional to virt_size dimensions");
	return NULL;
    }

    if(ratio[PF_S] & (ratio[PF_S] - 1))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfdLoadClipTexture: smallest_icache dimensions"
		 "aren't a power of two ratio of virt_size\n");
	return NULL;
    }


    /* Configure all the cliptexture's image caches */
    for(levelSizeS = config->imgSize[PF_S], levelSizeT = config->imgSize[PF_T],
	    level = 0;
	levelSizeS >= config->minICache[PF_S]; /* T's should match */
	level++, levelSizeS >>= 1, levelSizeT >>= 1)
    {
	ic = config->icFunc(ct, level, config);
	
	if(ic == NULL)
	{
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfdLoadClipTexture: level %d image cache, size"
		     "(%d,%d), failed. Giving up on parsing cliptexture",
		     level, levelSizeS, levelSizeT);
	    return NULL;
	}    

	if(config->clipSize >= levelSizeS &&
	   config->clipSize >= levelSizeT)
	{
	    /*
	    ** XXX TODO check for the existence of tile file pointed to by 
	    ** pyramid image cache
	    */
	}

	/* ensure texture and texregion size is no larger than clip size */
	/* XXX TODO this should handle imgSize; check */

	pfTexImage((pfTexture *)ct, NULL, config->components, 
		   config->clipSize,
		   config->clipSize,
		   1);

	pfImageCacheTexRegionSize(ic, 
				  PF_MIN2(config->clipSize, levelSizeS),
				  PF_MIN2(config->clipSize, levelSizeT),
				  1);
	pfImageCacheTexSize(ic,
			    PF_MIN2(config->clipSize, levelSizeS),
			    PF_MIN2(config->clipSize, levelSizeT),
			    1);

	pfClipTextureLevel(ct, level, ic);

    }
    
    /* Load Image Tiles, if not specified by config file */
    

    for (levelSizeS = config->minICache[PF_S]/2,
	     levelSizeT = config->minICache[PF_T]/2;
	 levelSizeS && levelSizeT;
	 level++, levelSizeS >>= 1, levelSizeT >>= 1)
    {
	it = config->tileFunc(ct, level, config);
	if(!it)
	{
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeClipTexture: "
		     "image tile configuration of level %d failed; aborting",
		     level);
	    return NULL;
	}

	pfImageTileMemImageFormat(it, config->imgFormat);
	pfImageTileFileImageFormat(it, config->imgFormat);
	pfImageTileMemImageType(it, config->extFormat);
	pfImageTileFileImageType(it, config->extFormat);
	pfImageTileSize(it, levelSizeS, levelSizeT, 1);
	pfImageTileHeaderOffset(it, config->hdrOffset);
	pfClipTextureLevel(ct, level, it);
	pfLoadImageTile(it);
    }
    return ct;
}

void
pfuInitClipTexConfig(pfuClipTexConfig *config)
{
    config->name = NULL;

    /* illegal values to see if they were filled in */

    config->extFormat = -1;
    config->intFormat = -1;
    config->imgFormat = -1;
    config->components = -1;

    config->imgSize[PF_S] = -1;
    config->imgSize[PF_T] = -1;
    config->imgSize[PF_R] = -1;

    config->clipSize = -1;

    config->minICache[PF_S] = -1; /* needed if image caches in pyramid */
    config->minICache[PF_T] = -1;
    config->minICache[PF_R] = -1;

    config->tileSize[PF_S] = -1; /* only needed if auto configing icaches */
    config->tileSize[PF_T] = -1;
    config->tileSize[PF_R] = -1;

    /* reasonable default values */

    config->invBorder = 8;
    config->effLevels = 16;
    config->allocLevels = 1000;
    config->hdrOffset = 0;
    config->sStreams = NULL;
    config->tStreams = NULL;
    config->rStreams = NULL;

    config->icData = NULL;
    config->tileData = NULL;
    
    config->icFunc = NULL;
    config->tileFunc = NULL;

    config->pageSize = -1; /* "not initialized" value */

    config->readFunc = NULL; 
    config->lookahead = 1; /* default value; 1 tile border */
}

/* Don't try to free struct or data specific struct pointers */
void
pfuFreeClipTexConfig(pfuClipTexConfig *config)
{
    int i;

    /* XXX Todo add default tile list */


    /* NOTE streams used for all icache levels */
    if(config->sStreams)
    {
	for(i = 0; i < pfGetNum(config->sStreams); i++)
	    free(pfGet(config->sStreams, i));
	pfDelete(config->sStreams);
    }
    if(config->tStreams)
    {
	for(i = 0; i < pfGetNum(config->tStreams); i++)
	    free(pfGet(config->tStreams, i));
	pfDelete(config->tStreams);
    }
    if(config->rStreams)
    {
	for(i = 0; i < pfGetNum(config->rStreams); i++)
	    free(pfGet(config->rStreams, i));
	pfDelete(config->rStreams);
    }
}


static int checkICache(pfuImgCacheConfig *state)
{


    /* check to see if mandatory values were filled in */

    if(state->extFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "mandatory field external format wasn't set or is invalid %d",
		 state->extFormat);
	return 1;
    }

    if(state->intFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "mandatory field internal format wasn't set or is invalid %d",
		 state->intFormat);
	return 1;
    }

    if(state->imgFormat < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "mandatory field image format wasn't set or is invalid %d",
		 state->imgFormat);
	return 1;
    }

    if(state->components < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "mandatory field number of components wasn't set "
		 "or is invalid %d",
		 state->components);
	return 1;
    }

    if(state->size[PF_S] < 0 ||
       state->size[PF_T] < 0 ||
       state->size[PF_R] < 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "mandatory field image cache size wasn't set or is invalid "
		 "(%d %d %d)",
		 state->size[PF_S],
		 state->size[PF_T],
		 state->size[PF_R]);
	return 1;
    }

    if(state->size[PF_S] == 0 ||
       state->size[PF_T] == 0 ||
       state->size[PF_R] == 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "one or more image cache size dimensions are zero "
		 "(%d %d %d)",
		 state->size[PF_S],
		 state->size[PF_T],
		 state->size[PF_R]);
	return 1;
    }

    if(state->size[PF_S] & (state->size[PF_S] - 1)  || 
       state->size[PF_T] & (state->size[PF_T] - 1)  || 
       state->size[PF_R] & (state->size[PF_R] - 1))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: image size (%d %d %d) "
		 "not a power of 2\n",
		 state->size[PF_S],
		 state->size[PF_T],
		 state->size[PF_R]);
	return 1; /* we failed */    
    }

    if(state->memRegSize[PF_S] == 0 ||
       state->memRegSize[PF_T] == 0 ||
       state->memRegSize[PF_R] == 0)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: "
		 "one or more dimensions of memregion size are zero "
		 "(%d %d %d)",
		 state->memRegSize[PF_S],
		 state->memRegSize[PF_T],
		 state->memRegSize[PF_R]);
	return 1; /* we failed */
    }

    /* if mem region size is provided, do checking */
    if(state->memRegSize[PF_S] > 0 ||
       state->memRegSize[PF_T] > 0 ||
       state->memRegSize[PF_R] > 0)
    {

	if(state->texRegSize[PF_S] > 
	   state->memRegSize[PF_S] * state->tileSize[PF_S] ||
	   state->texRegSize[PF_T] > 
	   state->memRegSize[PF_T] * state->tileSize[PF_T] ||
	   state->texRegSize[PF_R] > 
	   state->memRegSize[PF_R] * state->tileSize[PF_R])
	{
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeImageCache: memregion too small for texregion\n");
	    return 1; /* we failed */
	}
    
	if((state->memRegSize[PF_S] < 2 && 
	    state->memRegSize[PF_S] * state->tileSize[PF_S] < 
	    state->size[PF_S]) ||
	   (state->memRegSize[PF_T] < 2 && 
	    state->memRegSize[PF_T] * state->tileSize[PF_T] <
	    state->size[PF_T]) ||
	    (state->memRegSize[PF_R] < 2 && 
	     state->memRegSize[PF_R] * state->tileSize[PF_R] <
	     state->size[PF_R]))
	{
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeImageCache: "
		     "a 1 x 1 memregion must cover entire cache image\n");
	    return 1; /* we failed */
	}

	/*
	** Round texregion size to tile boundary, and assume we need two extra
	** tile sizes of border in the memregion.
	*/

	if(state->memRegSize[PF_S] * state->tileSize[PF_S] <
	   state->size[PF_S] &&
	   (state->texRegSize[PF_S] + state->tileSize[PF_S] - 1)/
	   state->tileSize[PF_S] + 2 > state->memRegSize[PF_S])
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeImageCache: "
		     "too few memregion S tiles to freely move texregion\n");

	if(state->memRegSize[PF_T] * state->tileSize[PF_T] <
	   state->size[PF_T] &&
	   (state->texRegSize[PF_T] + state->tileSize[PF_T] - 1)/
	   state->tileSize[PF_T] + 2 > state->memRegSize[PF_T])
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeImageCache: "
		     "too few memregion T tiles to freely move texregion\n");


	if(state->memRegSize[PF_R] * state->tileSize[PF_R] <
	   state->size[PF_R] &&
	   (state->texRegSize[PF_R] + state->tileSize[PF_R] - 1)/
	   state->tileSize[PF_R] + 2 > state->memRegSize[PF_R])
	    pfNotify(PFNFY_WARN, PFNFY_PRINT,
		     "pfuMakeImageCache: "
		     "too few memregion R tiles to freely move texregion\n");
    
    }
    return NULL; /* ok if we made it to here */
}


void
pfuInitImgCacheConfig(pfuImgCacheConfig *config)
{

    /* set illegal values to see if they were set by application */

    config->name = NULL;

    /* mandatory values */

    config->extFormat = -1;
    config->intFormat = -1;
    config->imgFormat = -1;
    config->components = -1;

    config->memRegSize[PF_S] = -1;
    config->memRegSize[PF_T] = -1;
    config->memRegSize[PF_R] = -1;

    config->texRegSize[PF_S] = -1;
    config->texRegSize[PF_T] = -1;
    config->texRegSize[PF_R] = -1;

    config->size[PF_S] = -1;
    config->size[PF_T] = -1;
    config->size[PF_R] = -1;

    config->tileSize[PF_S] = -1;
    config->tileSize[PF_T] = -1;
    config->tileSize[PF_R] = -1;

    /* optional values */

    config->texRegOrg[PF_S] = -1;
    config->texRegOrg[PF_T] = -1;
    config->texRegOrg[PF_R] = -1;

    config->memRegOrg[PF_S] = -1;
    config->memRegOrg[PF_T] = -1;
    config->memRegOrg[PF_R] = -1;


    config->args[0] = -1;

    config->texSize[PF_S] = -1;
    config->texSize[PF_T] = -1; 
    config->texSize[PF_R] = -1;

    config->tilesInFile[PF_S] = 0;
    config->tilesInFile[PF_T] = 0;
    config->tilesInFile[PF_R] = 0;

    /* reasonable defaults */

    config->header = 0;
    config->base = NULL; /* default icache base path/name is empty string */
    config->format = NULL;
    config->numParams = 0;
    config->defaultTile = NULL;
    config->sStreams = NULL;
    config->tStreams = NULL;
    config->rStreams = NULL;
    config->pageSize = -1; /* "not initialized" value */
    config->readFunc = NULL;
    config->lookahead = 1; /* default value; 1 tile border */
    config->clipSize = -1;
}

void
pfuFreeImgCacheConfig(pfuImgCacheConfig *config)
{
    char *str;
    int i;

    if(config->base)
	free(config->base);
    if(config->format)
	free(config->format);
    if(config->defaultTile)
	free(config->defaultTile);

    if(config->sStreams)
    {
	for(i = 0; i < pfGetNum(config->sStreams); i++)
	    free(pfGet(config->sStreams, i));
	pfDelete(config->sStreams);
    }
    if(config->tStreams)
    {
	for(i = 0; i < pfGetNum(config->tStreams); i++)
	    free(pfGet(config->tStreams, i));
	pfDelete(config->tStreams);
    }
    if(config->rStreams)
    {
	for(i = 0; i < pfGetNum(config->rStreams); i++)
	    free(pfGet(config->rStreams, i));
	pfDelete(config->rStreams);
    }
}


/* Return 0 on failure */
pfImageCache *
pfuMakeImageCache(pfTexture *texture, int level, pfuImgCacheConfig *state)
{
    int i;
    pfImageTile *proto;
    pfImageCache *ic;
    int prevNP = pfGetNumGlobalQueueServiceProcs();
    void *arena = pfGetSharedArena();

    if(checkICache(state))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: parameter checking failed");
	return NULL;
    }

    /* ensure a reasonable texregion size */
    if(state->texRegSize[PF_S] < 0 || /* not defined, or defined wrong */
       state->clipSize != -1 &&
       state->texRegSize[PF_S] != PF_MIN2(state->clipSize, state->size[PF_S]))
    {
       if(state->clipSize != -1)
	   state->texRegSize[PF_S] = 
	       PF_MIN2(state->clipSize, state->size[PF_S]);
       else 
       {
	   pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuMakeImageCache(); you must either set the "
		    "tex region size properly or use this image cache "
		    "in a cliptexture to get a clipsize");
	   return NULL;
       }
    }
    if(state->texRegSize[PF_T] < 0 || /* not defined, or defined wrong */
       state->clipSize != -1 &&
       state->texRegSize[PF_T] != PF_MIN2(state->clipSize, state->size[PF_T]))
    {
       if(state->clipSize != -1)
	   state->texRegSize[PF_T] = 
	       PF_MIN2(state->clipSize, state->size[PF_T]);
       else 
       {
	   pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuMakeImageCache(); you must either set the "
		    "tex region size properly or use this image cache "
		    "in a cliptexture to get a clipsize");
	   return NULL;
       }
    }
    if(state->texRegSize[PF_R] < 0 || /* not defined, or defined wrong */
       state->clipSize != -1 &&
       state->texRegSize[PF_R] != PF_MIN2(state->clipSize, state->size[PF_R]))
    {
       if(state->clipSize != -1)
	   state->texRegSize[PF_R] = 
	       PF_MIN2(state->clipSize, state->size[PF_R]);
       else 
       {
	   pfNotify(PFNFY_WARN, PFNFY_PRINT,
		    "pfuMakeImageCache(); you must either set the "
		    "tex region size properly or use this image cache "
		    "in a cliptexture to get a clipsize");
	   return NULL;
       }
    }


    ic = pfNewImageCache(arena);
    if(!ic)
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: couldn't create image cache");
    proto = pfNewImageTile(arena);
    if(!proto)
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: couldn't create proto tile");

    /* user-defined read function */
    if(state->readFunc)
	pfImageTileReadFunc(proto, state->readFunc);


    /* set the page size from the structure if it isn't -1 (the default) */
    if(state->pageSize >= 0)
    {
	int lock;
	pfGetImageTileMemInfo(proto, NULL, &lock);
	pfImageTileMemInfo(proto, state->pageSize, lock);
    }
    /*
    ** It would be nice to check if name is pointing to
    ** a valid string
    */
    if(state->name)
	pfImageCacheName(ic, state->name);

    /*
    ** /// XXX Should make setable parameter whether to 
    **default to parallel Reads or not 
    **
    ** Set proto tile to default parallel Read queue.
    */
    pfImageTileReadQueue(proto, pfGetGlobalReadQueue());



    /* image cache size */

    /* XXX check sizes (pow of 2) */

    /* if mem Region Size isn't given, default to reasonable values */

    if(state->memRegSize[PF_S] <= 0)
	if(state->tileSize[PF_S] == state->size[PF_S])
	    state->memRegSize[PF_S] = 1;
	else
	    state->memRegSize[PF_S] = 
		PF_MIN2((state->texRegSize[PF_S] + state->tileSize[PF_S] - 1) /
			state->tileSize[PF_S] + 1 + state->lookahead,
			(state->size[PF_S] + state->tileSize[PF_S] - 1)/
			state->tileSize[PF_S]);
	    
    if(state->memRegSize[PF_T] <= 0)
	if(state->tileSize[PF_T] == state->size[PF_T])
	    state->memRegSize[PF_T] = 1;
	else
	    state->memRegSize[PF_T] = 
		PF_MIN2((state->texRegSize[PF_T] + state->tileSize[PF_T] - 1) /
			state->tileSize[PF_T] + 1 + state->lookahead,
			(state->size[PF_T] + state->tileSize[PF_T] - 1)/
			state->tileSize[PF_T]);

    if(state->memRegSize[PF_R] <= 0)
	if(state->tileSize[PF_R] == state->size[PF_R])
	    state->memRegSize[PF_R] = 1;
	else
	    state->memRegSize[PF_R] = 
		PF_MIN2((state->texRegSize[PF_R] + state->tileSize[PF_R] - 1) /
			state->tileSize[PF_R] + 1 + state->lookahead,
			(state->size[PF_R] + state->tileSize[PF_R] - 1)/
			state->tileSize[PF_R]);
	    
    /*
    ** These values aren't set by the default parser, but can be set
    ** by the user of this routine if desired
    ** XXX Should check parameter values to see if they're in range
    */

    if(state->texRegOrg[PF_S] >= 0 &&
       state->texRegOrg[PF_T] >= 0 &&
       state->texRegOrg[PF_R] >= 0)
	pfImageCacheTexRegionOrg(ic, 
				 state->texRegOrg[PF_S], 
				 state->texRegOrg[PF_T],
				 state->texRegOrg[PF_R]);


    if(state->memRegOrg[PF_S] >= 0 &&
       state->memRegOrg[PF_T] >= 0 &&
       state->memRegOrg[PF_R] >= 0)
	pfImageCacheMemRegionOrg(ic, 
				 state->memRegOrg[PF_S], 
				 state->memRegOrg[PF_T],
				 state->memRegOrg[PF_R]);


    pfImageCacheImageSize(ic, 
			  state->size[PF_S],
			  state->size[PF_T],
			  state->size[PF_R]);

    pfImageCacheMemRegionSize(ic, 
			      state->memRegSize[PF_S], 
			      state->memRegSize[PF_T],
			      state->memRegSize[PF_R]);

    /* header tile offset (optional) */
    if(state->header)
	pfImageTileHeaderOffset(proto, state->header);

    /* tiles in file (optional) */
    /* XXX check tiles in file range */
    if(state->tilesInFile[PF_S] ||
       state->tilesInFile[PF_T] ||
       state->tilesInFile[PF_R])
	pfImageTileNumFileTiles(proto, 
				state->tilesInFile[PF_S],
				state->tilesInFile[PF_T],
				state->tilesInFile[PF_R]);


    /* tilesize */
    pfImageTileSize(proto,
		    state->tileSize[PF_S],
		    state->tileSize[PF_T],
		    state->tileSize[PF_R]);


    /* base name */

    /* XXX base name a valid string? */
    pfImageCacheName(ic, state->base);
    pfImageTileFileName(proto, state->base);

    /* file name format */
    /* XXX format a valid string? */
    /* chek number of params doesn't exceed max */
    if(state->format)
	pfImageCacheTileFileNameFormat(ic, 
				       state->format,
				       state->numParams,
				       state->args);


    /* use texsize for texture if texsize isn't defined */
    if(state->texSize[PF_S] == -1)
    {
	state->texSize[PF_S] = state->texRegSize[PF_S];
	state->texSize[PF_T] = state->texRegSize[PF_T];
	state->texSize[PF_R] = state->texRegSize[PF_R];
    }
    /* Make sure destination texture has power of two sized dimensions */
    if(state->texSize[PF_S] & (state->texSize[PF_S] - 1) ||
       state->texSize[PF_T] & (state->texSize[PF_T] - 1) ||
       state->texSize[PF_R] & (state->texSize[PF_R] - 1))
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: destination texture size "
		 "isn't power of two in each dimension (%d, %d %d)",
		 state->texSize[PF_S],
		 state->texSize[PF_T],
		 state->texSize[PF_R]);
	return NULL; /* we failed */
    }


    pfTexImage(texture, NULL, state->components,
	       state->texSize[PF_S],
	       state->texSize[PF_T],
	       state->texSize[PF_R]);

    /* ext format */
    pfTexFormat(texture, PFTEX_SUBLOAD_FORMAT, PF_ON);

    pfTexFormat(texture, PFTEX_EXTERNAL_FORMAT, state->extFormat);
    pfImageTileFileImageType(proto, state->extFormat);
    pfImageTileMemImageType(proto, state->extFormat);

    /* int format */
    pfTexFormat(texture, PFTEX_INTERNAL_FORMAT, state->intFormat);

    /* img format */
    pfTexFormat(texture, PFTEX_IMAGE_FORMAT, state->imgFormat);
    pfImageTileFileImageFormat(proto, state->imgFormat);
    pfImageTileMemImageFormat(proto, state->imgFormat);

    pfImageCacheTexRegionSize(ic,
			      state->texRegSize[PF_S],
			      state->texRegSize[PF_T],
			      state->texRegSize[PF_R]);

    pfImageCacheTex(ic, texture, level, PFTLOAD_DEST_TEXTURE);
    pfImageCacheTexSize(ic, 
			state->texSize[PF_S],
			state->texSize[PF_T],
			state->texSize[PF_R]);


    if(state->texSize[PF_S] < state->texRegSize[PF_S] ||
       state->texSize[PF_T] < state->texRegSize[PF_T] ||
       state->texSize[PF_R] < state->texRegSize[PF_R])
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: destination texture size (%d %d %d) "
		 "is smaller than tex region size (%d %d %d)",
		 state->texSize[PF_S],
		 state->texSize[PF_T],
		 state->texSize[PF_R],
		 state->texRegSize[PF_S], 
		 state->texRegSize[PF_T], 
		 state->texRegSize[PF_R]);
	return NULL;
    }

#if 0
    /*
    ** Commenting this out, since I don't think there's a corrolation
    ** between tilesize and texsize. Will remove when certain -tomcat
    */
    if(state->tileSize[PF_S] == state->size[PF_S] &&
       state->texSize[PF_S] < state->tileSize[PF_S] ||
       state->tileSize[PF_T] == state->size[PF_T] &&
       state->texSize[PF_T] < state->tileSize[PF_T] ||
       state->tileSize[PF_R] == state->size[PF_R] &&
       state->texSize[PF_R] < state->tileSize[PF_R])
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
		 "pfuMakeImageCache: destination texture size (%d %d %d) "
		 "is smaller than tile size (%d %d %d)",
		 state->texSize[PF_S],
		 state->texSize[PF_T],
		 state->texSize[PF_R],
		 state->tileSize[PF_S], 
		 state->tileSize[PF_T], 
		 state->tileSize[PF_R]);
	return NULL;
    }
#endif

    /* XXX check streams are valid pathnames */
    if(state->sStreams)
	for(i = 0; i <  pfGetNum(state->sStreams); i++)
	{
	    pfImageCacheFileStreamServer(ic, PFIMAGECACHE_S_DIMENSION,
					 i,
					 pfGet(state->sStreams, i));
	}

    if(state->tStreams)
	for(i = 0; i <  pfGetNum(state->tStreams); i++)
	{
	    pfImageCacheFileStreamServer(ic, PFIMAGECACHE_T_DIMENSION,
					 i,
					 pfGet(state->tStreams, i));
	}

    if(state->rStreams)
	for(i = 0; i <  pfGetNum(state->rStreams); i++)
	{
	    pfImageCacheFileStreamServer(ic, PFIMAGECACHE_R_DIMENSION,
					 i,
					 pfGet(state->rStreams, i));
	}



    /* XXX check if default tile is a valid file */
    if(state->defaultTile)
    {
	pfImageTile *defaultTile;
	char *tmp;
	int baselen;

	if(state->base)
	    baselen = strlen(state->base);
	else
	    baselen = 0;

	tmp = malloc(baselen + strlen(state->defaultTile) + 1);

	if(state->base)
	    strcpy(tmp, state->base);
	else
	    strcpy(tmp, "");

	strcat(tmp, state->defaultTile);
	defaultTile = pfNewImageTile(arena);
	pfCopy(defaultTile, proto);
	pfImageTileFileName(defaultTile, tmp);
	free(tmp);

	/* Read the default tile now */
	pfImageTileReadQueue(defaultTile, NULL);
	pfLoadImageTile(defaultTile);
	pfImageTileDefaultTile(proto, defaultTile);
    }

    pfImageCacheProtoTile(ic, proto);
    {
	int n = pfGetNumGlobalQueueServiceProcs();

	for(i = prevNP; i < n; i++)
	    pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
		     "Added Process with PID %d to service read queue 0x%x",
		     pfGetGlobalQueueServiceProcPID(i),
		     pfGetGlobalQueueServiceProcQueue(i));
    }

    pfDelete(proto);
    return ic;
}