#include "PtexPlatform.h"
#include <iostream>
#include <sstream>
#include <errno.h>
#include <stdio.h>
#include "Ptexture.h"
#include "PtexUtils.h"
#include "PtexReader.h"
namespace {
class DefaultInputHandler : public PtexInputHandler
{
public:
virtual Handle open(const char* path) { return (Handle) fopen(path, "rb"); }
virtual void seek(Handle handle,
int64_t pos) { fseeko((FILE*)handle, pos, SEEK_SET); }
return fread(buffer, size, 1, (FILE*)handle) == 1 ? size : 0;
}
virtual bool close(Handle handle) { return fclose((FILE*)handle); }
virtual const char* lastError() { return strerror(errno); }
} defaultInputHandler;
}
PtexTexture* PtexTexture::open(const char* path, Ptex::String& error, bool premultiply)
{
PtexCache* cache = PtexCache::create(1, 1024*1024, premultiply);
PtexTexture* file = cache->get(path, error);
PtexReader* reader = dynamic_cast<PtexReader*> (file);
if (reader) reader->setOwnsCache();
cache->purgeAll();
return file;
}
bool PtexReader::open(const char* path, Ptex::String& error)
{
if (!LittleEndian()) {
error = "Ptex library doesn't currently support big-endian cpu's";
return 0;
}
_path = path;
_fp = _io->open(path);
if (!_fp) {
errstr += path; errstr += "\n"; errstr += _io->lastError();
error = errstr.c_str();
return 0;
}
readBlock(&_header, HeaderSize);
if (_header.magic != Magic) {
std::string errstr =
"Not a ptex file: "; errstr += path;
error = errstr.c_str();
return 0;
}
if (_header.version != 1) {
char ver[21]; snprintf(ver, 20, "%d", _header.version);
std::string errstr =
"Unsupported ptex file version (";
errstr += ver; errstr += "): "; errstr += path;
error = errstr.c_str();
return 0;
}
_pixelsize = _header.pixelSize();
memset(&_extheader, 0, sizeof(_extheader));
readBlock(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize));
FilePos pos = tell();
_faceinfopos = pos; pos += _header.faceinfosize;
_constdatapos = pos; pos += _header.constdatasize;
_levelinfopos = pos; pos += _header.levelinfosize;
_leveldatapos = pos; pos += _header.leveldatasize;
_metadatapos = pos; pos += _header.metadatazipsize;
pos += sizeof(uint64_t);
_lmdheaderpos = pos; pos += _extheader.lmdheaderzipsize;
_lmddatapos = pos; pos += _extheader.lmddatasize;
readFaceInfo();
readConstData();
readLevelInfo();
readEditData();
if (!_ok) {
error = _error.c_str();
return 0;
}
return 1;
}
PtexReader::PtexReader(void** parent, PtexCacheImpl* cache, bool premultiply,
PtexInputHandler* io)
: PtexCachedFile(parent, cache),
_io(io ? io : &defaultInputHandler),
_premultiply(premultiply),
_ownsCache(false),
_ok(true),
_fp(0),
_pos(0),
_pixelsize(0),
_constdata(0),
_metadata(0),
_hasEdits(false)
{
memset(&_header, 0, sizeof(_header));
memset(&_zstream, 0, sizeof(_zstream));
inflateInit(&_zstream);
}
PtexReader::~PtexReader()
{
if (_fp) _io->close(_fp);
if (_constdata) free(_constdata);
orphanList(_levels);
for (ReductionMap::iterator i = _reductions.begin(); i != _reductions.end(); i++) {
if (f) f->orphan();
}
if (_metadata) {
_metadata->orphan();
_metadata = 0;
}
inflateEnd(&_zstream);
if (_ownsCache) _cache->setPendingDelete();
}
void PtexReader::release()
{
PtexCacheImpl* cache = _cache;
{
AutoLockCache lock(cache->cachelock);
unref();
}
cache->handlePendingDelete();
}
const Ptex::FaceInfo& PtexReader::getFaceInfo(int faceid)
{
if (faceid >= 0 && uint32_t(faceid) < _faceinfo.size())
return _faceinfo[faceid];
static Ptex::FaceInfo dummy;
return dummy;
}
void PtexReader::readFaceInfo()
{
if (_faceinfo.empty()) {
seek(_faceinfopos);
int nfaces = _header.nfaces;
_faceinfo.resize(nfaces);
readZipBlock(&_faceinfo[0], _header.faceinfosize,
sizeof(FaceInfo)*nfaces);
_rfaceids.resize(nfaces);
std::vector<uint32_t> faceids_r(nfaces);
PtexUtils::genRfaceids(&_faceinfo[0], nfaces,
&_rfaceids[0], &faceids_r[0]);
_res_r.resize(nfaces);
for (int i = 0; i < nfaces; i++)
_res_r[i] = _faceinfo[faceids_r[i]].
res;
}
}
void PtexReader::readLevelInfo()
{
if (_levelinfo.empty()) {
seek(_levelinfopos);
_levelinfo.resize(_header.nlevels);
readBlock(&_levelinfo[0], LevelInfoSize*_header.nlevels);
_levels.resize(_header.nlevels);
_levelpos.resize(_header.nlevels);
FilePos pos = _leveldatapos;
for (int i = 0; i < _header.nlevels; i++) {
_levelpos[i] = pos;
pos += _levelinfo[i].leveldatasize;
}
}
}
void PtexReader::readConstData()
{
if (!_constdata) {
seek(_constdatapos);
int size = _pixelsize * _header.nfaces;
_constdata = (uint8_t*) malloc(size);
readZipBlock(_constdata, _header.constdatasize, size);
if (_premultiply && _header.hasAlpha())
PtexUtils::multalpha(_constdata, _header.nfaces, _header.datatype,
_header.nchannels, _header.alphachan);
}
}
PtexMetaData* PtexReader::getMetaData()
{
AutoLockCache locker(_cache->cachelock);
if (_metadata) _metadata->ref();
else readMetaData();
return _metadata;
}
PtexReader::MetaData::Entry*
PtexReader::MetaData::getEntry(const char* key)
{
MetaMap::iterator iter = _map.find(key);
if (iter == _map.end()) {
return 0;
}
Entry* e = &iter->second;
if (!e->isLmd) {
return e;
}
AutoLockCache lock(_cache->cachelock);
if (e->lmdData) {
e->lmdData->ref();
_lmdRefs.push_back(e->lmdData);
return e;
}
else {
_cache->cachelock.unlock();
AutoMutex locker(_reader->readlock);
if (e->lmdData) {
_cache->cachelock.lock();
if (e->lmdData) {
e->data = e->lmdData->data();
_lmdRefs.push_back(e->lmdData);
e->lmdData->ref();
return e;
}
}
LargeMetaData*& parent = e->lmdData;
LargeMetaData* volatile lmdData = new LargeMetaData((void**)&parent, _cache, e->datasize);
e->data = lmdData->data();
_reader->seek(e->lmdPos);
_reader->readZipBlock(e->data, e->lmdZipSize, e->datasize);
_cache->cachelock.lock();
e->lmdData = lmdData;
return e;
}
}
void PtexReader::readMetaData()
{
_cache->cachelock.unlock();
AutoMutex locker(readlock);
if (_metadata) {
_cache->cachelock.lock();
if (_metadata) {
_metadata->ref();
return;
}
_cache->cachelock.unlock();
}
int totalsize = _header.metadatamemsize;
for (
size_t i = 0, size = _metaedits.size(); i <
size; i++)
totalsize += _metaedits[i].memsize;
if (_header.metadatamemsize)
readMetaDataBlock(newmeta, _metadatapos,
_header.metadatazipsize, _header.metadatamemsize);
if (_extheader.lmdheadermemsize)
readLargeMetaDataHeaders(newmeta, _lmdheaderpos,
_extheader.lmdheaderzipsize, _extheader.lmdheadermemsize);
for (
size_t i = 0, size = _metaedits.size(); i <
size; i++)
readMetaDataBlock(newmeta, _metaedits[i].pos,
_metaedits[i].zipsize, _metaedits[i].memsize);
_cache->cachelock.lock();
_metadata = newmeta;
_cache->purgeData();
}
void PtexReader::readMetaDataBlock(
MetaData* metadata, FilePos pos,
int zipsize,
int memsize)
{
seek(pos);
bool useMalloc = memsize > AllocaMax;
char* buff = useMalloc ? (char*) malloc(memsize) : (char*)alloca(memsize);
if (readZipBlock(buff, zipsize, memsize)) {
char* ptr = buff;
char*
end = ptr + memsize;
while (ptr < end) {
uint8_t keysize = *ptr++;
char* key = (char*)ptr; ptr += keysize;
key[keysize-1] = '\0';
uint8_t datatype = *ptr++;
uint32_t datasize; memcpy(&datasize, ptr, sizeof(datasize));
ptr += sizeof(datasize);
char*
data = ptr; ptr += datasize;
metadata->addEntry(keysize-1, key, datatype, datasize, data);
}
}
if (useMalloc) free(buff);
}
void PtexReader::readLargeMetaDataHeaders(
MetaData* metadata, FilePos pos,
int zipsize,
int memsize)
{
seek(pos);
bool useMalloc = memsize > AllocaMax;
char* buff = useMalloc ? (char*) malloc(memsize) : (char*)alloca(memsize);
if (readZipBlock(buff, zipsize, memsize)) {
pos += zipsize;
char* ptr = buff;
char* end = ptr + memsize;
while (ptr < end) {
uint8_t keysize = *ptr++;
char* key = (char*)ptr; ptr += keysize;
uint8_t datatype = *ptr++;
uint32_t datasize; memcpy(&datasize, ptr, sizeof(datasize));
ptr += sizeof(datasize);
uint32_t zipsize; memcpy(&zipsize, ptr, sizeof(zipsize));
ptr += sizeof(zipsize);
metadata->addLmdEntry(keysize-1, key, datatype, datasize, pos, zipsize);
pos += zipsize;
}
}
if (useMalloc) free(buff);
}
void PtexReader::readEditData()
{
FilePos pos = FilePos(_editdatapos), endpos;
if (_extheader.editdatapos > 0) {
endpos = FilePos(pos + _extheader.editdatasize);
}
else {
endpos = FilePos((uint64_t)-1);
}
while (pos < endpos) {
seek(pos);
uint8_t edittype = et_editmetadata;
uint32_t editsize;
if (!readBlock(&edittype, sizeof(edittype), false)) break;
if (!readBlock(&editsize, sizeof(editsize), false)) break;
if (!editsize) break;
_hasEdits = true;
pos = tell() + editsize;
switch (edittype) {
case et_editfacedata: readEditFaceData(); break;
case et_editmetadata: readEditMetaData(); break;
}
}
}
void PtexReader::readEditFaceData()
{
EditFaceDataHeader efdh;
if (!readBlock(&efdh, EditFaceDataHeaderSize)) return;
int faceid = efdh.faceid;
if (faceid < 0 || size_t(faceid) >= _header.nfaces) return;
FaceInfo& f = _faceinfo[faceid];
f = efdh.faceinfo;
f.flags |= FaceInfo::flag_hasedits;
uint8_t* constdata = _constdata + _pixelsize * faceid;
if (!readBlock(constdata, _pixelsize)) return;
if (_premultiply && _header.hasAlpha())
PtexUtils::multalpha(constdata, 1, _header.datatype,
_header.nchannels, _header.alphachan);
if (!f.isConstant()) {
_faceedits.push_back(FaceEdit());
FaceEdit& e = _faceedits.back();
e.pos = tell();
e.faceid = faceid;
e.fdh = efdh.fdh;
}
}
void PtexReader::readEditMetaData()
{
EditMetaDataHeader emdh;
if (!readBlock(&emdh, EditMetaDataHeaderSize)) return;
_metaedits.push_back(MetaEdit());
MetaEdit& e = _metaedits.back();
e.pos = tell();
e.zipsize = emdh.metadatazipsize;
e.memsize = emdh.metadatamemsize;
}
bool PtexReader::readBlock(
void*
data,
int size,
bool reporterror)
{
int result = _io->read(data, size, _fp);
if (result == size) {
STATS_INC(nblocksRead);
STATS_ADD(nbytesRead, size);
return 1;
}
if (reporterror)
setError("PtexReader error: read failed (EOF)");
return 0;
}
bool PtexReader::readZipBlock(void* data, int zipsize, int unzipsize)
{
void* buff = alloca(BlockSize);
_zstream.next_out = (Bytef*) data;
_zstream.avail_out = unzipsize;
while (1) {
int size = (zipsize < BlockSize) ? zipsize : BlockSize;
if (!readBlock(buff, size)) break;
_zstream.next_in = (Bytef*) buff;
_zstream.avail_in =
size;
int zresult = inflate(&_zstream, zipsize ? Z_NO_FLUSH : Z_FINISH);
if (zresult == Z_STREAM_END) break;
if (zresult != Z_OK) {
setError("PtexReader error: unzip failed, file corrupt");
inflateReset(&_zstream);
return 0;
}
}
int total = _zstream.total_out;
inflateReset(&_zstream);
return total == unzipsize;
}
void PtexReader::readLevel(
int levelid, Level*&
level)
{
_cache->cachelock.unlock();
AutoMutex locker(readlock);
if (level) {
_cache->cachelock.lock();
if (level) {
level->ref();
return;
}
_cache->cachelock.unlock();
}
LevelInfo& l = _levelinfo[levelid];
Level* volatile newlevel = new Level((void**)&level, _cache, l.nfaces);
seek(_levelpos[levelid]);
readZipBlock(&newlevel->fdh[0], l.levelheadersize, FaceDataHeaderSize * l.nfaces);
computeOffsets(tell(), l.nfaces, &newlevel->fdh[0], &newlevel->offsets[0]);
if (levelid == 0) {
for (
size_t i = 0, size = _faceedits.size(); i <
size; i++) {
FaceEdit& e = _faceedits[i];
newlevel->fdh[e.faceid] = e.fdh;
newlevel->offsets[e.faceid] = e.pos;
}
}
_cache->cachelock.lock();
level = newlevel;
_cache->purgeData();
}
void PtexReader::readFace(int levelid, Level* level, int faceid)
{
_cache->cachelock.unlock();
FaceData*&
face = level->faces[faceid];
AutoMutex locker(readlock);
if (face) {
_cache->cachelock.lock();
if (face) {
face->ref();
return;
}
_cache->cachelock.unlock();
}
int first = faceid, last = faceid;
int totalsize = 0;
FaceDataHeader fdh = level->fdh[faceid];
if (fdh.encoding() != enc_tiled) {
totalsize += unpackedSize(fdh, levelid, faceid);
int nfaces =
int(level->fdh.size());
while (1) {
int f = first-1;
if (fdh.encoding() == enc_tiled) break;
int size = totalsize + unpackedSize(fdh, levelid, f);
if (size > BlockSize) break;
}
while (1) {
int f = last+1;
if (f >= nfaces || level->faces[f]) break;
if (fdh.encoding() == enc_tiled) break;
int size = totalsize + unpackedSize(fdh, levelid, f);
if (size > BlockSize) break;
}
}
std::vector<FaceData*> extraFaces;
extraFaces.reserve(last-first);
for (int i = first; i <= last; i++) {
fdh = level->fdh[i];
if (fdh.blocksize()) {
FaceData*& face = level->faces[i];
readFaceData(level->offsets[i], fdh, getRes(levelid, i), levelid, face);
if (i != faceid) extraFaces.push_back(face);
}
}
_cache->cachelock.lock();
for (
size_t i = 0, size = extraFaces.size(); i <
size; i++)
extraFaces[i]->unref();
}
void PtexReader::TiledFace::readTile(int tile, FaceData*& data)
{
_cache->cachelock.unlock();
AutoMutex locker(_reader->readlock);
if (data) {
_cache->cachelock.lock();
if (data) {
data->ref();
return;
}
_cache->cachelock.unlock();
}
_reader->readFaceData(_offsets[tile], _fdh[tile], _tileres, _levelid, data);
_cache->cachelock.lock();
_cache->purgeData();
}
void PtexReader::readFaceData(FilePos pos, FaceDataHeader fdh, Res
res,
int levelid,
FaceData*& face)
{
FaceData* volatile newface = 0;
seek(pos);
switch (fdh.encoding()) {
case enc_constant:
{
ConstantFace* pf = new ConstantFace((void**)&face, _cache, _pixelsize);
readBlock(pf->data(), _pixelsize);
if (levelid==0 && _premultiply && _header.hasAlpha())
PtexUtils::multalpha(pf->data(), 1, _header.datatype,
_header.nchannels, _header.alphachan);
newface = pf;
}
break;
case enc_tiled:
{
Res tileres;
readBlock(&tileres, sizeof(tileres));
uint32_t tileheadersize;
readBlock(&tileheadersize, sizeof(tileheadersize));
TiledFace* tf = new TiledFace((void**)&face, _cache, res, tileres, levelid, this);
readZipBlock(&tf->_fdh[0], tileheadersize, FaceDataHeaderSize * tf->_ntiles);
computeOffsets(tell(), tf->_ntiles, &tf->_fdh[0], &tf->_offsets[0]);
newface = tf;
}
break;
case enc_zipped:
case enc_diffzipped:
{
int uw = res.u(), vw = res.v();
int npixels = uw * vw;
int unpackedSize = _pixelsize * npixels;
PackedFace* pf = new PackedFace((void**)&face, _cache,
res, _pixelsize, unpackedSize);
void* tmp = alloca(unpackedSize);
readZipBlock(tmp, fdh.blocksize(), unpackedSize);
if (fdh.encoding() == enc_diffzipped)
PtexUtils::decodeDifference(tmp, unpackedSize, _header.datatype);
PtexUtils::interleave(tmp, uw * DataSize(_header.datatype), uw, vw,
pf->data(), uw * _pixelsize,
_header.datatype, _header.nchannels);
if (levelid==0 && _premultiply && _header.hasAlpha())
PtexUtils::multalpha(pf->data(), npixels, _header.datatype,
_header.nchannels, _header.alphachan);
newface = pf;
}
break;
}
face = newface;
}
void PtexReader::getData(
int faceid,
void*
buffer,
int stride)
{
if (!_ok) return;
FaceInfo& f = _faceinfo[faceid];
getData(faceid, buffer, stride, f.res);
}
void PtexReader::getData(int faceid, void* buffer, int stride, Res res)
{
if (!_ok) return;
FaceInfo& f = _faceinfo[faceid];
int resu = res.u(), resv = res.v();
int rowlen = _pixelsize * resu;
if (stride == 0) stride = rowlen;
PtexPtr<PtexFaceData> d ( getData(faceid, res) );
if (!d) return;
if (d->isConstant()) {
resu, resv, _pixelsize);
}
else if (d->isTiled()) {
Res tileres = d->tileRes();
int ntilesu = f.res.ntilesu(tileres);
int ntilesv = f.res.ntilesv(tileres);
int tileures = tileres.u();
int tilevres = tileres.v();
int tilerowlen = _pixelsize * tileures;
int tile = 0;
char* dsttilerow = (char*) buffer;
for (int i = 0; i < ntilesv; i++) {
char* dsttile = dsttilerow;
for (int j = 0; j < ntilesu; j++) {
PtexPtr<PtexFaceData>
t ( d->getTile(tile++) );
if (!
t) { i = ntilesv;
break; }
PtexUtils::fill(
t->getData(), dsttile,
stride,
tileures, tilevres, _pixelsize);
else
PtexUtils::copy(
t->getData(), tilerowlen, dsttile,
stride,
tilevres, tilerowlen);
dsttile += tilerowlen;
}
dsttilerow += stride * tilevres;
}
}
else {
PtexUtils::copy(d->getData(), rowlen,
buffer,
stride, resv, rowlen);
}
}
PtexFaceData* PtexReader::getData(int faceid)
{
if (faceid < 0 || size_t(faceid) >= _header.nfaces) return 0;
if (!_ok) return 0;
FaceInfo& fi = _faceinfo[faceid];
if (fi.isConstant() || fi.res == 0) {
return new ConstDataPtr(getConstData() + faceid * _pixelsize, _pixelsize);
}
AutoLockCache locker(_cache->cachelock);
Level* level = getLevel(0);
FaceData* face = getFace(0, level, faceid);
level->unref();
}
PtexFaceData* PtexReader::getData(int faceid, Res res)
{
if (!_ok) return 0;
if (faceid < 0 || size_t(faceid) >= _header.nfaces) return 0;
FaceInfo& fi = _faceinfo[faceid];
if ((fi.isConstant() && res >= 0) || res == 0) {
return new ConstDataPtr(getConstData() + faceid * _pixelsize, _pixelsize);
}
_cache->cachelock.lock();
int redu = fi.res.ulog2 - res.ulog2, redv = fi.res.vlog2 - res.vlog2;
if (redu == 0 && redv == 0) {
Level* level = getLevel(0);
FaceData* face = getFace(0, level, faceid);
level->unref();
_cache->cachelock.unlock();
}
if (redu == redv && !fi.hasEdits() && res >= 0) {
int levelid = redu;
if (size_t(levelid) < _levels.size()) {
Level* level = getLevel(levelid);
int rfaceid = _rfaceids[faceid];
FaceData* face = 0;
if (size_t(rfaceid) < level->faces.size())
face = getFace(levelid, level, rfaceid);
level->unref();
if (face) {
_cache->cachelock.unlock();
}
}
}
FaceData*& face = _reductions[ReductionKey(faceid, res)];
if (face) {
face->ref();
_cache->cachelock.unlock();
}
_cache->cachelock.unlock();
if (res.ulog2 < 0 || res.vlog2 < 0) {
std::cerr <<
"PtexReader::getData - reductions below 1 pixel not supported" <<
std::endl;
return 0;
}
if (redu < 0 || redv < 0) {
std::cerr <<
"PtexReader::getData - enlargements not supported" <<
std::endl;
return 0;
}
if (_header.meshtype == mt_triangle)
{
if (redu != redv) {
std::cerr <<
"PtexReader::getData - anisotropic reductions not supported for triangle mesh" <<
std::endl;
return 0;
}
PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2+1)) );
FaceData*
src =
dynamic_cast<FaceData*
>(psrc.get());
assert(src);
if (src) src->reduce(face, this, res, PtexUtils::reduceTri);
}
bool blendu;
if (redu == redv) {
blendu = (res.ulog2 & 1);
}
else blendu = redu > redv;
if (blendu) {
PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2)) );
FaceData* src = dynamic_cast<FaceData*>(psrc.get());
assert(src);
if (src) src->reduce(face, this, res, PtexUtils::reduceu);
}
else {
PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2, res.vlog2+1)) );
FaceData* src = dynamic_cast<FaceData*>(psrc.get());
assert(src);
if (src) src->reduce(face, this, res, PtexUtils::reducev);
}
}
void PtexReader::blendFaces(FaceData*& face, int faceid, Res res, bool blendu)
{
Res pres;
int e1, e2;
if (blendu) {
assert(res.ulog2 < 0);
length = (res.vlog2 <= 0 ? 1 : res.v());
e1 = e_bottom; e2 = e_top;
pres = Res(res.ulog2+1, res.vlog2);
}
else {
assert(res.vlog2 < 0);
length = (res.ulog2 <= 0 ? 1 : res.u());
e1 = e_right; e2 = e_left;
pres = Res(res.ulog2, res.vlog2+1);
}
FaceInfo& f = _faceinfo[faceid];
int nf1 = f.adjfaces[e1], nf2 = f.adjfaces[e2];
int r1 = (f.adjedge(e1)-e1+2)&3;
int r2 = (f.adjedge(e2)-e2+2)&3;
Res pres1 = pres, pres2 = pres;
if (r1 & 1) pres1.swapuv();
if (r2 & 1) pres2.swapuv();
if (nf1 >= 0 && !(_faceinfo[nf1].res >= pres)) nf1 = -1;
if (nf2 >= 0 && !(_faceinfo[nf2].res >= pres)) nf2 = -1;
int nf = 1;
bool flip[3];
PtexFaceData* psrc[3];
psrc[0] = getData(faceid, pres);
flip[0] = 0;
if (nf1 >= 0) {
flip[nf] = length ? (r1 + blendu) & 1 : 0;
psrc[nf++] = getData(nf1, pres1);
}
if (nf2 >= 0) {
flip[nf] = length ? (r2 + blendu) & 1 : 0;
psrc[nf++] = getData(nf2, pres2);
}
AutoMutex rlocker(reducelock);
if (face) {
AutoLockCache locker(_cache->cachelock);
if (face) {
face->ref();
for (int i = 0; i < nf; i++) psrc[i]->release();
return;
}
}
DataType dt = datatype();
int nchan = nchannels();
int size = _pixelsize *
length;
PackedFace* pf = new PackedFace((void**)&face, _cache, res,
_pixelsize, size);
void* data = pf->getData();
if (nf == 1) {
memcpy(data, psrc[0]->getData(), size);
}
else {
float weight = 1.0f / nf;
memset(data, 0, size);
for (int i = 0; i < nf; i++)
PtexUtils::blend(psrc[i]->getData(), weight, data, flip[i],
length, dt, nchan);
}
{
AutoLockCache clocker(_cache->cachelock);
face = pf;
_cache->purgeData();
}
for (int i = 0; i < nf; i++) psrc[i]->release();
}
void PtexReader::getPixel(
int faceid,
int u,
int v,
float* result, int firstchan, int nchannels)
{
memset(result, 0, nchannels);
nchannels = PtexUtils::min(nchannels,
_header.nchannels-firstchan);
if (nchannels <= 0) return;
PtexPtr<PtexFaceData>
data ( getData(faceid) );
if (!data) return;
void* pixel = alloca(_pixelsize);
data->getPixel(u, v, pixel);
int datasize = DataSize(_header.datatype);
if (firstchan)
pixel = (char*) pixel + datasize * firstchan;
if (_header.datatype == dt_float)
memcpy(result, pixel, datasize * nchannels);
else
ConvertToFloat(result, pixel, _header.datatype, nchannels);
}
void PtexReader::getPixel(int faceid, int u, int v,
float* result, int firstchan, int nchannels,
Ptex::Res res)
{
memset(result, 0, nchannels);
nchannels = PtexUtils::min(nchannels,
_header.nchannels-firstchan);
if (nchannels <= 0) return;
PtexPtr<PtexFaceData>
data ( getData(faceid, res) );
if (!data) return;
void* pixel = alloca(_pixelsize);
data->getPixel(u, v, pixel);
int datasize = DataSize(_header.datatype);
if (firstchan)
pixel = (char*) pixel + datasize * firstchan;
if (_header.datatype == dt_float)
memcpy(result, pixel, datasize * nchannels);
else
ConvertToFloat(result, pixel, _header.datatype, nchannels);
}
void PtexReader::PackedFace::reduce(FaceData*& face, PtexReader*
r,
Res newres, PtexUtils::ReduceFn reducefn)
{
AutoMutex rlocker(r->reducelock);
if (face) {
AutoLockCache clocker(_cache->cachelock);
if (face) {
face->ref();
return;
}
}
DataType dt = r->datatype();
int nchan = r->nchannels();
PackedFace* pf = new PackedFace((void**)&face, _cache, newres,
_pixelsize, _pixelsize * newres.size());
reducefn(_data, _pixelsize * _res.u(), _res.u(), _res.v(),
pf->_data, _pixelsize * newres.u(), dt, nchan);
AutoLockCache clocker(_cache->cachelock);
face = pf;
_cache->purgeData();
}
void PtexReader::ConstantFace::reduce(FaceData*& face, PtexReader*,
Res, PtexUtils::ReduceFn)
{
AutoLockCache locker(_cache->cachelock);
ConstantFace* pf = new ConstantFace((void**)&face, _cache, _pixelsize);
memcpy(pf->_data, _data, _pixelsize);
face = pf;
}
void PtexReader::TiledFaceBase::reduce(FaceData*& face, PtexReader* r,
Res newres, PtexUtils::ReduceFn reducefn)
{
AutoMutex rlocker(r->reducelock);
if (face) {
AutoLockCache clocker(_cache->cachelock);
if (face) {
face->ref();
return;
}
}
FaceData* volatile newface = 0;
Res newtileres;
if (newres.ulog2 == 1 || newres.vlog2 == 1) {
newtileres = newres;
}
else {
newtileres = _tileres;
if (newtileres.ulog2 > newres.ulog2) newtileres.ulog2 = newres.ulog2;
if (newtileres.vlog2 > newres.vlog2) newtileres.vlog2 = newres.vlog2;
}
int newntiles = newres.ntiles(newtileres);
if (newntiles == 1) {
PtexFaceData** tiles = (PtexFaceData**) alloca(_ntiles * sizeof(PtexFaceData*));
bool allConstant = true;
for (int i = 0; i < _ntiles; i++) {
PtexFaceData* tile = tiles[i] = getTile(i);
allConstant = (allConstant && tile->isConstant() &&
(i == 0 || (0 == memcmp(tiles[0]->getData(), tile->getData(),
_pixelsize))));
}
if (allConstant) {
newface = new ConstantFace((void**)&face, _cache, _pixelsize);
memcpy(newface->getData(), tiles[0]->getData(), _pixelsize);
}
else {
newface = new PackedFace((void**)&face, _cache, newres,
_pixelsize, _pixelsize*newres.size());
int tileures = _tileres.u();
int tilevres = _tileres.v();
int sstride = _pixelsize * tileures;
int dstride = _pixelsize * newres.u();
int dstepu = dstride/_ntilesu;
int dstepv = dstride*newres.v()/_ntilesv - dstepu*(_ntilesu-1);
char*
dst = (
char*) newface->getData();
for (int i = 0; i < _ntiles;) {
PtexFaceData* tile = tiles[i];
if (tile->isConstant())
PtexUtils::fill(tile->getData(),
dst, dstride,
newres.u()/_ntilesu, newres.v()/_ntilesv,
_pixelsize);
else
reducefn(tile->getData(), sstride, tileures, tilevres,
dst, dstride, _dt, _nchan);
i++;
dst += i%_ntilesu ? dstepu : dstepv;
}
}
for (int i = 0; i < _ntiles; i++) tiles[i]->release();
}
else {
newface = new TiledReducedFace((void**)&face, _cache, newres, newtileres,
_dt, _nchan, this, reducefn);
}
AutoLockCache clocker(_cache->cachelock);
face = newface;
_cache->purgeData();
}
void PtexReader::TiledFaceBase::getPixel(int ui, int vi, void* result)
{
int tileu = ui >> _tileres.ulog2;
int tilev = vi >> _tileres.vlog2;
PtexPtr<PtexFaceData> tile ( getTile(tilev * _ntilesu + tileu) );
tile->getPixel(ui - (tileu<<_tileres.ulog2),
vi - (tilev<<_tileres.vlog2), result);
}
PtexFaceData* PtexReader::TiledReducedFace::getTile(int tile)
{
_cache->cachelock.lock();
FaceData*& face = _tiles[tile];
if (face) {
face->ref();
_cache->cachelock.unlock();
}
int pntilesu = _parentface->ntilesu();
int pntilesv = _parentface->ntilesv();
int nu = pntilesu / _ntilesu;
int nv = pntilesv / _ntilesv;
int ntiles = nu*nv;
PtexFaceData** tiles = (PtexFaceData**) alloca(ntiles * sizeof(PtexFaceData*));
bool allConstant = true;
int ptile = (tile/_ntilesu) * nv * pntilesu + (tile%_ntilesu) * nu;
for (int i = 0; i < ntiles;) {
_cache->cachelock.unlock();
PtexFaceData* tile = tiles[i] = _parentface->getTile(ptile);
_cache->cachelock.lock();
allConstant = (allConstant && tile->isConstant() &&
(i==0 || (0 == memcmp(tiles[0]->getData(), tile->getData(),
_pixelsize))));
i++;
ptile += i%nu? 1 : pntilesu - nu + 1;
}
if (face) {
face->ref();
_cache->cachelock.unlock();
for (int i = 0; i < ntiles; i++) tiles[i]->release();
}
if (allConstant) {
face = new ConstantFace((void**)&face, _cache, _pixelsize);
memcpy(face->getData(), tiles[0]->getData(), _pixelsize);
}
else {
face = new PackedFace((void**)&face, _cache, _tileres,
_pixelsize, _pixelsize*_tileres.size());
int ptileures = _parentface->tileres().u();
int ptilevres = _parentface->tileres().v();
int sstride = ptileures * _pixelsize;
int dstride = _tileres.u() * _pixelsize;
int dstepu = dstride/nu;
int dstepv = dstride*_tileres.v()/nv - dstepu*(nu-1);
char* dst = (char*) face->getData();
for (int i = 0; i < ntiles;) {
PtexFaceData* tile = tiles[i];
if (tile->isConstant())
PtexUtils::fill(tile->getData(),
dst, dstride,
_tileres.u()/nu, _tileres.v()/nv,
_pixelsize);
else
_reducefn(tile->getData(), sstride, ptileures, ptilevres,
dst, dstride, _dt, _nchan);
i++;
dst += i%nu ? dstepu : dstepv;
}
}
_cache->cachelock.unlock();
for (int i = 0; i < ntiles; i++) tiles[i]->release();
}