/*
* pfdLoadImage.c
*
* $Revision: 1.1 $
* $Date: 2000/11/21 21:39:35 $
*
* Load a database file into an IRIS Performer in-memory data
* structure using the filename's extension to intuit the file
* format. The file is sought in the directories named in the
* active performer file search path (see pfFilePath for more
* details).
*
*
* Copyright 1995, Silicon Graphics, Inc.
* ALL RIGHTS RESERVED
*
* UNPUBLISHED -- Rights reserved under the copyright laws of the United
* States. Use of a copyright notice is precautionary only and does not
* imply publication or disclosure.
*
* U.S. GOVERNMENT RESTRICTED RIGHTS LEGEND:
* Use, duplication or disclosure by the Government is subject to restrictions
* as set forth in FAR 52.227.19(c)(2) or subparagraph (c)(1)(ii) of the Rights
* in Technical Data and Computer Software clause at DFARS 252.227-7013 and/or
* in similar or successor clauses in the FAR, or the DOD or NASA FAR
* Supplement. Contractor/manufacturer is Silicon Graphics, Inc.,
* 2011 N. Shoreline Blvd. Mountain View, CA 94039-7311.
*
* THE CONTENT OF THIS WORK CONTAINS CONFIDENTIAL AND PROPRIETARY
* INFORMATION OF SILICON GRAPHICS, INC. ANY DUPLICATION, MODIFICATION,
* DISTRIBUTION, OR DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY
* PROHIBITED WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF SILICON
* GRAPHICS, INC.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <assert.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dlfcn.h>
#include <Performer/pf.h>
#include <Performer/pfdu.h>
#include <Performer/pfutil.h>
/*
** Read Normal() and ReadDirect() are prototypes for users to create
** custom icache readers. Performer has its own versions of these
** loaders.
*/
static int ReadNormal(pfImageTile *itile, int nTexels)
{
int read;
int os,ot,or,w,h,d;
FILE *fp;
int diskHeader = pfGetImageTileHeaderOffset(itile);
int diskTexelSize = pfGetImageTileFileImageTexelSize(itile);
int prev = pfGetImageTileValidTexels(itile);
int s, t, r; /* which tile in this tile file */
int nS, nT, nR; /* how many tiles in this tile file */
int fileoffset;
pfTileFileNameFuncType fnfunc;
pfGetImageTileOrigin(itile,&os,&ot,&or);
pfGetImageTileSize(itile,&w,&h,&d);
pfGetImageTileFileTile(itile, &s, &t, &r);
pfGetImageTileNumFileTiles(itile, &nS, &nT, &nR);
fnfunc = pfGetImageTileFileNameFunc(itile);
if(fnfunc)
(*fnfunc)(itile);
fp = fopen(pfGetImageTileFileName(itile),"r");
/* offset to proper tile in file */
fileoffset = (s + nS * (t + nT * r)) * diskTexelSize * w * h * d
+ prev * diskTexelSize /* previous reads */
+ diskHeader; /* application-specific file headers */
if(fileoffset)
fseek(fp, fileoffset, SEEK_SET);
read = fread(pfGetImageTileMem(itile), diskTexelSize, nTexels, fp);
if (read < nTexels) {
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"Normal Read of file %s failed",
pfGetImageTileFileName(itile));
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"Only Read %d of %d Texels",
read, nTexels);
/* use default tile if it's available */
pfImageTileDefaultTileMode(itile, 1);
}
fclose(fp);
return read;
}
static int ReadDirect(pfImageTile *itile, int nTexels)
{
#if defined(__linux__has_direct_file_io) || !defined(__linux__)
int bytesRead;
int os,ot,or,w,h,d,op;
char namebuf[512];
int diskTexelSize = pfGetImageTileFileImageTexelSize(itile);
int diskHeader = pfGetImageTileHeaderOffset(itile);
int prev = pfGetImageTileValidTexels(itile);
struct dioattr DIO;
int s, t, r; /* which tile in this tile file */
int nS, nT, nR; /* how many tiles in this tile file */
int fileoffset;
int psize;
pfTileFileNameFuncType fnfunc;
pfGetImageTileOrigin(itile,&os,&ot,&or);
pfGetImageTileSize(itile,&w,&h,&d);
pfGetImageTileFileTile(itile, &s, &t, &r);
pfGetImageTileNumFileTiles(itile, &nS, &nT, &nR);
pfGetImageTileMemInfo(itile, &psize, NULL);
if(!psize)
return ReadNormal(itile, nTexels);
fnfunc = pfGetImageTileFileNameFunc(itile);
if(fnfunc)
(*fnfunc)(itile);
op = open(pfGetImageTileFileName(itile), O_RDONLY|O_DIRECT);
if(op == -1)
return ReadNormal(itile, nTexels);
fcntl(op,F_SETFL,FDIRECT);
fcntl(op,F_DIOINFO,&DIO);
/* offset to proper tile in file */
fileoffset = (s + nS * (t + nT * r)) * diskTexelSize * w * h * d
+ prev * diskTexelSize /* previous reads */
+ diskHeader; /* application-specific file headers */
if(fileoffset)
lseek(op, fileoffset, SEEK_SET);
bytesRead = read(op,pfGetImageTileMem(itile), diskTexelSize*nTexels);
if (bytesRead/diskTexelSize < nTexels)
{
pfNotify(PFNFY_WARN,PFNFY_PRINT,
"Only Read %d texels (%d bytes) of %d in %s",
bytesRead/diskTexelSize,bytesRead,nTexels,
pfGetImageTileFileName(itile));
pfNotify(PFNFY_WARN,PFNFY_PRINT,"Direct IO Read of File %s failed",
pfGetImageTileFileName(itile));
pfNotify(PFNFY_WARN,PFNFY_PRINT,"Trying normal fread for File %s",
pfGetImageTileFileName(itile));
close(op);
return ReadNormal(itile,nTexels);
}
if (bytesRead == -1)
fprintf(stderr,"error on read %d\n",errno);
close(op);
return bytesRead/diskTexelSize;
#endif
}
#if 0
/* needs to be altered to a generic C function */
/* Computes the file name to read to load the image tile */
void
autoConfigFileName(pfImageCache *ic, pfImageTile *tile)
{
static char NULLSTRING[] = "";
char *dname = NULLSTRING;
void* argTable[MAX_TILE_FILENAME_UNIQUE_ARGS];
pfQueue* qs[3];
int dim[3];
int dimCnt[3];
int min, cnt = 0;
int S, T, R;
qs[0] = qs[1] = qs[2] = NULL;
// MAXIMUM NUMBER OF ARGS for fname == 16
// To increase add args to sprintf below
void* args[PFIMAGECACHE_MAX_TILE_FILENAME_ARGS];
pfGetImageTileTileIndex(&S, &T, &R);
if (ic->readQueues[0] != NULL)
if ((qs[cnt] = (pfQueue*)
ic->readQueues[0]->get(S % ic->readQueues[0]->getNum())) != NULL)
{
dim[cnt] = 0;
dimCnt[cnt] = S % ic->readQueues[0]->getNum();
cnt++;
}
if (ic->readQueues[1] != NULL)
if ((qs[cnt] = (pfQueue*)
ic->readQueues[1]->get(T % ic->readQueues[1]->getNum())) != NULL)
{
dim[cnt] = 1;
dimCnt[cnt] = T % ic->readQueues[1]->getNum();
cnt++;
}
if (ic->readQueues[2] != NULL)
if ((qs[cnt] = (pfQueue*)
ic->readQueues[2]->get(R % ic->readQueues[2]->getNum())) != NULL)
{
dim[cnt] = 2;
dimCnt[cnt] = R % ic->readQueues[2]->getNum();
cnt++;
}
if (cnt > 0)
{
min = qs[0]->getNum();
int which = 0;
int ii;
for(ii = 1; ii < cnt; ii++)
if (qs[ii]->getNum() < min)
{
which = ii;
min = qs[ii]->getNum();
}
if (ic->autoSetQueue && (qs[which] != NULL))
{
tile->setReadQueue(qs[which]);
}
if (ic->readNames[dim[which]] != NULL)
dname = (char *)
ic->readNames[dim[which]]->get(dimCnt[which]);
else
dname = NULLSTRING;
}
else
dname = NULLSTRING;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME] =
(void *)(dname);
argTable[PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S] =
(void*)(long)ic->imageSize.S;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T] =
(void*)(long)ic->imageSize.T;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R] =
(void*)(long)ic->imageSize.R;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME] =
(void*)ic->name;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S] =
(void*)(long)S;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T] =
(void*)(long)T;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R] =
(void*)(long)R;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S] =
(void*)(long)tile->s;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T] =
(void*)(long)tile->t;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R] =
(void*)(long)tile->r;
argTable[PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S] =
(void*)((long)(S/tile->nFTilesS));
argTable[PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T] =
(void*)((long)(T/tile->nFTilesT));
argTable[PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R] =
(void*)((long)(R/tile->nFTilesR));
int ii;
for(ii=0; ii< ic->nArgs; ii++)
args[ii] = argTable[ic->argList[ii]];
for(; ii<PFIMAGECACHE_MAX_TILE_FILENAME_ARGS; ii++)
args[ii]=NULL; // avoid UMRs
// Add args here to increase MAX args...
char buf[PF_MAXSTRING + 256];
sprintf(buf,ic->fNameFmt,
args[0], args[1], args[2], args[3],
args[4], args[5], args[6], args[7],
args[8], args[9], args[10], args[11],
args[12], args[13], args[14], args[15]);
pfImageTileFileName(tile, buf);
}
#endif
/*
** Spec for finding config and tile files:
**
** 0. base_name: make obsolecent; semantics for null or missing base names are
** too confusing. Make missing token == missing arg == "" arg ->
** ignore basename
** 1. expand environment variables in filename
** 2. Add basenames
** 2. relative path: add calling config file path
** icache config file -> tiles: add .ic config file path
** cliptexture config file -> icaches and tiles: add .ct config file path
** 3. apply pfFindFile to resolve files that may in the performer search path
** 4. for save the path for tile files so findfile doesn't have to be
** re-searched
*/
/* Helper funcs */
typedef struct {
char *string;
int enumval;
} FormatInfo;
/* Equivalent to OpenGL type */
static FormatInfo ExternalFormats[] = {
{"PFTEX_PACK_8", PFTEX_PACK_8},
{"PFTEX_PACK_16", PFTEX_PACK_16},
{"PFTEX_PACK_USHORT_5_6_5", PFTEX_PACK_USHORT_5_6_5},
{"PFTEX_PACK_USHORT_4_4_4_4", PFTEX_PACK_USHORT_4_4_4_4},
{"PFTEX_UNSIGNED_SHORT_5_5_5_1", PFTEX_UNSIGNED_SHORT_5_5_5_1},
{"PFTEX_BYTE", PFTEX_BYTE},
{"PFTEX_UNSIGNED_BYTE", PFTEX_UNSIGNED_BYTE},
{"PFTEX_SHORT", PFTEX_SHORT},
{"PFTEX_UNSIGNED_SHORT", PFTEX_UNSIGNED_SHORT},
{"PFTEX_INT", PFTEX_INT},
{"PFTEX_UNSIGNED_INT", PFTEX_UNSIGNED_INT},
{"PFTEX_FLOAT", PFTEX_FLOAT},
{"GL_UNSIGNED_BYTE", GL_UNSIGNED_BYTE},
{"GL_BYTE", GL_BYTE},
{"GL_BITMAP", GL_BITMAP},
{"GL_UNSIGNED_SHORT", GL_UNSIGNED_SHORT},
{"GL_SHORT", GL_SHORT},
{"GL_UNSIGNED_INT", GL_UNSIGNED_INT},
{"GL_INT", GL_INT},
{"GL_FLOAT", GL_FLOAT},
#ifdef GL_UNSIGNED_BYTE_3_3_2_EXT
{"GL_UNSIGNED_BYTE_3_3_2_EXT", GL_UNSIGNED_BYTE_3_3_2_EXT},
#endif
#ifdef GL_UNSIGNED_SHORT_4_4_4_4_EXT
{"GL_UNSIGNED_SHORT_4_4_4_4_EXT", GL_UNSIGNED_SHORT_4_4_4_4_EXT},
#endif
#ifdef GL_UNSIGNED_SHORT_5_5_5_1_EXT
{"GL_UNSIGNED_SHORT_5_5_5_1_EXT", GL_UNSIGNED_SHORT_5_5_5_1_EXT},
#endif
#ifdef GL_UNSIGNED_INT_8_8_8_8_EXT
{"GL_UNSIGNED_INT_8_8_8_8_EXT", GL_UNSIGNED_INT_8_8_8_8_EXT},
#endif
#ifdef GL_UNSIGNED_INT_10_10_10_2_EXT
{"GL_UNSIGNED_INT_10_10_10_2_EXT", GL_UNSIGNED_INT_10_10_10_2_EXT},
#endif
{"", -1}
};
/* Equivalent to OpenGL components */
static FormatInfo InternalFormats[] = {
{"PFTEX_I_8", PFTEX_I_8},
{"PFTEX_I_16", PFTEX_I_16},
{"PFTEX_IA_8", PFTEX_IA_8},
{"PFTEX_I_12A_4", PFTEX_I_12A_4},
{"PFTEX_IA_12", PFTEX_IA_12},
{"PFTEX_RGB_4", PFTEX_RGB_4},
{"PFTEX_RGB_5", PFTEX_RGB_5},
{"PFTEX_RGB_8", PFTEX_RGB_8},
{"PFTEX_RGB_12", PFTEX_RGB_12},
{"PFTEX_RGBA_8", PFTEX_RGBA_8},
{"PFTEX_RGBA_12", PFTEX_RGBA_12},
{"PFTEX_RGB5_A1", PFTEX_RGB5_A1},
{"PFTEX_RGBA_4", PFTEX_RGBA_4},
{"PFTEX_DEFAULT", PFTEX_DEFAULT},
{"GL_ALPHA", GL_ALPHA},
{"GL_ALPHA4_EXT", GL_ALPHA4_EXT},
{"GL_ALPHA8_EXT", GL_ALPHA8_EXT},
{"GL_ALPHA12_EXT", GL_ALPHA12_EXT},
{"GL_ALPHA16_EXT", GL_ALPHA16_EXT},
{"GL_LUMINANCE", GL_LUMINANCE},
{"GL_LUMINANCE4_EXT", GL_LUMINANCE4_EXT},
{"GL_LUMINANCE8_EXT", GL_LUMINANCE8_EXT},
{"GL_LUMINANCE12_EXT", GL_LUMINANCE12_EXT},
{"GL_LUMINANCE16_EXT", GL_LUMINANCE16_EXT},
{"GL_LUMINANCE_ALPHA", GL_LUMINANCE_ALPHA},
{"GL_LUMINANCE4_ALPHA4_EXT", GL_LUMINANCE4_ALPHA4_EXT},
{"GL_LUMINANCE6_ALPHA2_EXT", GL_LUMINANCE6_ALPHA2_EXT},
{"GL_LUMINANCE8_ALPHA8_EXT", GL_LUMINANCE8_ALPHA8_EXT},
{"GL_LUMINANCE12_ALPHA4_EXT", GL_LUMINANCE12_ALPHA4_EXT},
{"GL_LUMINANCE12_ALPHA12_EXT", GL_LUMINANCE12_ALPHA12_EXT},
{"GL_LUMINANCE16_ALPHA16_EXT", GL_LUMINANCE16_ALPHA16_EXT},
{"GL_INTENSITY_EXT", GL_INTENSITY_EXT},
{"GL_INTENSITY4_EXT", GL_INTENSITY4_EXT},
{"GL_INTENSITY8_EXT", GL_INTENSITY8_EXT},
{"GL_INTENSITY12_EXT", GL_INTENSITY12_EXT},
{"GL_INTENSITY16_EXT", GL_INTENSITY16_EXT},
{"GL_RGB", GL_RGB},
{"GL_RGB2_EXT", GL_RGB2_EXT},
{"GL_RGB4_EXT", GL_RGB4_EXT},
{"GL_RGB5_EXT", GL_RGB5_EXT},
{"GL_RGB8_EXT", GL_RGB8_EXT},
{"GL_RGB10_EXT", GL_RGB10_EXT},
{"GL_RGB12_EXT", GL_RGB12_EXT},
{"GL_RGB16_EXT", GL_RGB16_EXT},
{"GL_RGBA2_EXT", GL_RGBA2_EXT},
{"GL_RGBA4_EXT", GL_RGBA4_EXT},
{"GL_RGB5_A1_EXT", GL_RGB5_A1_EXT},
{"GL_RGBA", GL_RGBA},
{"GL_RGBA8_EXT", GL_RGBA8_EXT},
{"GL_RGB10_A2_EXT", GL_RGB10_A2_EXT},
{"GL_RGBA12_EXT", GL_RGBA12_EXT},
{"GL_RGBA16_EXT", GL_RGBA16_EXT},
{"", -1}
};
typedef struct {
char *string;
int format;
int components; /* how many components */
} ImageFormatInfo;
/* Equivalent to OpenGL formats */
static ImageFormatInfo ImageFormats[] = {
{"PFTEX_RGB", PFTEX_RGB, 3},
{"PFTEX_LUMINANCE_ALPHA", PFTEX_LUMINANCE_ALPHA, 2},
{"PFTEX_LUMINANCE", PFTEX_LUMINANCE, 1},
{"PFTEX_RGBA", PFTEX_RGBA, 4},
{"GL_RED" , GL_RED, 1},
{"GL_GREEN", GL_GREEN, 1},
{"GL_BLUE", GL_BLUE, 1},
{"GL_ALPHA", GL_ALPHA, 1},
{"GL_LUMINANCE", GL_LUMINANCE, 1},
{"GL_LUMINANCE_ALPHA", GL_LUMINANCE_ALPHA, 2},
{"GL_RGB", GL_RGB, 3},
{"GL_RGBA", GL_RGBA, 4},
{"GL_ABGR_EXT", GL_ABGR_EXT, 4},
{"", -1, -1}
};
static ImageFormatInfo *_pfImageFormatStrToEnum(const char *function,
const char *str)
{
ImageFormatInfo *format;
for(format = ImageFormats;*format->string;format++) {
if(!strcmp(str,format->string))
return format;
}
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"%s: Invalid Image Format %s", function, str);
return NULL;
}
int _pfExternalFormatStrToEnum(const char *function, const char *str)
{
FormatInfo *format;
int val;
for(format = ExternalFormats;*format->string;format++) {
if(!strcmp(str, format->string))
return format->enumval;
}
if(sscanf(str,"%d", &val)) /* if they just give a number, use it */
return val;
else {
pfNotify(PFNFY_WARN,PFNFY_PRINT,
"%s: Invalid External Format %s", function, str);
return format->enumval; /* return invalid value */
}
}
int _pfInternalFormatStrToEnum(const char *function, const char *str)
{
FormatInfo *format;
int val;
for(format = InternalFormats;*format->string;format++) {
if(!strcmp(str, format->string))
return format->enumval;
}
if(sscanf(str,"%d", &val)) /* if they just give a number, use it */
return val;
else {
pfNotify(PFNFY_WARN,PFNFY_PRINT,
"%s: Invalid Internal Format %s", function, str);
return format->enumval; /* return invalid value */
}
}
typedef struct {
char *string;
int val;
} FnameInfo;
static FnameInfo Fnames[] = {
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME",
PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME},
{"PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME",
PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R},
{"", -1}
};
static int _pfFnameArgToNum(const char *str)
{
FnameInfo *fnamearg;
for(fnamearg = Fnames; *fnamearg->string;fnamearg++) {
if(!strcmp(str, fnamearg->string))
return fnamearg->val;
}
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: invalid file parameter name; %s\n",
str);
return fnamearg->val;
}
typedef struct {
char *string;
int token;
} pfCTFnameConvert;
/*
** XXX This is a disgusting merger of two name spaces
** it should be re-architected. It makes it possible to
** use image cache arguments in the the tile_format string of cliptextures
** so that image cache config files can be optional
*/
pfCTFnameConvert CTFnameTokens[] = {
{"PFCLIPTEX_FNAMEARG_LEVEL",
PFCLIPTEX_FNAMEARG_LEVEL},
{"PFCLIPTEX_FNAMEARG_IMAGE_CACHE_BASE",
PFCLIPTEX_FNAMEARG_IMAGE_CACHE_BASE},
{"PFCLIPTEX_FNAMEARG_LEVEL_SIZE",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S},
{"PFCLIPTEX_FNAMEARG_TILE_BASE",
PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME},
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R",
PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R",
PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R",
PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R},
{"PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME",
PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME},
{"PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME",
PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T},
{"PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R",
PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R},
{"", PFCLIPTEX_FNAMEARG_INVALID}
};
/* convert a token string to an integer */
int _pfCTFnameArgToNum(const char *buf)
{
pfCTFnameConvert *tokenlist;
for(tokenlist = CTFnameTokens;*tokenlist->string;tokenlist++) {
if(!strcmp(tokenlist->string, buf))
return tokenlist->token;
}
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: Invalid Argument Token Name: %s\n",
buf);
return PFCLIPTEX_FNAMEARG_INVALID;
}
/* Support for Tokens in Config Files */
/* XXX TODO: split tokens for icache and cliptexture */
typedef enum {
START_OF_FILE = -2,
BAD_TOKEN, /* -1 */
CLIP_TEX_VERSION, /* 0 */
IMAGE_CACHE_VERSION,
COMMENT,
EXT_FORMAT,
INT_FORMAT,
IMG_FORMAT,
VSIZE,
CLIPSIZE,
INVALID_BORDER,
EFFECTIVE_LEVELS,
ALLOCATED_LEVELS,
LAST_ICACHE_SIZE,
ICACHE_BASE,
TILE_BASE,
ICACHE_FORMAT,
NUM_ICACHE_PARAMS,
ICACHE_PARAMS,
TILE_FORMAT,
NUM_TILE_PARAMS,
TILE_PARAMS,
ICACHE_FILES,
TILE_FILES,
CACHE_SIZE,
VALID_REGION,
VALID_REGION_DST,
HEADER_OFFSET,
TILES_IN_FILE,
TILE_SIZE,
NUM_STREAM_SERVERS,
S_STREAM_SERVERS,
T_STREAM_SERVERS,
R_STREAM_SERVERS,
DEFAULT_TILE,
LEVEL_PHASE_SHIFT,
LEVEL_PHASE_MARGIN,
TEX_REGION,
MEM_REGION,
PAGE_SIZE, /* size for reading from disk with direct i/o */
READ_FUNC, /* user-defined read function */
LOOKAHEAD, /* mem region border in tiles */
END_OF_FILE /* run out of file to read */
} Token;
/* Matching a token string to a token */
typedef struct {
char *str;
Token token;
} TokStr;
static TokStr TokenStrings[] = {
{"ct_version1.0", CLIP_TEX_VERSION},
{"ct_version2.0", CLIP_TEX_VERSION},
{"ic_version1.0", IMAGE_CACHE_VERSION},
{"ic_version2.0", IMAGE_CACHE_VERSION}, /* support new icache api */
{"#", COMMENT}, /* shell-style comment */
{"//", COMMENT}, /* C++-style comment */
{";", COMMENT}, /* LISP-style comment */
{"rem", COMMENT}, /* BASIC-style comment */
{"comment", COMMENT}, /* ALGOL-style comment */
{"num_streams", COMMENT}, /* no longer needed */
{"ext_format", EXT_FORMAT},
{"int_format", INT_FORMAT},
{"img_format", IMG_FORMAT},
{"virt_size", VSIZE}, /* old api for icache -> icache_size */
{"icache_size", VSIZE}, /* new api */
{"cache_size", CACHE_SIZE}, /* old api -> mem region size */
{"valid_region", VALID_REGION}, /* old api -> tex_region_size */
{"valid_region_dst", VALID_REGION_DST}, /* old api -> tex_size */
{"tex_size", VALID_REGION_DST}, /* new api */
{"header_offset", HEADER_OFFSET},
{"tiles_in_file", TILES_IN_FILE},
{"tile_size", TILE_SIZE},
{"tile_base", TILE_BASE},
{"tile_format", TILE_FORMAT},
{"num_tile_params", NUM_TILE_PARAMS},
{"tile_params", TILE_PARAMS},
{"tile_files", TILE_FILES},
{"s_stream", S_STREAM_SERVERS},
{"t_stream", T_STREAM_SERVERS},
{"r_stream", R_STREAM_SERVERS},
{"s_streams", S_STREAM_SERVERS}, /* deal with typo in man pages */
{"t_streams", T_STREAM_SERVERS},
{"r_streams", R_STREAM_SERVERS},
{"default_tile", DEFAULT_TILE},
{"clip_size", CLIPSIZE},
{"invalid_border", INVALID_BORDER},
{"effective_levels", EFFECTIVE_LEVELS},
{"allocated_levels", ALLOCATED_LEVELS},
{"smallest_icache", LAST_ICACHE_SIZE},
{"icache_base", ICACHE_BASE},
{"icache_format", ICACHE_FORMAT},
{"num_icache_params", NUM_ICACHE_PARAMS},
{"icache_params", ICACHE_PARAMS},
{"icache_files", ICACHE_FILES},
{"phase_shift", LEVEL_PHASE_SHIFT},
{"phase_margin", LEVEL_PHASE_MARGIN},
{"tex_region_size", TEX_REGION}, /* new api */
{"mem_region_size", MEM_REGION}, /* new api */
{"page_size", PAGE_SIZE}, /* new api; page size to use for direct i/o */
{"read_func", READ_FUNC}, /* user-defined read function */
{"lookahead", LOOKAHEAD}, /* mem region tile border */
{"start of file", START_OF_FILE}, /* default token values */
{"", BAD_TOKEN}
};
/*
** Find and return the next token
** Current limitiation: tokens must be surrounded by whitespace
** XXX TODO: remove following whitespace restriction.
*/
static Token isToken(char *str, FILE *fp)
{
TokStr *token;
int length;
char *ptr;
for(token = TokenStrings;token->token != BAD_TOKEN;token++) {
if(ptr = strstr(str, token->str)) {
if(ptr == str)
{
/* string is more than just token */
length = strlen(str) - strlen(token->str);
if(length)
{
/* subtract extra */
fseek(fp, -length, SEEK_CUR);
}
return token->token;
}
}
}
/* couldn't find a match */
return BAD_TOKEN;
}
static Token nextToken(FILE *fp)
{
int read;
Token token;
char tokstr[256];
switch(fscanf(fp, "%s", tokstr)) {
case 0:
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"nextToken(); failed trying to parse string %s\n",
tokstr);
return BAD_TOKEN;
case EOF:
return END_OF_FILE;
default:
token = isToken(tokstr, fp);
if(token == BAD_TOKEN)
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"Encountered invalid token \"%s\"\n",
tokstr);
return token;
}
}
/*
** Find the string associated with the token
** for error reporting
*/
static char *tokenString(Token token)
{
TokStr *tokens;
for(tokens = TokenStrings;*tokens->str;tokens++) {
if(tokens->token == token)
return tokens->str;
}
return "BAD_TOKEN";
}
#define VERSION_ENFORCE(str) \
if(!version_enforce) { \
pfNotify(PFNFY_WARN, PFNFY_PRINT, \
"%s: version token doesn't start file!\n", \
str); \
version_enforce = 1; \
}
/*
** Parse tile_base or icache_base strings to expand any environment
** variable name at the beginning.
** Assuming that str must be at least a null string
*/
void _pfExpandBase(const char *fname, char *str)
{
char var[256]; /* variable names */
char *offset; /* skip over variable name */
char *value; /* environment variable value */
char tmp[512]; /* temporary string */
char *defaultvalue; /* if not defined in environment */
if(*str != '$')
return; /* no varible, no expansion */
*var = 0;
switch(str[1]) { /* how to parse variable */
case '(':
case '{':
sscanf(&str[2],"%256[^)}]", var);
break;
default:
sscanf(&str[1],"%256[^./]", var);
break;
}
if(*var) {
*tmp = 0;
defaultvalue = NULL;
if ((defaultvalue = strstr(var, ":-")) != NULL)
{
*defaultvalue = '\0';
defaultvalue += 2;
}
if((value = getenv(var)) != NULL)
strcpy(tmp, value); /* if defined */
else if (defaultvalue != NULL)
strcpy(tmp, defaultvalue);
else { /* if not defined */
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
"%s: variable %s undefined\n", fname, var);
*tmp = 0; /* use empty string if variable undefined */
}
if(strchr(tmp, ':')) {
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"%s: Invalid value %s (contains ':') for variable %s\n",
fname, tmp, var);
return;
}
/* skip over variable name part of original string */
if(offset = strpbrk(str, ")}")) /* $(NAME) or ${NAME} */
{
offset++;
strcat(tmp, offset);
}
else if(offset = strpbrk(str, "./")) /* $NAME/ or $NAME.[.] */
strcat(tmp, offset);
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT, "%s: expanding %s to %s\n",
fname, str, tmp);
strcpy(str, tmp);
return;
}
}
void
_pfExpandRelative(const char *funname, const char *path, char *fname)
{
char *start;
int count;
char *end;
char *tmp;
if(*fname == '/')
return; /* not a relative filepath */
/* remove last filename in path */
end = strrchr(path, '/');
if(!end)
return; /* fname is relative, and path has no slashes */
count = end - path + 1;
tmp = (char *)malloc(strlen(fname) + count + 1);
if(!tmp)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"%s _pfExpandRelative malloc failed; no expansion done",
funname);
return;
}
strncpy(tmp, path, count);
tmp[count] = '\0'; /* add the null so it's a string */
strcat(tmp, fname);
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT, "%s: expanding %s to %s\n",
funname, fname, tmp);
strcpy(fname, tmp);
free(tmp);
}
enum {NONE, FILELIST, FORMAT};
typedef struct {
int type; /* list of files or format string and args */
char *base; /* image cache file base name */
char *format; /* image cache name format string */
int numFnameArgs; /* number of arguments in filename string */
int fnameTokens[PFCLIPTEX_MAX_NUM_FORMAT_ARGS];
pfList *files; /* list of user supplied config filenames */
} pfdClipTexData;
void initClipTexData(pfdClipTexData *config)
{
config->type = NONE; /* a pseudo-union */
config->base = NULL;
/* if format string and arguments */
config->format = NULL;
config->numFnameArgs = 0;
config->fnameTokens[0] = -1;
/* if list of files */
config->files = NULL;
}
void freeClipTexData(pfdClipTexData *config)
{
int i;
/* function should work whether format or files */
if(config->base)
free(config->base);
if(config->format)
free(config->format);
if(config->files)
{
for(i = 0; i < pfGetNum(config->files); i++)
free(pfGet(config->files, i));
pfDelete(config->files);
}
}
#define MAX_STR_BUF 2048
typedef char Strbuf[MAX_STR_BUF];
/* Hack to expand tokens into actual parameters values. Used as scanf args */
#define PFCLIPTEX_EVAL_FORMAT_ARGS(TOKENS, NUMARGS, ARGS, STR) \
/* stay below PFCLIPTEX_MAX_NUM_FORMAT_ARGS */ \
for(i=0;i < NUMARGS;i++) { \
switch(TOKENS[i]) { \
case PFCLIPTEX_FNAMEARG_INVALID: \
pfNotify(PFNFY_WARN, PFNFY_PRINT, \
"pfuMakeClipTexture: invalid token" \
"value %d found while parsing %s" \
"pfuMakeClipTexture: %s probably wasn't" \
"initialized", \
TOKENS[i], STR, STR); \
return NULL; /* bail out; config file unusable */ \
case PFCLIPTEX_FNAMEARG_LEVEL: \
ARGS[i] = (void *)level; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_S: \
ARGS[i] = (void *)levelSizeS; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_T: \
ARGS[i] = (void *)levelSizeT; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_VSIZE_R: \
ARGS[i] = (void *)1; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_S: \
case PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_T: \
case PFIMAGECACHE_TILE_FILENAMEARG_TILENUM_R: \
case PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_S: \
case PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_T: \
case PFIMAGECACHE_TILE_FILENAMEARG_TILEORG_R: \
case PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_S: \
case PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_T: \
case PFIMAGECACHE_TILE_FILENAMEARG_FILENUM_R: \
ARGS[i] = (void *)0; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_STREAMSERVERNAME: \
pfNotify(PFNFY_WARN, PFNFY_PRINT, \
"pfuMakeClipTexture: sorry, streams" \
"not supported yet for image cache" \
"cliptexture config files"); \
return NULL; /* bail out; config file unusable */ \
case PFCLIPTEX_FNAMEARG_IMAGE_CACHE_BASE: \
ARGS[i] = (void *)icData->base; \
break; \
case PFIMAGECACHE_TILE_FILENAMEARG_CACHENAME: \
ARGS[i] = (void *)tileData->base; \
break; \
default: \
pfNotify(PFNFY_WARN, PFNFY_PRINT, \
"pfuMakeClipTexture: unknown token" \
"value %d found while parsing %s", \
TOKENS[i], STR); \
return NULL; /* bail out; config file unusable */ \
} \
} \
for (; i < PFCLIPTEX_MAX_NUM_FORMAT_ARGS; ++i) \
ARGS[i] = NULL;
pfImageTile *pfdLoadImageTileFormat(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
int i;
int levelSizeS, levelSizeT;
pfImageTile *it;
struct stat dummy; /* for checking the existence of files */
Strbuf fname;
pfdClipTexData *tileData, *icData = NULL; /* for macro */
void *FnameArgs[PFCLIPTEX_MAX_NUM_FORMAT_ARGS];
void *arena = pfGetSharedArena();
char itFilePath[2048];
tileData = (pfdClipTexData *)config->tileData;
levelSizeS = config->imgSize[PF_S] >> level;
levelSizeT = config->imgSize[PF_T] >> level;
PFCLIPTEX_EVAL_FORMAT_ARGS(tileData->fnameTokens,
tileData->numFnameArgs,
FnameArgs,
"image tile tokens");
PFASSERTALWAYS(PFCLIPTEX_MAX_NUM_FORMAT_ARGS == 6);
sprintf(fname, tileData->format,
FnameArgs[0], FnameArgs[1], FnameArgs[2],
FnameArgs[3], FnameArgs[4], FnameArgs[5]);
if(!pfFindFile(fname, itFilePath, R_OK))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageTileFormat: "
"couldn't find pyramid image tile file %s, level %d",
fname, level);
return NULL;
}
/* check for the existence of all pyramid tile files */
if(stat(itFilePath, &dummy))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageTileFormat: "
"couldn't open pyramid image tile %s, level %d ",
itFilePath, level);
return NULL;
}
it = pfNewImageTile(arena);
pfImageTileFileName(it, itFilePath);
return it;
}
/* zero will give erroneus results */
int powof2(int val)
{
int i = 0;
while(val)
{
i++;
val >>= 1;
}
return i;
}
pfImageTile *pfdLoadImageTileFiles(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
pfImageTile *it;
int i;
int levelSizeS;
struct stat dummy; /* for checking the existence of files */
char *fname;
pfdClipTexData *tileData;
char itFilePath[2048];
void *arena = pfGetSharedArena();
tileData = (pfdClipTexData *)config->tileData;
levelSizeS = config->imgSize[PF_S] >> level;
i = powof2(config->minICache[PF_S]) - powof2(levelSizeS) - 1;
fname = pfGet(tileData->files, i);
if(!pfFindFile(fname, itFilePath, R_OK))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageTileFiles: "
"couldn't find pyramid image tile file %s, level %d",
fname, level);
return NULL;
}
/* check for the existence of all pyramid tile files */
if(stat(itFilePath, &dummy))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageTileFiles: pyramid image tile %s, level %d "
"doesn't exist", itFilePath, level);
return NULL;
}
it = pfNewImageTile(arena);
pfImageTileFileName(it, itFilePath);
return it;
}
/* XXX TODO: Document the circumstances when this function would be used */
/* use default names */
pfImageTile *pfdLoadImageTileDefault(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
void *arena = pfGetSharedArena();
return pfNewImageTile(arena);
}
pfImageCache *pfdLoadImageCacheFormat(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
int i;
int levelSizeS, levelSizeT;
pfdClipTexData *icData, *tileData = NULL; /* for macro */
Strbuf fname;
pfImageCache *ic;
pfuImgCacheConfig icConfig;
void *FnameArgs[PFCLIPTEX_MAX_NUM_FORMAT_ARGS];
icData = (pfdClipTexData *)config->icData;
levelSizeS = config->imgSize[PF_S] >> level;
levelSizeT = config->imgSize[PF_T] >> level;
PFCLIPTEX_EVAL_FORMAT_ARGS(icData->fnameTokens,
icData->numFnameArgs,
FnameArgs,
"image cache tokens");
PFASSERTALWAYS(PFCLIPTEX_MAX_NUM_FORMAT_ARGS == 6);
sprintf(fname, icData->format,
FnameArgs[0], FnameArgs[1], FnameArgs[2],
FnameArgs[3], FnameArgs[4], FnameArgs[5]);
pfuInitImgCacheConfig(&icConfig);
/* pass parameters from cliptex config to icache config as needed */
icConfig.pageSize = config->pageSize;
icConfig.readFunc = config->readFunc;
icConfig.lookahead = config->lookahead;
icConfig.clipSize = config->clipSize;
return pfdLoadImageCacheState(fname, (pfTexture *)ct, level, &icConfig);
}
pfImageCache *pfdLoadImageCacheFiles(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
char *fname;
pfdClipTexData *icData;
pfuImgCacheConfig icConfig;
icData = (pfdClipTexData *)config->icData;
fname = pfGet(icData->files, level);
pfuInitImgCacheConfig(&icConfig);
/* pass parameters from cliptex config to icache config as needed */
icConfig.pageSize = config->pageSize;
icConfig.readFunc = config->readFunc;
icConfig.lookahead = config->lookahead;
icConfig.clipSize = config->clipSize;
return pfdLoadImageCacheState(fname, (pfTexture *)ct, level, &icConfig);
}
pfImageCache *pfdLoadImageCacheAuto(pfClipTexture *ct, int level,
struct _pfuClipTexConfig *config)
{
int i;
pfImageCache *ic;
pfuImgCacheConfig icConfig;
pfdClipTexData *icData;
pfdClipTexData *tileData;
int levelSizeS, levelSizeT;
levelSizeS = config->imgSize[PF_S] >> level;
levelSizeT = config->imgSize[PF_T] >> level;
icData = (pfdClipTexData *)config->icData;
tileData = (pfdClipTexData *)config->tileData;
pfuInitImgCacheConfig(&icConfig);
icConfig.extFormat = config->extFormat;
icConfig.intFormat = config->intFormat;
icConfig.imgFormat = config->imgFormat;
icConfig.components = config->components;
icConfig.size[PF_S] = levelSizeS;
icConfig.size[PF_T] = levelSizeT;
icConfig.size[PF_R] = 1;
/* tilesize */
if(config->tileSize[PF_S] < 0 ||
config->tileSize[PF_T] < 0 ||
config->tileSize[PF_R] < 0)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuMakeClipTexture: you must provide image "
"tile sizes if no image caches are specified");
}
icConfig.tileSize[PF_S] =
PF_MIN2(config->tileSize[PF_S], levelSizeS);
icConfig.tileSize[PF_T] =
PF_MIN2(config->tileSize[PF_T], levelSizeT);
icConfig.tileSize[PF_R] = config->tileSize[PF_R];
icConfig.texRegSize[PF_S] = PF_MIN2(config->clipSize, levelSizeS);
icConfig.texRegSize[PF_T] = PF_MIN2(config->clipSize, levelSizeT);
icConfig.texRegSize[PF_R] = 1;
icConfig.texSize[PF_S] = PF_MIN2(config->clipSize, levelSizeS);
icConfig.texSize[PF_T] = PF_MIN2(config->clipSize, levelSizeT);
icConfig.texSize[PF_R] = 1;
if(!tileData->format)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuMakeClipTexture: must provide tile file format "
"if no image caches supplied");
}
/*
** Semantics brain-dead for no.ic files with tile_base defined
** we'll de-support tile_base and icace_base over time (not needed
** anymore anyway).
**
** tileData->base can probably never equal "" anymore; (empty
** string comes from undefined environment variable expansion)
** relative pathname expansion will replace it with a path, but
** changing it just before 2.2 release is too dangerous; I'll
** just add the check for null pointer. Enviroment variable
** expansion should probably be overhauled. -tomcat
*/
if(tileData->base)
if(*tileData->base == '\0') /* if empty string */
icConfig.base = "./"; /* guess that base should be this directory */
else
icConfig.base = tileData->base;
icConfig.format = tileData->format;
icConfig.numParams = tileData->numFnameArgs;
for(i = 0; i < icConfig.numParams; i++)
icConfig.args[i] = tileData->fnameTokens[i];
icConfig.pageSize = config->pageSize;
icConfig.readFunc = config->readFunc;
icConfig.lookahead = config->lookahead;
icConfig.clipSize = config->clipSize;
/* default tile (use tile base) */
ic = pfuMakeImageCache((pfTexture *)ct, level, &icConfig);
/* don't free these; they're shared */
icConfig.base = NULL;
icConfig.format = NULL;
pfuFreeImgCacheConfig(&icConfig);
if (config->lookahead != 1 && level == 0) /* only do this once per cliptex*/
{
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " ");
pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "WARNING: pfdLoadClipTexture():");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " no-ic config file \"%s\"",
config->name);
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " contains the 'lookahead' token;");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " this was broken in Performer2.2 and 2.2.1");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " and is now fixed, so your program may");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " use more memory than before; please tune your");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " configuration file and application accordingly.");
pfNotify(PFNFY_DEBUG, PFNFY_MORE, " ");
}
return ic;
}
/*
** Read in Configuration information to Load a Clip Texture
**
** Arguments:
**
** configuration file name
**
** Configuration File Clip Texture Format:
**
** comments: start "#" "comment" or "//" until end of line. May go anywhere
** a token is expected.
**
** Treat tokens as if order dependent. Some tokens and values can be
** omitted to use defaults. Don't use token without a value.
**
** ct_version1.0 must be at top of file
** ext_format <format string> (external format of stored texels)
** int_format <format string> (internal format used by graphics hw)
** img_format <format string> (image format of stored texels)
** virt_size <int> <int> <int> (size of entire texture)
** clip_size <int> (size of square of texture levels in hardware)
** invalid_border <int> ()
** smallest_icache <int> (dimensions of lowest icache level in clip texture)
** phase_shift <int list> (list of phase shift pairs (s,t) for each level)
** phase_margin <int list> (list of phase margins for each level)
**
** icache_base <file path string> (root of icache config filenames)
** icache_format <scanf string> (icache config fnames no string: list files)
** num_icache_params <int> (number of format arguments to follow)
** icache_params <string list> (format tokens in order)
**
** header_offset <int> (byte offset to skip user's tile file headers)
** tile_base <file path string> (root of pyramid tile filenames)
** tile_format <scanf string> (pyramid tile fnames no string; list tile files)
** num_tile_params <int> (number of format arguments to follow)
** tile_params <string list> (format tokens in order)
**
** icache_files <list of filenames> (only if icache_format is default )
** tile_files <list of filenames> (pyramid part; only if tile_format default)
**
*/
pfClipTexture*
pfdLoadClipTexture(const char *fileName)
{
return pfdLoadClipTextureState(fileName, NULL);
}
pfClipTexture *
pfdLoadClipTextureState(const char *fileName, pfuClipTexConfig *state)
{
Strbuf buf;
FILE *fp;
pfClipTexture *ct;
pfuClipTexConfig stateInfo;
int i, j, len, cnt;
int done = FALSE; /* done parsing file? */
int failed = FALSE; /* to find multiple errors */
Token lastToken = START_OF_FILE; /* for analyzing parse failures */
Token curToken = START_OF_FILE;
int version_enforce = 0;
int iCacheType = 0; /* how icache file is specified */
char ctFilePath[2048]; /* get this file's filepath for relative pathnames*/
pfdClipTexData icData, tileData;
pfdClipTexData *data[2];
char **strptr, *str;
initClipTexData(&icData);
initClipTexData(&tileData);
/* pfdOpenFile should be using this */
if(!pfFindFile(fileName, ctFilePath, R_OK))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexure: couldn't find clip texture config file %s",
fileName);
return NULL;
}
if ((fp = pfdOpenFile(ctFilePath)) == NULL)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexure: couldn't open clip texture config file %s",
fileName);
return NULL;
}
if(!state)
{
state = &stateInfo;
pfuInitClipTexConfig(state);
}
/*
** XXX in the future, the user can override name by
** providing a name token
*/
state->name = ctFilePath; /* give this cliptexture a default name */
while(!done) {
switch(curToken = nextToken(fp)) {
case CLIP_TEX_VERSION:
if(lastToken != START_OF_FILE) {
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: "
"version token doesn't start file!\n");
}
version_enforce = 1;
break;
case COMMENT:
/* Comment from token to end of current line */
while(fscanf(fp, "%*[^\n]"));
lastToken = COMMENT;
break;
case EXT_FORMAT:
/* Read in External Format of Image Tile */
fscanf(fp,"%s",buf);
state->extFormat =
_pfExternalFormatStrToEnum("pfdLoadClipTexture", buf);
if(state->extFormat < 0)
failed = TRUE;
lastToken = EXT_FORMAT;
break;
case INT_FORMAT:
/* Read in Internal Format of Image Tile */
fscanf(fp,"%s",buf);
state->intFormat =
_pfInternalFormatStrToEnum("pfdLoadClipTexture", buf);
if(state->intFormat < 0)
failed = TRUE;
lastToken = INT_FORMAT;
break;
case IMG_FORMAT:
{
/* Read in Image Format of Image Tile */
ImageFormatInfo *imageFormat;
fscanf(fp, "%s", buf);
imageFormat = _pfImageFormatStrToEnum("pfdLoadClipTexture", buf);
if(!imageFormat)
failed = TRUE;
else
{
state->imgFormat = imageFormat->format;
state->components = imageFormat->components;
}
lastToken = IMG_FORMAT;
break;
}
case VSIZE:
/* Read Image (Virtual) Size */
fscanf(fp, "%d %d %d",
&state->imgSize[PF_S],
&state->imgSize[PF_T],
&state->imgSize[PF_R]);
lastToken = VSIZE;
VERSION_ENFORCE("pfdLoadClipTexture");
break;
case CLIPSIZE:
/* Read Clip Size */
fscanf(fp, "%d", &state->clipSize);
lastToken = CLIPSIZE;
break;
case INVALID_BORDER:
/* Read Invalid Border Value */
fscanf(fp, "%d", &state->invBorder);
lastToken = INVALID_BORDER;
break;
case EFFECTIVE_LEVELS:
/* Read Effective Levels Value */
fscanf(fp, "%d", &state->effLevels);
lastToken = EFFECTIVE_LEVELS;
break;
case ALLOCATED_LEVELS:
/* Read Allocated Levels Value */
fscanf(fp, "%d", &state->allocLevels);
lastToken = ALLOCATED_LEVELS;
break;
case TILE_SIZE:
/* Read in Tile Width,Height,Depth specified in texels */
/* Only used if no image caches specified */
fscanf(fp,"%d %d %d",
&state->tileSize[PF_S],
&state->tileSize[PF_T],
&state->tileSize[PF_R]);
lastToken = TILE_SIZE;
break;
case LAST_ICACHE_SIZE:
fscanf(fp, "%d %d %d",
&state->minICache[PF_S],
&state->minICache[PF_T],
&state->minICache[PF_R]);
lastToken = LAST_ICACHE_SIZE;
break;
case LEVEL_PHASE_SHIFT:
/* obsolete; ignore */
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadClipTexture: phase shift is no longer "
"supported. Arguments ignored");
while(1) /* consume arguments */
{
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break;
if(isToken(buf, fp) != BAD_TOKEN) /* done reading params */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
break;
case LEVEL_PHASE_MARGIN:
/* obsolete; ignore */
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadClipTexture: phase margin is no longer "
"supported. Arguments ignored");
while(1) /* consume arguments */
{
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break;
if(isToken(buf, fp) != BAD_TOKEN) /* done reading params */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
break;
case ICACHE_BASE:
/* get image cache fname base */
{
int status;
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
buf[0] = '\0'; /* use empty string */
}
_pfExpandBase("pfdLoadClipTexture", buf); /* expand env var */
if(icData.base)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image cache filename base "
"can only be set once");
failed = TRUE;
}
else
{
icData.base = (char *)malloc(strlen(buf) + 1);
strcpy(icData.base, buf);
}
lastToken = ICACHE_BASE;
}
break;
case TILE_BASE:
/* get tile fname base */
{
int status;
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
buf[0] = '\0'; /* use empty string */
}
_pfExpandBase("pfdLoadClipTexture", buf); /* expand env var */
if(tileData.base)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image tile filename base "
"can only be set once");
failed = TRUE;
}
else
{
tileData.base = (char *)malloc(strlen(buf) + 1);
strcpy(tileData.base, buf);
}
lastToken = TILE_BASE;
}
break;
case ICACHE_FORMAT:
/* get image cache file name format */
fscanf(fp, "%s", buf);
if(icData.format)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image icache filename format "
"can only be set once");
failed = TRUE;
}
else
{
_pfExpandBase("pfdLoadClipTexture", buf); /* expand env var */
icData.type |= FORMAT;
icData.format = (char *)malloc(strlen(buf) + 1);
strcpy(icData.format, buf);
}
lastToken = ICACHE_FORMAT;
break;
case NUM_ICACHE_PARAMS:
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadClipTexture: num_icache_params obsolete. "
"Argument ignored");
fscanf(fp, "%*d");
lastToken = NUM_ICACHE_PARAMS;
break;
case ICACHE_PARAMS:
icData.type |= FORMAT;
for(i = 0; 1; i++)
{
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break;
if(isToken(buf, fp) == BAD_TOKEN) /* still reading params */
{
int arg;
arg = _pfCTFnameArgToNum(buf);
if(arg == PFCLIPTEX_FNAMEARG_INVALID)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: invalid image cache "
"parameter token");
failed = TRUE;
}
if(i < PFCLIPTEX_MAX_NUM_FORMAT_ARGS)
icData.fnameTokens[i] = arg;
}
else /* unread last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
if(i > PFCLIPTEX_MAX_NUM_FORMAT_ARGS)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: number of image cache filename "
"arguments (%d) exceeds maximum (%d)",
i, PFCLIPTEX_MAX_NUM_FORMAT_ARGS);
failed = TRUE;
}
icData.numFnameArgs = i;
lastToken = ICACHE_PARAMS;
break;
case HEADER_OFFSET:
/* Read in file header offset */
fscanf(fp, "%d", &state->hdrOffset);
lastToken = HEADER_OFFSET;
break;
case TILE_FORMAT:
/* get tile file name format */
fscanf(fp, "%s", buf);
if(tileData.format)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image tile filename format "
"can only be set once");
failed = TRUE;
}
else
{
_pfExpandBase("pfdLoadClipTexture", buf); /* expand env var */
tileData.type |= FORMAT;
tileData.format = (char *)malloc(strlen(buf) + 1);
strcpy(tileData.format, buf);
}
lastToken = TILE_FORMAT;
break;
case NUM_TILE_PARAMS:
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadClipTexture: num_tile_params obsolete. "
"Argument ignored");
fscanf(fp, "%*d");
lastToken = NUM_TILE_PARAMS;
break;
case TILE_PARAMS:
tileData.type |= FORMAT;
for(i = 0;1;i++) /* read everything until the next valid token */
{
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break;
if(isToken(buf, fp) == BAD_TOKEN) /* still reading params */
{
int arg;
arg = _pfCTFnameArgToNum(buf);
if(arg == PFCLIPTEX_FNAMEARG_INVALID)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: invalid tile "
"parameter token");
failed = TRUE;
}
if(i < PFCLIPTEX_MAX_NUM_FORMAT_ARGS)
tileData.fnameTokens[i] = arg;
}
else /* unread last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
if(i > PFCLIPTEX_MAX_NUM_FORMAT_ARGS)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: number of tile filename arguments"
" (%d) exceeds maximum (%d)",
i, PFCLIPTEX_MAX_NUM_FORMAT_ARGS);
failed = TRUE;
}
tileData.numFnameArgs = i;
lastToken = TILE_PARAMS;
break;
case ICACHE_FILES:
if(icData.files)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image icache filename format "
"can only be set once");
failed = TRUE;
}
else
{
icData.type |= FILELIST;
icData.files = pfNewList(sizeof(char *), 10, NULL);
for(i = 0;1; i++) {
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break; /* invalid token or end of file */
if(isToken(buf, fp) == BAD_TOKEN) /* still reading streams */
{
int length;
char *icache;
_pfExpandBase("pfdLoadClipTexture",
buf); /* expand env var */
length = strlen(buf) + 1;
if(icData.base)
length += strlen(icData.base);
icache = (char *)malloc(length);
if(icData.base)
{
strcpy(icache, icData.base);
strcat(icache, buf);
}
else
strcpy(icache, buf);
pfAdd(icData.files, icache);
}
else /* "unread" last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
}
lastToken = ICACHE_FILES;
break;
case TILE_FILES:
if(tileData.files)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: image icache filename format "
"can only be set once");
failed = TRUE;
}
else
{
tileData.type |= FILELIST;
tileData.files = pfNewList(sizeof(char *), 10, NULL);
for(i = 0;1; i++) {
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break; /* invalid token or end of file */
if(isToken(buf, fp) == BAD_TOKEN) /* still reading streams */
{
int length;
char *tile;
_pfExpandBase("pfdLoadClipTexture",
buf); /* expand env var */
length = strlen(buf) + 1;
if(tileData.base)
length += strlen(tileData.base);
tile = (char *)malloc(length);
if(tileData.base)
{
strcpy(tile, tileData.base);
strcat(tile, buf);
}
else
strcpy(tile, buf);
pfAdd(tileData.files, tile);
}
else /* "unread" last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
}
lastToken = TILE_FILES;
break;
case END_OF_FILE:
done = 1;
lastToken = END_OF_FILE;
break;
default:
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"Tried to use image cache config file token");
pfNotify(PFNFY_WARN, PFNFY_MORE,
"%s in clip texture config file. Failed ",
tokenString(curToken));
case BAD_TOKEN:
pfNotify(PFNFY_WARN, PFNFY_MORE,
"in pfdLoadClipTexture after parsing token %s\n",
tokenString(lastToken));
failed = TRUE;
case PAGE_SIZE:
/* Read in page size for direct i/o; info passed to icaches */
if(fscanf(fp, "%d", &state->pageSize) != 1)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): couldn't read page_size "
"argument (should be positive small integer). "
"using default value.");
state->pageSize = -1;
}
lastToken = PAGE_SIZE;
break;
case READ_FUNC:
{ /* 1 arg: function name (from executable). 2 args: dso funcname */
int status;
char *dsoname;
char *readname;
void *dso;
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: read function "
"requires 1 or 2 arguments; "
"using default read function");
lastToken = READ_FUNC;
break;
}
/* this may turn out to be fname (no dso arg) */
dsoname = (char *)malloc(sizeof(buf) + 1);
strcpy(dsoname, buf);
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
readname = dsoname;
dsoname = NULL;
}
else
{
readname = (char *)malloc(sizeof(buf) + 1);
strcpy(readname, buf);
}
dso = dlopen(dsoname, RTLD_LAZY);
if(!dso && dsoname)
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuMakeClipTexture(): can't find DSO %s for "
"read function; looking for %s in executable",
dsoname, readname);
if(!dso)
dso = dlopen(NULL, RTLD_LAZY);
if(!dso)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): could open read function dso; "
"using default read function");
lastToken = READ_FUNC;
break;
}
state->readFunc = (pfReadImageTileFuncType)dlsym(dso, readname);
if(!state->readFunc)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): couldn't find %s symbol; "
"using default read function",
readname);
}
lastToken = READ_FUNC;
break;
} /* READ_FUNC */
case LOOKAHEAD:
if(fscanf(fp,"%d", &state->lookahead) != 1)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): couldn't read lookahead "
"argument (should be positive small integer). "
"using default border of 1. ");
state->lookahead = 1;
}
lastToken = LOOKAHEAD;
break;
} /* switch statement */
} /* while loop */
/* expand relative pathnames. Use old method (expand base name)
** if tileData.base is set, then it is expanded, otherwise expand
** tileData.format or tileData.files, whichever was set
** if icData.base is set, then it is expanded otherwise expand
** icData.format or icData.files, whichever was set
*/
data[0] = &icData;
data[1] = &tileData;
for(i = 0; i < 2; i++)
{
cnt = 0; /* for noic files: no image cache base, format, or files */
strptr = NULL;
if(data[i]->base) /*expand the base */
{
strptr = &data[i]->base;
cnt = 1;
}
else
{
if(data[i]->type == FORMAT) /* expand format string */
{
strptr = &data[i]->format;
cnt = 1;
}
if(data[i]->type == FILELIST) /* expand list of files */
{
strptr = NULL;
cnt = pfGetNum(data[i]->files);
}
}
if(strptr || cnt) /* avoid core dump if nothing set */
for(j = 0; j < cnt; j++)
{
if(data[i]->type == FILELIST)
{
str = (char *)pfGet(data[i]->files, j);
strptr = &str;
}
len = strlen(*strptr);
strcpy(buf, *strptr);
_pfExpandRelative("pfdLoadClipTexture", ctFilePath, buf);
if(strlen(buf) > len)
*strptr = (char *)realloc(*strptr, strlen(buf) + 1);
strcpy(*strptr, buf);
if(data[i]->type == FILELIST)
pfSet(data[i]->files, j, *strptr);
}
}
/* set the functions needed to configure data */
state->icData = &icData;
state->tileData = &tileData;
switch(icData.type) {
case FORMAT:
state->icFunc = pfdLoadImageCacheFormat;
break;
case FILELIST:
state->icFunc = pfdLoadImageCacheFiles;
break;
case NONE: /* XXX TODO: semantic conflict! need another way to do auto */
state->icFunc = pfdLoadImageCacheAuto;
break;
default: /* FORMAT | FILELIST */
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuLoadClipTexture: both format string and file list method "
"used to configure image caches");
failed = TRUE;
break;
}
switch(tileData.type) {
case FORMAT:
state->tileFunc = pfdLoadImageTileFormat;
break;
case FILELIST:
state->tileFunc = pfdLoadImageTileFiles;
break;
case NONE:
state->tileFunc = pfdLoadImageTileDefault;
break;
default: /* FORMAT|FILELIST or NONE*/
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuLoadClipTexture: either format string or file list method "
"must be used to configure image tile");
failed = TRUE;
break;
}
if(failed)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture: "
"parse of clip texture config file %s failed",
fileName);
freeClipTexData(&icData);
freeClipTexData(&tileData);
pfuFreeClipTexConfig(state);
return NULL;
}
ct = pfuMakeClipTexture(state);
freeClipTexData(&icData);
freeClipTexData(&tileData);
pfuFreeClipTexConfig(state);
return ct;
}
pfMPClipTexture *
pfdLoadMPClipTexture(const char *_fileName)
{
pfMPClipTexture *mpct = pfNewMPClipTexture();
pfClipTexture *ct = pfdLoadClipTexture(_fileName);
if(ct == NULL)
{
pfDelete(mpct);
return NULL;
}
pfMPClipTextureClipTexture(mpct, ct);
return mpct;
}
/*
** Read in Configuration Information to Load an Image Cache.
**
** Arguments:
**
** configuration file name
** destination texture
** destination texture level
**
** Configuration File Cache Format:
**
** comments: start "#" "comment" or "//" until end of line. May go anywhere
** a token is expected.
**
** Treat tokens as if order dependent. Some tokens and values can be
** omitted to use defaults. Don't use token without a value.
**
** (old)ic_version1.0 must be at top of file
** ic_version2.0 must be at top of file
** ext_format <string> (external format of stored texels)
** int_format <string> (internal format used by graphics hw)
** img_format <string> (image format of stored texels)
**
** (old)virt_size <int> <int> <int> (area in texels of entire tex)
** icache_size <int> <int> <int> (texel area of entire image cache)
** (old)cache_size <int> <int> (number of rows and columns of tiles in memory)
** mem_region_size <int> <int> (tile area downloaded to image cache memory)
** (old)valid_region <int> <int> <int> (texel area downloaded to texture meme)
** tex_region_size <int> <int> <int> (texel area downloaded to texture meme)
**
** header_offset <int> (byte offset to skip user's file header)
** tiles_in_file <int> <int> <int> number of image tiles in each image file
** tile_size <int> <int> <int> (width, height, depth in texels of each tile)
**
** tile_base <file path string> (root of tile file names)
**
** tile_format <scanf string> (tile file name format; null string=default)
** num_tile_params <int> (number of parameter token arguments)
** tile_params <string list> (format parameter tokens in order)
**
** (ignored)num_streams <int> <int> <int> (number of s, t, and r stream servers)
** s_streams <string list> (list of S stream servers)
** t_streams <string list> (list of T stream servers)
** r_streams <string list> (list of R stream servers)
** default_tile <file path string> (name of tile to use if a tile isn't found)
*/
pfImageCache *
pfdLoadImageCache(const char *fileName, pfTexture *texture, int level)
{
return pfdLoadImageCacheState(fileName, texture, level, NULL);
}
pfImageCache *
pfdLoadImageCacheState(const char *fileName, pfTexture *texture,
int level, pfuImgCacheConfig *state)
{
Strbuf buf;
FILE *fp;
pfImageCache *ic;
pfuImgCacheConfig stateInfo;
int i;
int done = FALSE; /* done parsing file? */
int failed = FALSE;
Token lastToken = START_OF_FILE; /* for analyzing parse failures */
Token curToken = START_OF_FILE;
char icFilePath[2048]; /* get this file's filepath for relative pathnames*/
int version_enforce = 0;
char **strptr;
/* pfdOpenFile should be using this */
if(!pfFindFile(fileName, icFilePath, R_OK))
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexure: couldn't find image cache config file %s",
fileName);
return NULL;
}
if ((fp = pfdOpenFile(icFilePath)) == NULL)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexure: couldn't open image cache config file %s",
fileName);
return NULL;
}
if(!state) /* no state arg, point to struct and initialize */
{
state = &stateInfo;
pfuInitImgCacheConfig(state);
}
/*
** In the future, this will be settable by the user; path is the
** default name.
*/
state->name = icFilePath;
while(!done) {
switch(curToken = nextToken(fp)) {
case IMAGE_CACHE_VERSION:
if(lastToken != START_OF_FILE) {
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: "
"version token doesn't start file!\n");
}
version_enforce = 1;
break;
case COMMENT:
/* Comment from token to end of current line */
while(fscanf(fp, "%*[^\n]"));
lastToken = COMMENT;
break;
case EXT_FORMAT:
/* Read in External Format of Image Tile */
fscanf(fp,"%s", buf);
state->extFormat = _pfExternalFormatStrToEnum("pfdLoadImageCache",
buf);
if(state->extFormat < 0)
failed = TRUE;
lastToken = EXT_FORMAT;
break;
case INT_FORMAT:
/* Read in Internal Format of Image Tile */
fscanf(fp,"%s",buf);
state->intFormat = _pfInternalFormatStrToEnum("pfdLoadImageCache",
buf);
if(state->intFormat < 0)
failed = TRUE;
lastToken = INT_FORMAT;
break;
case IMG_FORMAT:
{
/* Read in Image Format of Image Tile */
ImageFormatInfo *imageFormat;
fscanf(fp, "%s", buf);
imageFormat = _pfImageFormatStrToEnum("pfdLoadImageCache", buf);
if(!imageFormat)
failed = TRUE;
else
{
state->imgFormat = imageFormat->format;
state->components = imageFormat->components;
}
lastToken = IMG_FORMAT;
break;
}
case VSIZE:
/* Read Virtual Size */
fscanf(fp, "%d %d %d", &state->size[PF_S],
&state->size[PF_T], &state->size[PF_R]);
lastToken = VSIZE;
VERSION_ENFORCE("pfdLoadImageCache");
break;
case TEX_REGION:
fscanf(fp,"%d %d %d",
&state->texRegSize[PF_S],
&state->texRegSize[PF_T],
&state->texRegSize[PF_R]);
break;
case MEM_REGION:
fscanf(fp,"%d %d %d",
&state->memRegSize[PF_S],
&state->memRegSize[PF_T],
&state->memRegSize[PF_R]);
break;
case CACHE_SIZE:
/* Read in Cache size in mem specified in tiles */
fscanf(fp,"%d %d %d",
&state->memRegSize[PF_S],
&state->memRegSize[PF_T],
&state->memRegSize[PF_R]);
lastToken = CACHE_SIZE;
break;
case VALID_REGION:
/* Read in Valid Region Size - specified in texels */
fscanf(fp,"%d %d %d",
&state->texRegSize[PF_S],
&state->texRegSize[PF_T],
&state->texRegSize[PF_R]);
lastToken = VALID_REGION;
break;
case VALID_REGION_DST:
/* Read in Valid Region Dst Size - specified in texels */
fscanf(fp,"%d %d %d",
&state->texSize[PF_S],
&state->texSize[PF_T],
&state->texSize[PF_R]);
lastToken = VALID_REGION_DST;
break;
case HEADER_OFFSET:
/* Read in file header offset */
fscanf(fp, "%d", &state->header);
lastToken = HEADER_OFFSET;
break;
case TILES_IN_FILE:
/* Read in number of S, T, & R Tiles in each tile file */
fscanf(fp,"%d %d %d",
&state->tilesInFile[PF_S],
&state->tilesInFile[PF_T],
&state->tilesInFile[PF_R]);
lastToken = TILES_IN_FILE;
break;
case TILE_SIZE:
/* Read in Tile Width,Height,Depth specified in texels */
fscanf(fp,"%d %d %d",
&state->tileSize[PF_S],
&state->tileSize[PF_T],
&state->tileSize[PF_R]);
lastToken = TILE_SIZE;
break;
case TILE_BASE:
/* get image cache fname base */
{
int status;
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
buf[0] = '\0'; /* use empty string */
}
_pfExpandBase("pfdLoadImageCache", buf);/* expand env var */
if(state->base)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: image cache filename base "
"can only be set once");
failed = TRUE;
}
else
{
state->base = (char *)malloc(strlen(buf) + 1);
strcpy(state->base, buf);
}
lastToken = TILE_BASE;
}
break;
case TILE_FORMAT:
/* get image cache file name format */
fscanf(fp, "%s", buf);
_pfExpandBase("pfdLoadImageCache", buf);/* expand env var */
state->format = (char *)malloc(strlen(buf) + 1);
strcpy(state->format, buf);
lastToken = TILE_FORMAT;
break;
case NUM_TILE_PARAMS:
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadImageCache: num_tile_params obsolete. "
"Argument ignored");
fscanf(fp, "%*d");
lastToken = NUM_TILE_PARAMS;
break;
case TILE_PARAMS:
for(i = 0;1; i++)
{
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break;
if(isToken(buf, fp) == BAD_TOKEN) /* still reading params */
{
int arg;
arg = _pfFnameArgToNum(buf);
if(arg == -1)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: invalid tile "
"parameter token");
failed = TRUE;
}
if(i < PFIMAGECACHE_MAX_TILE_FILENAME_ARGS)
state->args[i] = arg;
}
else /* unread last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
if(i > PFIMAGECACHE_MAX_TILE_FILENAME_ARGS)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: number of filename arguments"
"exceeds maximum (%d)",
PFIMAGECACHE_MAX_TILE_FILENAME_ARGS);
failed = TRUE;
}
state->numParams = i;
lastToken = TILE_PARAMS;
break;
case NUM_STREAM_SERVERS:
/* Obsolete Token: no longer used */
pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
"pfdLoadImageCache: num_streams servers obsolete."
"Arguments ignored");
(void)fscanf(fp, "%*d %*d %*d"); /* consume arguments */
break;
case S_STREAM_SERVERS:
state->sStreams = pfNewList(sizeof(char*), 5, NULL);
for(i = 0;1; i++) {
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break; /* invalid token or end of file */
if(isToken(buf, fp) == BAD_TOKEN) /* still reading streams */
{
char *path = (char *)malloc(strlen(buf) + 1);
strcpy(path, buf);
pfAdd(state->sStreams, path);
}
else /* "unread" last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
break;
case T_STREAM_SERVERS:
state->tStreams = pfNewList(sizeof(char*), 5, NULL);
for(i = 0;1; i++) {
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break; /* invalid token or end of file */
if(isToken(buf, fp) == BAD_TOKEN) /* not a token */
{
char *path = (char *)malloc(strlen(buf) + 1);
strcpy(path, buf);
pfAdd(state->tStreams, path);
}
else /* "unread" last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
break;
case R_STREAM_SERVERS:
state->rStreams = pfNewList(sizeof(char*), 5, NULL);
for(i = 0;1; i++) {
int status;
status = fscanf(fp, "%s", buf);
if(status == 0 || status == EOF)
break; /* invalid token or end of file */
if(isToken(buf, fp) == BAD_TOKEN) /* not a token */
{
char *path = (char *)malloc(strlen(buf) + 1);
strcpy(path, buf);
pfAdd(state->rStreams, path);
}
else /* "unread" last fscanf so nextToken() call works */
{
fseek(fp, -strlen(buf), SEEK_CUR);
break;
}
}
break;
case DEFAULT_TILE:
fscanf(fp, "%s", buf); /* default tile file name */
state->defaultTile = (char *)malloc(strlen(buf) + 1);
strcpy(state->defaultTile, buf);
lastToken = DEFAULT_TILE;
break;
case PAGE_SIZE:
/* Will override pagesize set in clip texture */
if(state->pageSize != -1)
{
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
"pfdLoadImageCache(): page_size value overrides "
"value set in cliptexture config file.");
}
if(fscanf(fp, "%d", &state->pageSize) != 1)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): couldn't read page_size "
"argument (should be positive small integer). "
"using default value.");
state->pageSize = -1;
}
lastToken = PAGE_SIZE;
break;
case READ_FUNC:
{ /* 1 arg: function name (from executable). 2 args: dso funcname */
int status;
char *dsoname;
char *readname;
void *dso;
pfReadImageTileFuncType readfunc;
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache(): read function "
"requires 1 or 2 arguments; "
"using default read function");
lastToken = READ_FUNC;
break;
}
/* this may turn out to be fname (no dso arg) */
dsoname = (char *)malloc(sizeof(buf) + 1);
strcpy(dsoname, buf);
status = fscanf(fp, "%s", buf);
if(isToken(buf, fp) != BAD_TOKEN || /* no usable argument */
status == EOF || status == 0)
{
/* unread next token */
if(status != 0 && status != EOF)
fseek(fp, -strlen(buf), SEEK_CUR);
readname = dsoname;
dsoname = NULL;
}
else
{
readname = (char *)malloc(sizeof(buf) + 1);
strcpy(readname, buf);
}
dso = dlopen(dsoname, RTLD_LAZY);
if(!dso && dsoname)
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfuMakeClipTexture(): can't find DSO %s for "
"read function; looking for %s in executable",
dsoname, readname);
if(!dso)
dso = dlopen(NULL, RTLD_LAZY);
if(!dso)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadClipTexture(): could open read function dso; "
"using default read function");
lastToken = READ_FUNC;
break;
}
readfunc = (pfReadImageTileFuncType)dlsym(dso, readname);
if(!readfunc)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache(): couldn't find %s symbol; "
"read function unchanged",
readname);
}
else
state->readFunc = readfunc;
lastToken = READ_FUNC;
break;
} /* READ_FUNC */
case LOOKAHEAD:
if(state->lookahead != 1)
{
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
"pfdLoadImageCache(): overriding lookahead value "
"%d set in clip texture config file.",
state->lookahead);
}
if(fscanf(fp,"%d", &state->lookahead) != 1)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache(): couldn't read lookahead "
"argument (should be positive small integer). "
"using default border of 1. ");
state->lookahead = 1;
}
lastToken = LOOKAHEAD;
break;
case END_OF_FILE:
done = 1;
lastToken = END_OF_FILE;
break;
default:
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"Tried to used image cache config file token");
pfNotify(PFNFY_WARN, PFNFY_MORE,
"%s in clip texture config file. Failed ",
tokenString(curToken));
case BAD_TOKEN:
pfNotify(PFNFY_WARN, PFNFY_MORE,
"in pfdLoadImageCache after parsing token %s\n",
tokenString(lastToken));
failed = TRUE;
}
}
/* Create and Configure Image Cache */
if(failed)
{
pfNotify(PFNFY_WARN, PFNFY_PRINT,
"pfdLoadImageCache: "
"parse of image cache configuration file %s failed",
fileName);
return NULL;
}
/*
** do relative pathname expansion. Use old method (expand base name)
** if state->base is set, otherwise expand state->format
** semantics are unclear if base or format is passed in through struct and
** not in config file.
*/
if(state->base)
strptr = &state->base;
else
strptr = &state->format;
if(*strptr) /* neither set; an error, but test prevents core dump */
{
i = strlen(*strptr);
strcpy(buf, *strptr);
_pfExpandRelative("pfdLoadImageCache", icFilePath, buf);
if(strlen(buf) > i)
*strptr = realloc(*strptr, strlen(buf) + 1);
strcpy(*strptr, buf);
}
ic = pfuMakeImageCache(texture, level, state);
pfuFreeImgCacheConfig(state);
return ic;
}
int
pfdClipTextureNodeUpdate(pfTraverser *trav, void *userData)
{
static int pcS,pcT,pcR;
static int LSIZE;
int i,n,ds,dt,vS,vT,vR,rS,rT,tS,tT,tR;
int nCacheCS,nCacheCT,csizeS,csizeT,csizeR;
pfMPClipTexture *mpClip;
pfClipTexture *clip;
pfImageCache *ic;
pfImageTile *proto;
static int first = 1;
pfChannel *chan = pfGetTravChan(trav);
pfNode *nd = pfGetTravNode(trav);
pfList *lst = pfGetUserData(nd);
if (first)
{
pcS = 0;
pcT = 0;
LSIZE = atoi(getenv("LOADSIZE"));
if (LSIZE < 8)
LSIZE = 8;
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,"Using load size %d",LSIZE);
first = 0;
}
if (lst == NULL)
return 0;
n = pfGetNum(lst);
for(i=0;i<n;i++)
{
mpClip = (pfMPClipTexture*)pfGet(lst,i);
clip = pfGetMPClipTextureClipTexture(mpClip);
pfGetClipTextureVirtualSize(clip,&vS,&vT,&vR);
rS = rT = pfGetClipTextureClipSize(clip);
pfGetMPClipTextureCenter(mpClip,&pcS,&pcT,&pcR);
{
static int signS = 1;
static int signT = 1;
pcS += LSIZE * signS;
pcT += LSIZE * signT;
if (pcS >= vS - rS)
{
signS = -1;
pcS = vS-rS;
}
if (pcS < 0)
{
signS = 1;
pcS = 0;
}
if (pcT >= vT - rT)
{
signT = -1;
pcT = vT-rT;
}
if (pcT < 0)
{
signT = 1;
pcT = 0;
}
}
if (first)
{
first--;
pcS = pcT = vS/2;
}
pfNotify(PFNFY_INTERNAL_DEBUG, PFNFY_PRINT,
"Frame %d - setting center of clip 0x%x - %d, %d",
pfGetFrameCount(),mpClip,pcS,pcT);
pfMPClipTextureCenter(mpClip,pcS,pcT,0);
}
return PFTRAV_CONT;
}