#include "PtexPlatform.h"
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <sstream>
#include "Ptexture.h"
#include "PtexUtils.h"
#include "PtexWriter.h"
namespace {
{
static Mutex lock;
AutoMutex locker(lock);
static int initialized = 0;
if (!initialized) {
initialized = 1;
#ifdef WINDOWS
DWORD result = ::GetTempPath(0, _T(""));
if (result > 0) {
std::vector<TCHAR> tempPath(result + 1);
result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
if (result > 0 && result <= tempPath.size())
tempPath.begin() + static_cast<std::size_t>(result));
else
tmpdir = ".";
}
#else
const char*
t = getenv(
"TEMP");
if (!t) t = getenv("TMP");
if (!t) t = "/tmp";
#endif
}
#ifdef WINDOWS
s << tmpdir <<
"/" <<
"PtexTmp" << getpid() <<
"_" << ++
count;
tmppath = s.str();
return fopen((char*) tmppath.c_str(), "wb+");
#else
tmppath = tmpdir + "/PtexTmpXXXXXX";
int fd = mkstemp((char*) tmppath.c_str());
return fdopen(fd, "w+");
#endif
}
std::string fileError(
const char* message,
const char* path)
{
std::stringstream str;
str << message << path << "\n" << strerror(errno);
return str.str();
}
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan,
Ptex::String& error)
{
if (!PtexIO::LittleEndian()) {
error = "PtexWriter doesn't currently support big-endian cpu's";
return 0;
}
if (mt < Ptex::mt_triangle || mt > Ptex::mt_quad) {
error = "PtexWriter error: Invalid mesh type";
return 0;
}
if (dt < Ptex::dt_uint8 || dt > Ptex::dt_float) {
error = "PtexWriter error: Invalid data type";
return 0;
}
if (nchannels <= 0) {
error = "PtexWriter error: Invalid number of channels";
return 0;
}
if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
error = "PtexWriter error: Invalid alpha channel";
return 0;
}
return 1;
}
}
PtexWriter* PtexWriter::open(const char* path,
Ptex::MeshType mt, Ptex::DataType dt,
int nchannels, int alphachan, int nfaces,
Ptex::String& error, bool genmipmaps)
{
if (!checkFormat(mt, dt, nchannels, alphachan, error))
return 0;
PtexMainWriter*
w =
new PtexMainWriter(path, 0,
mt, dt, nchannels, alphachan, nfaces,
genmipmaps);
if (!w->ok(error)) {
w->release();
return 0;
}
}
PtexWriter* PtexWriter::edit(const char* path, bool incremental,
Ptex::MeshType mt, Ptex::DataType dt,
int nchannels, int alphachan, int nfaces,
Ptex::String& error, bool genmipmaps)
{
if (!checkFormat(mt, dt, nchannels, alphachan, error))
return 0;
FILE* fp = fopen(path, "rb+");
if (!fp && errno != ENOENT) {
error = fileError("Can't open ptex file for update: ", path).c_str();
}
PtexWriterBase* w = 0;
if (incremental && fp) {
w = new PtexIncrWriter(path, fp, mt, dt, nchannels, alphachan, nfaces);
}
else {
PtexTexture* tex = 0;
if (fp) {
fclose(fp);
tex = PtexTexture::open(path, error);
if (!tex) return 0;
bool headerMatch = (mt == tex->meshType() &&
dt == tex->dataType() &&
nchannels == tex->numChannels() &&
alphachan == tex->alphaChannel() &&
nfaces == tex->numFaces());
if (!headerMatch) {
std::stringstream str;
str << "PtexWriter::edit error: header doesn't match existing file, "
<< "conversions not currently supported";
error = str.str().c_str();
return 0;
}
}
w = new PtexMainWriter(path, tex, mt, dt, nchannels, alphachan,
nfaces, genmipmaps);
}
if (!w->ok(error)) {
w->release();
return 0;
}
}
bool PtexWriter::applyEdits(const char* path, Ptex::String& error)
{
PtexTexture* tex = PtexTexture::open(path, error);
if (!tex) return 0;
if (tex->hasEdits()) {
PtexWriter* w = new PtexMainWriter(path, tex, tex->meshType(), tex->dataType(),
tex->numChannels(), tex->alphaChannel(), tex->numFaces(),
tex->hasMipMaps());
if (!w->close(error)) return 0;
w->release();
}
return 1;
}
PtexWriterBase::PtexWriterBase(const char* path,
Ptex::MeshType mt, Ptex::DataType dt,
int nchannels, int alphachan, int nfaces,
bool compress)
: _ok(true),
_path(path),
_tilefp(0)
{
memset(&_header, 0, sizeof(_header));
_header.magic = Magic;
_header.version = PtexFileMajorVersion;
_header.minorversion = PtexFileMinorVersion;
_header.meshtype = mt;
_header.datatype = dt;
_header.alphachan = alphachan;
_header.nchannels = nchannels;
_header.nfaces = nfaces;
_header.nlevels = 0;
_header.extheadersize = sizeof(_extheader);
_pixelSize = _header.pixelSize();
memset(&_extheader, 0, sizeof(_extheader));
if (mt == mt_triangle)
_reduceFn = &PtexUtils::reduceTri;
else
_reduceFn = &PtexUtils::reduce;
memset(&_zstream, 0, sizeof(_zstream));
deflateInit(&_zstream, compress ? Z_DEFAULT_COMPRESSION : 0);
_tilefp = OpenTempFile(_tilepath);
if (!_tilefp) {
setError(fileError("Error creating temp file: ", _tilepath.c_str()));
}
}
void PtexWriterBase::release()
{
Ptex::String error;
if (_tilefp && !close(error))
delete this;
}
PtexWriterBase::~PtexWriterBase()
{
deflateEnd(&_zstream);
}
bool PtexWriterBase::close(Ptex::String& error)
{
if (_ok) finish();
if (!_ok) getError(error);
if (_tilefp) {
fclose(_tilefp);
unlink(_tilepath.c_str());
_tilefp = 0;
}
return _ok;
}
bool PtexWriterBase::storeFaceInfo(
int faceid, FaceInfo&
f,
const FaceInfo&
src,
int flags)
{
if (faceid < 0 || size_t(faceid) >= _header.nfaces) {
setError("PtexWriter error: faceid out of range");
return 0;
}
if (_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
setError("PtexWriter error: asymmetric face res not supported for triangle textures");
return 0;
}
if (_header.meshtype == mt_triangle) {
f.flags = 0;
f.adjfaces[3] = -1;
f.adjedges &= 0x3f;
}
else {
f.flags &= FaceInfo::flag_subface;
}
f.flags |= flags;
return 1;
}
void PtexWriterBase::writeMeta(
const char* key,
const char*
value)
{
addMetaData(key, mdt_string, value, int(strlen(value)+1));
}
void PtexWriterBase::writeMeta(
const char* key,
const int8_t* value,
int count)
{
addMetaData(key, mdt_int8, value, count);
}
void PtexWriterBase::writeMeta(
const char* key,
const int16_t* value,
int count)
{
addMetaData(key, mdt_int16, value, count*sizeof(int16_t));
}
void PtexWriterBase::writeMeta(const char* key, const int32_t* value, int count)
{
addMetaData(key, mdt_int32, value, count*sizeof(int32_t));
}
void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
{
addMetaData(key, mdt_float, value, count*sizeof(float));
}
void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
{
addMetaData(key, mdt_double, value, count*sizeof(double));
}
void PtexWriterBase::writeMeta(PtexMetaData*
data)
{
int nkeys = data->numKeys();
for (int i = 0; i < nkeys; i++) {
const char* key = 0;
data->getKey(i, key, type);
switch (type) {
case mdt_string:
{
const char* val=0;
data->getValue(key, val);
writeMeta(key, val);
}
break;
case mdt_int8:
{
const int8_t* val=0;
data->getValue(key, val, count);
writeMeta(key, val, count);
}
break;
case mdt_int16:
{
const int16_t* val=0;
data->getValue(key, val, count);
writeMeta(key, val, count);
}
break;
case mdt_int32:
{
const int32_t* val=0;
data->getValue(key, val, count);
writeMeta(key, val, count);
}
break;
case mdt_float:
{
const float* val=0;
data->getValue(key, val, count);
writeMeta(key, val, count);
}
break;
case mdt_double:
{
const double* val=0;
data->getValue(key, val, count);
writeMeta(key, val, count);
}
break;
}
}
}
void PtexWriterBase::addMetaData(
const char* key, MetaDataType
t,
const void* value,
int size)
{
if (strlen(key) > 255) {
std::stringstream str;
str << "PtexWriter error: meta data key too long (max=255) \"" << key << "\"";
setError(str.str());
return;
}
if (size <= 0) {
std::stringstream str;
str << "PtexWriter error: meta data size <= 0 for \"" << key << "\"";
setError(str.str());
}
std::map<std::string,int>::iterator iter = _metamap.find(key);
if (iter != _metamap.end()) {
index = iter->second;
}
else {
index = _metadata.size();
_metadata.resize(index+1);
}
MetaEntry& m = _metadata[
index];
m.key = key;
m.data.resize(size);
memcpy(&m.data[0], value, size);
}
int PtexWriterBase::writeBlank(FILE* fp, int size)
{
if (!_ok) return 0;
static char zeros[BlockSize] = {0};
while (remain > 0) {
remain -= writeBlock(fp, zeros, remain < BlockSize ? remain : BlockSize);
}
}
int PtexWriterBase::writeBlock(FILE* fp, const void* data, int size)
{
if (!_ok) return 0;
if (!fwrite(data, size, 1, fp)) {
setError("PtexWriter error: file write failed");
return 0;
}
}
int PtexWriterBase::writeZipBlock(FILE* fp, const void* data, int size, bool finish)
{
if (!_ok) return 0;
void* buff = alloca(BlockSize);
_zstream.next_in = (Bytef*)data;
_zstream.avail_in =
size;
while (1) {
_zstream.next_out = (Bytef*)buff;
_zstream.avail_out = BlockSize;
int zresult = deflate(&_zstream, finish ? Z_FINISH : Z_NO_FLUSH);
int size = BlockSize - _zstream.avail_out;
if (size > 0) writeBlock(fp, buff, size);
if (zresult == Z_STREAM_END) break;
if (zresult != Z_OK) {
setError("PtexWriter error: data compression internal error");
break;
}
if (!finish && _zstream.avail_out != 0)
break;
}
if (!finish) return 0;
int total = _zstream.total_out;
deflateReset(&_zstream);
return total;
}
int PtexWriterBase::readBlock(FILE* fp, void* data, int size)
{
if (!fread(data, size, 1, fp)) {
setError("PtexWriter error: temp file read failed");
return 0;
}
}
int PtexWriterBase::copyBlock(FILE*
dst, FILE* src, FilePos pos,
int size)
{
if (size <= 0) return 0;
fseeko(src, pos, SEEK_SET);
void* buff = alloca(BlockSize);
while (remain) {
int nbytes = remain < BlockSize ? remain : BlockSize;
if (!fread(buff, nbytes, 1, src)) {
setError("PtexWriter error: temp file read failed");
return 0;
}
if (!writeBlock(dst, buff, nbytes)) break;
remain -= nbytes;
}
}
Ptex::Res PtexWriterBase::calcTileRes(Res faceres)
{
int facesize = faceres.size() * _pixelSize;
int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
if (ntileslog2 == 0) return faceres;
int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
Res tileres;
tileres.ulog2 = PtexUtils::min((n+1)/2, int(faceres.ulog2));
tileres.vlog2 = PtexUtils::min(n - tileres.ulog2, int(faceres.vlog2));
return tileres;
}
void PtexWriterBase::writeConstFaceBlock(FILE* fp, const void* data,
FaceDataHeader& fdh)
{
fdh.set(_pixelSize, enc_constant);
writeBlock(fp, data, _pixelSize);
}
void PtexWriterBase::writeFaceBlock(FILE* fp,
const void* data,
int stride,
Res
res, FaceDataHeader& fdh)
{
int ures = res.u(), vres = res.v();
int blockSize = ures*vres*_pixelSize;
bool useMalloc = blockSize > AllocaMax;
char* buff = useMalloc ? (char*) malloc(blockSize) : (char*)alloca(blockSize);
PtexUtils::deinterleave(data, stride, ures, vres, buff,
ures*DataSize(_header.datatype),
_header.datatype, _header.nchannels);
bool diff = (_header.datatype == dt_uint8 ||
_header.datatype == dt_uint16);
if (diff) PtexUtils::encodeDifference(buff, blockSize, _header.datatype);
int zippedsize = writeZipBlock(fp, buff, blockSize);
fdh.set(zippedsize, diff ? enc_diffzipped : enc_zipped);
if (useMalloc) free(buff);
}
void PtexWriterBase::writeFaceData(FILE* fp, const void* data, int stride,
Res res, FaceDataHeader& fdh)
{
Res tileres = calcTileRes(res);
int ntilesu = res.ntilesu(tileres);
int ntilesv = res.ntilesv(tileres);
int ntiles = ntilesu * ntilesv;
if (ntiles == 1) {
writeFaceBlock(fp, data, stride, res, fdh);
} else {
rewind(_tilefp);
std::vector<FaceDataHeader> tileHeader(ntiles);
int tileures = tileres.u();
int tilevres = tileres.v();
int tileustride = tileures*_pixelSize;
int tilevstride = tilevres*
stride;
FaceDataHeader* tdh = &tileHeader[0];
int datasize = 0;
const char* rowp = (const char*) data;
const char* rowpend = rowp + ntilesv * tilevstride;
for (; rowp != rowpend; rowp += tilevstride) {
const char* pend = p + ntilesu * tileustride;
for (; p != pend; tdh++, p += tileustride) {
if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize))
writeConstFaceBlock(_tilefp, p, *tdh);
else
writeFaceBlock(_tilefp, p, stride, tileres, *tdh);
datasize += tdh->blocksize();
}
}
uint32_t tileheadersize = writeZipBlock(_tilefp, &tileHeader[0],
int(sizeof(FaceDataHeader)*tileHeader.size()));
int totalsize = 0;
totalsize += writeBlock(fp, &tileres, sizeof(Res));
totalsize += writeBlock(fp, &tileheadersize, sizeof(tileheadersize));
totalsize += copyBlock(fp, _tilefp, datasize, tileheadersize);
totalsize += copyBlock(fp, _tilefp, 0, datasize);
fdh.set(totalsize, enc_tiled);
}
}
void PtexWriterBase::writeReduction(FILE* fp, const void* data, int stride, Res res)
{
Ptex::Res newres(res.ulog2-1, res.vlog2-1);
int buffsize = newres.size() * _pixelSize;
bool useMalloc = buffsize > AllocaMax;
char* buff = useMalloc ? (char*) malloc(buffsize) : (char*)alloca(buffsize);
int dstride = newres.u() * _pixelSize;
_reduceFn(data, stride, res.u(), res.v(), buff, dstride, _header.datatype, _header.nchannels);
writeBlock(fp, buff, buffsize);
if (useMalloc) free(buff);
}
int PtexWriterBase::writeMetaDataBlock(FILE* fp, MetaEntry& val)
{
uint8_t keysize = uint8_t(val.key.size()+1);
uint8_t datatype = val.datatype;
uint32_t datasize = uint32_t(val.data.size());
writeZipBlock(fp, &keysize, sizeof(keysize), false);
writeZipBlock(fp, val.key.c_str(), keysize, false);
writeZipBlock(fp, &datatype, sizeof(datatype), false);
writeZipBlock(fp, &datasize, sizeof(datasize), false);
writeZipBlock(fp, &val.data[0], datasize, false);
int memsize = (sizeof(keysize) + keysize + sizeof(datatype)
+ sizeof(datasize) + datasize);
return memsize;
}
PtexMainWriter::PtexMainWriter(const char* path, PtexTexture* tex,
Ptex::MeshType mt, Ptex::DataType dt,
int nchannels, int alphachan, int nfaces, bool genmipmaps)
: PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
true),
_hasNewData(false),
_genmipmaps(genmipmaps),
_reader(0)
{
_tmpfp = OpenTempFile(_tmppath);
if (!_tmpfp) {
setError(fileError("Error creating temp file: ", _tmppath.c_str()));
return;
}
_newpath = path; _newpath += ".new";
_levels.reserve(20);
_levels.resize(1);
_faceinfo.resize(nfaces);
for (int i = 0; i < nfaces; i++) _faceinfo[i].flags = uint8_t(-1);
_levels.front().pos.resize(nfaces);
_levels.front().fdh.resize(nfaces);
_rpos.resize(nfaces);
_constdata.resize(nfaces*_pixelSize);
if (tex) {
_reader = dynamic_cast<PtexReader*>(tex);
if (!_reader) {
setError("Internal error: dynamic_cast<PtexReader*> failed");
tex->release();
return;
}
setBorderModes(tex->uBorderMode(), tex->vBorderMode());
PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
writeMeta(meta);
_hasNewData = _reader->hasEdits();
}
}
PtexMainWriter::~PtexMainWriter()
{
if (_reader) _reader->release();
}
bool PtexMainWriter::close(Ptex::String& error)
{
bool result = PtexWriterBase::close(error);
if (_reader) {
_reader->release();
_reader = 0;
}
if (_tmpfp) {
fclose(_tmpfp);
unlink(_tmppath.c_str());
_tmpfp = 0;
}
if (result && _hasNewData) {
unlink(_path.c_str());
if (rename(_newpath.c_str(), _path.c_str()) == -1) {
error = fileError("Can't write to ptex file: ", _path.c_str()).c_str();
unlink(_newpath.c_str());
result = false;
}
}
return result;
}
bool PtexMainWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
{
if (!_ok) return 0;
if (stride == 0) stride = f.res.u()*_pixelSize;
if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
return writeConstantFace(faceid, f, data);
if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return 0;
_levels.front().pos[faceid] = ftello(_tmpfp);
writeFaceData(_tmpfp, data, stride, f.res, _levels.front().fdh[faceid]);
if (!_ok) return 0;
uint8_t* temp = 0;
if (_header.hasAlpha()) {
int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
temp = (uint8_t*) malloc(rowlen * nrows);
PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
PtexUtils::multalpha(temp, f.res.size(), _header.datatype, _header.nchannels,
_header.alphachan);
data = temp;
stride = rowlen;
}
if (_genmipmaps &&
(f.res.ulog2 > MinReductionLog2 && f.res.vlog2 > MinReductionLog2))
{
_rpos[faceid] = ftello(_tmpfp);
writeReduction(_tmpfp, data, stride, f.res);
}
else {
storeConstValue(faceid, data, stride, f.res);
}
if (temp) free(temp);
_hasNewData = true;
return 1;
}
bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
{
if (!_ok) return 0;
if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
_hasNewData = true;
return 1;
}
void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
{
uint8_t* constdata = &_constdata[faceid*_pixelSize];
PtexUtils::average(data, stride, res.u(), res.v(), constdata,
_header.datatype, _header.nchannels);
if (_header.hasAlpha())
PtexUtils::divalpha(constdata, 1, _header.datatype, _header.nchannels, _header.alphachan);
}
void PtexMainWriter::finish()
{
if (!_hasNewData) return;
if (_reader) {
for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
if (_faceinfo[i].flags == uint8_t(-1)) {
const Ptex::FaceInfo& info = _reader->getFaceInfo(i);
int size = _pixelSize * info.res.size();
if (info.isConstant()) {
PtexPtr<PtexFaceData>
data ( _reader->getData(i) );
if (data) {
writeConstantFace(i, info, data->getData());
}
} else {
void* data = malloc(size);
_reader->getData(i, data, 0);
writeFace(i, info, data, 0);
free(data);
}
}
}
}
else {
for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
if (_faceinfo[i].flags == uint8_t(-1))
_faceinfo[i].flags = FaceInfo::flag_constant;
}
}
if (_genmipmaps)
generateReductions();
flagConstantNeighorhoods();
_header.nlevels = uint16_t(_levels.size());
_header.nfaces = uint32_t(_faceinfo.size());
FILE* newfp = fopen(_newpath.c_str(), "wb+");
if (!newfp) {
setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
return;
}
writeBlank(newfp, HeaderSize);
writeBlank(newfp, ExtHeaderSize);
_header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0],
sizeof(FaceInfo)*_header.nfaces);
_header.constdatasize = writeZipBlock(newfp, &_constdata[0], int(_constdata.size()));
FilePos levelInfoPos = ftello(newfp);
writeBlank(newfp, LevelInfoSize * _header.nlevels);
std::vector<LevelInfo> levelinfo(_header.nlevels);
for (int li = 0; li < _header.nlevels; li++)
{
LevelInfo& info = levelinfo[li];
LevelRec&
level = _levels[li];
int nfaces =
int(level.fdh.size());
info.nfaces = nfaces;
info.levelheadersize = writeZipBlock(newfp, &level.fdh[0],
sizeof(FaceDataHeader)*nfaces);
info.leveldatasize = info.levelheadersize;
for (int fi = 0; fi < nfaces; fi++)
info.leveldatasize += copyBlock(newfp, _tmpfp, level.pos[fi],
level.fdh[fi].blocksize());
_header.leveldatasize += info.leveldatasize;
}
rewind(_tmpfp);
if (!_metadata.empty())
writeMetaData(newfp);
_extheader.editdatapos = ftello(newfp);
fseeko(newfp, levelInfoPos, SEEK_SET);
_header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels);
fseeko(newfp, 0, SEEK_SET);
writeBlock(newfp, &_header, HeaderSize);
writeBlock(newfp, &_extheader, ExtHeaderSize);
fclose(newfp);
}
void PtexMainWriter::flagConstantNeighorhoods()
{
for (
int faceid = 0, n =
int(_faceinfo.size()); faceid <
n; faceid++) {
FaceInfo& f = _faceinfo[faceid];
if (!f.isConstant()) continue;
uint8_t* constdata = &_constdata[faceid*_pixelSize];
bool isConst = true;
bool isTriangle = _header.meshtype == mt_triangle;
int nedges = isTriangle ? 3 : 4;
for (int eid = 0; eid < nedges; eid++) {
bool prevWasSubface = f.isSubface();
int prevFid = faceid;
int afid = f.adjface(eid);
int aeid = f.adjedge(eid);
int count = 0;
const int maxcount = 10;
while (afid != faceid) {
if (afid < 0 || ++count == maxcount)
{ isConst = false; break; }
FaceInfo& af = _faceinfo[afid];
if (!af.isConstant() ||
0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
{ isConst = false; break; }
bool isSubface = af.isSubface();
bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
prevWasSubface = isSubface;
if (isT) {
FaceInfo& pf = _faceinfo[afid];
int peid = af.adjedge(aeid);
peid = (peid + 3) % 4;
afid = pf.adjface(peid);
aeid = pf.adjedge(peid);
aeid = (aeid + 3) % 4;
}
else {
aeid = (aeid + 1) % nedges;
afid = af.adjface(aeid);
aeid = af.adjedge(aeid);
}
}
if (!isConst) break;
}
if (isConst) f.flags |= FaceInfo::flag_nbconstant;
}
}
void PtexMainWriter::generateReductions()
{
int nfaces = _header.nfaces;
_rfaceids.resize(nfaces);
_faceids_r.resize(nfaces);
PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
for (int rfaceid = nfaces-1, cutoffres = MinReductionLog2; rfaceid >= 0; rfaceid--) {
int faceid = _faceids_r[rfaceid];
FaceInfo&
face = _faceinfo[faceid];
Res res = face.res;
int min = face.isConstant() ? 1 : PtexUtils::min(res.ulog2, res.vlog2);
while (min > cutoffres) {
int size = rfaceid+1;
_levels.push_back(LevelRec());
LevelRec& level = _levels.back();
level.pos.resize(size);
level.fdh.resize(size);
cutoffres++;
}
}
int buffsize = 0;
for (int i = 0; i < nfaces; i++)
buffsize *= _pixelSize;
char* buff = (char*) malloc(buffsize);
int nlevels =
int(_levels.size());
for (int i = 1; i < nlevels; i++) {
LevelRec& level = _levels[i];
int nextsize = (i+1 < nlevels)? int(_levels[i+1].fdh.size()) : 0;
for (
int rfaceid = 0, size =
int(level.fdh.size()); rfaceid <
size; rfaceid++) {
int faceid = _faceids_r[rfaceid];
Res res = _faceinfo[faceid].res;
res.ulog2 -= i; res.vlog2 -= i;
int stride = res.u() * _pixelSize;
int blocksize = res.size() * _pixelSize;
fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
readBlock(_tmpfp, buff, blocksize);
fseeko(_tmpfp, 0, SEEK_END);
level.pos[rfaceid] = ftello(_tmpfp);
writeFaceData(_tmpfp, buff, stride, res, level.fdh[rfaceid]);
if (!_ok) return;
if (rfaceid < nextsize) {
fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
writeReduction(_tmpfp, buff, stride, res);
}
else {
storeConstValue(faceid, buff, stride, res);
}
}
}
fseeko(_tmpfp, 0, SEEK_END);
free(buff);
}
void PtexMainWriter::writeMetaData(FILE* fp)
{
std::vector<MetaEntry*> lmdEntries;
for (
int i = 0, n = _metadata.size(); i <
n; i++) {
MetaEntry& e = _metadata[i];
if (int(e.data.size()) > MetaDataThreshold) {
lmdEntries.push_back(&e);
}
else {
_header.metadatamemsize += writeMetaDataBlock(fp, e);
}
}
if (_header.metadatamemsize) {
_header.metadatazipsize = writeZipBlock(fp, 0, 0, true);
}
writeBlank(fp, sizeof(uint64_t));
int nLmd = lmdEntries.size();
if (nLmd > 0) {
std::vector<FilePos> lmdoffset(nLmd);
std::vector<uint32_t> lmdzipsize(nLmd);
for (int i = 0; i < nLmd; i++) {
MetaEntry* e= lmdEntries[i];
lmdoffset[i] = ftello(_tmpfp);
lmdzipsize[i] = writeZipBlock(_tmpfp, &e->data[0], e->data.size());
}
for (int i = 0; i < nLmd; i++) {
MetaEntry* e = lmdEntries[i];
uint8_t keysize = uint8_t(e->key.size()+1);
uint8_t datatype = e->datatype;
uint32_t datasize = e->data.size();
uint32_t zipsize = lmdzipsize[i];
writeZipBlock(fp, &keysize, sizeof(keysize), false);
writeZipBlock(fp, e->key.c_str(), keysize, false);
writeZipBlock(fp, &datatype, sizeof(datatype), false);
writeZipBlock(fp, &datasize, sizeof(datasize), false);
writeZipBlock(fp, &zipsize, sizeof(zipsize), false);
_extheader.lmdheadermemsize +=
sizeof(keysize) + keysize + sizeof(datatype) + sizeof(datasize) + sizeof(zipsize);
}
_extheader.lmdheaderzipsize = writeZipBlock(fp, 0, 0, true);
for (int i = 0; i < nLmd; i++) {
_extheader.lmddatasize +=
copyBlock(fp, _tmpfp, lmdoffset[i], lmdzipsize[i]);
}
}
}
PtexIncrWriter::PtexIncrWriter(const char* path, FILE* fp,
Ptex::MeshType mt, Ptex::DataType dt,
int nchannels, int alphachan, int nfaces)
: PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
false),
_fp(fp)
{
if (!fread(&_header, PtexIO::HeaderSize, 1, fp) || _header.magic != Magic) {
std::stringstream str;
str << "Not a ptex file: " << path;
setError(str.str());
return;
}
bool headerMatch = (mt == _header.meshtype &&
dt == _header.datatype &&
nchannels == _header.nchannels &&
alphachan ==
int(_header.alphachan) &&
nfaces ==
int(_header.nfaces));
if (!headerMatch) {
std::stringstream str;
str << "PtexWriter::edit error: header doesn't match existing file, "
<< "conversions not currently supported";
setError(str.str());
return;
}
memset(&_extheader, 0, sizeof(_extheader));
if (!fread(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, fp)) {
std::stringstream str;
str << "Error reading extended header: " << path;
setError(str.str());
return;
}
fseeko(_fp, 0, SEEK_END);
}
PtexIncrWriter::~PtexIncrWriter()
{
}
bool PtexIncrWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
{
if (stride == 0) stride = f.res.u()*_pixelSize;
if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
return writeConstantFace(faceid, f, data);
uint8_t edittype = et_editfacedata;
uint32_t editsize;
EditFaceDataHeader efdh;
efdh.faceid = faceid;
if (!storeFaceInfo(faceid, efdh.faceinfo, f))
return 0;
FilePos pos = ftello(_fp);
writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(efdh));
uint8_t* constval = (uint8_t*) malloc(_pixelSize);
if (_header.hasAlpha()) {
int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
uint8_t* temp = (uint8_t*) malloc(rowlen * nrows);
PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
PtexUtils::multalpha(temp, f.res.size(), _header.datatype, _header.nchannels,
_header.alphachan);
PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
_header.datatype, _header.nchannels);
PtexUtils::divalpha(constval, 1, _header.datatype, _header.nchannels,
_header.alphachan);
free(temp);
}
else {
PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
_header.datatype, _header.nchannels);
}
writeBlock(_fp, constval, _pixelSize);
free(constval);
writeFaceData(_fp, data, stride, f.res, efdh.fdh);
editsize = sizeof(efdh) + _pixelSize + efdh.fdh.blocksize();
fseeko(_fp, pos, SEEK_SET);
writeBlock(_fp, &edittype, sizeof(edittype));
writeBlock(_fp, &editsize, sizeof(editsize));
writeBlock(_fp, &efdh, sizeof(efdh));
fseeko(_fp, 0, SEEK_END);
return 1;
}
bool PtexIncrWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
{
uint8_t edittype = et_editfacedata;
uint32_t editsize;
EditFaceDataHeader efdh;
efdh.faceid = faceid;
efdh.fdh.set(0, enc_constant);
editsize = sizeof(efdh) + _pixelSize;
if (!storeFaceInfo(faceid, efdh.faceinfo, f, FaceInfo::flag_constant))
return 0;
writeBlock(_fp, &edittype, sizeof(edittype));
writeBlock(_fp, &editsize, sizeof(editsize));
writeBlock(_fp, &efdh, sizeof(efdh));
writeBlock(_fp, data, _pixelSize);
return 1;
}
void PtexIncrWriter::writeMetaDataEdit()
{
uint8_t edittype = et_editmetadata;
uint32_t editsize;
EditMetaDataHeader emdh;
emdh.metadatazipsize = 0;
emdh.metadatamemsize = 0;
FilePos pos = ftello(_fp);
writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(emdh));
for (
int i = 0, n = _metadata.size(); i <
n; i++) {
MetaEntry& e = _metadata[i];
emdh.metadatamemsize += writeMetaDataBlock(_fp, e);
}
emdh.metadatazipsize = writeZipBlock(_fp, 0, 0, true);
editsize = sizeof(emdh) + emdh.metadatazipsize;
fseeko(_fp, pos, SEEK_SET);
writeBlock(_fp, &edittype, sizeof(edittype));
writeBlock(_fp, &editsize, sizeof(editsize));
writeBlock(_fp, &emdh, sizeof(emdh));
fseeko(_fp, 0, SEEK_END);
}
bool PtexIncrWriter::close(Ptex::String& error)
{
bool result = PtexWriterBase::close(error);
if (_fp) {
fclose(_fp);
_fp = 0;
}
return result;
}
void PtexIncrWriter::finish()
{
if (!_metadata.empty()) writeMetaDataEdit();
if (_extheader.editdatapos) {
_extheader.editdatasize = uint64_t(ftello(_fp)) - _extheader.editdatapos;
fseeko(_fp, HeaderSize, SEEK_SET);
fwrite(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, _fp);
}
}