PtexWriter.cpp
Go to the documentation of this file.
/*
PTEX SOFTWARE
Copyright 2014 Disney Enterprises, Inc. All rights reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
Studios" or the names of its contributors may NOT be used to
endorse or promote products derived from this software without
specific prior written permission from Walt Disney Pictures.
Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
/* Ptex writer classes:
PtexIncrWriter implements "incremental" mode and simply appends
"edit" blocks to the end of the file.
PtexMainWriter implements both writing from scratch and updating
an existing file, either to add data or to "roll up" previous
incremental edits.
Because the various headers (faceinfo, levelinfo, etc.) are
variable-length and precede the data, and because the data size
is not known until it is compressed and written, all data
are written to a temp file and then copied at the end to the
final location. This happens during the "finish" phase.
Each time a texture is written to the file, a reduction of the
texture is also generated and stored. These reductions are stored
in a temporary form and recalled later as the resolution levels are
generated.
The final reduction for each face is averaged and stored in the
const data block.
*/
#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"
PTEX_NAMESPACE_BEGIN
namespace {
FILE* OpenTempFile(std::string& tmppath)
{
static Mutex lock;
AutoMutex locker(lock);
// choose temp dir
static std::string tmpdir;
static int initialized = 0;
if (!initialized) {
initialized = 1;
#ifdef PTEX_PLATFORM_WINDOWS
// use GetTempPath API (first call determines length of result)
DWORD result = ::GetTempPath(0, (LPTSTR) L"");
if (result > 0) {
std::vector<TCHAR> tempPath(result + 1);
result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
if (result > 0 && result <= tempPath.size())
tmpdir = std::string(tempPath.begin(),
tempPath.begin() + static_cast<std::size_t>(result));
else
tmpdir = ".";
}
#else
// try $TEMP or $TMP, use /tmp as last resort
const char* t = getenv("TEMP");
if (!t) t = getenv("TMP");
if (!t) t = "/tmp";
tmpdir = t;
#endif
}
// build temp path
#ifdef PTEX_PLATFORM_WINDOWS
// use process id and counter to make unique filename
std::stringstream s;
static int count = 0;
s << tmpdir << "/" << "PtexTmp" << _getpid() << "_" << ++count;
tmppath = s.str();
return fopen((char*) tmppath.c_str(), "wb+");
#else
// use mkstemp to open unique file
tmppath = tmpdir + "/PtexTmpXXXXXX";
int fd = mkstemp(&tmppath[0]);
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)
{
// check to see if given file attributes are valid
if (!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;
}
return w;
}
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;
// try to open existing file (it might not exist)
FILE* fp = fopen(path, "rb+");
if (!fp && errno != ENOENT) {
error = fileError("Can't open ptex file for update: ", path).c_str();
}
PtexWriterBase* w = 0;
// use incremental writer iff incremental mode requested and file exists
if (incremental && fp) {
w = new PtexIncrWriter(path, fp, mt, dt, nchannels, alphachan, nfaces);
}
// otherwise use main writer
else {
PtexTexture* tex = 0;
if (fp) {
// got an existing file, close and reopen with PtexReader
fclose(fp);
// open reader for existing file
tex = PtexTexture::open(path, error);
if (!tex) return 0;
// make sure header matches
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;
}
return w;
}
bool PtexWriter::applyEdits(const char* path, Ptex::String& error)
{
// open reader for existing file
PtexTexture* tex = PtexTexture::open(path, error);
if (!tex) return 0;
// see if we have any edits to apply
if (tex->hasEdits()) {
// create non-incremental writer
PtexPtr<PtexWriter> w(new PtexMainWriter(path, tex, tex->meshType(), tex->dataType(),
tex->numChannels(), tex->alphaChannel(), tex->numFaces(),
tex->hasMipMaps()));
// close to rebuild file
if (!w->close(error)) return 0;
}
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 = (uint16_t)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);
// create temp file for writing tiles
// (must compress each tile before assembling a tiled face)
_tilefp = OpenTempFile(_tilepath);
if (!_tilefp) {
setError(fileError("Error creating temp file: ", _tilepath.c_str()));
}
}
void PtexWriterBase::release()
{
Ptex::String error;
// close writer if app didn't, and report error if any
if (_tilefp && !close(error))
std::cerr << error.c_str() << std::endl;
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;
}
// copy all values
f = src;
// and clear extraneous ones
if (_header.meshtype == mt_triangle) {
f.flags = 0; // no user-settable flags on triangles
f.adjfaces[3] = -1;
f.adjedges &= 0x3f; // clear all but bottom six bits
}
else {
// clear non-user-settable flags
f.flags &= FaceInfo::flag_subface;
}
// set new flags
f.flags |= (uint8_t)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*(int)sizeof(int16_t));
}
void PtexWriterBase::writeMeta(const char* key, const int32_t* value, int count)
{
addMetaData(key, mdt_int32, value, count*(int)sizeof(int32_t));
}
void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
{
addMetaData(key, mdt_float, value, count*(int)sizeof(float));
}
void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
{
addMetaData(key, mdt_double, value, count*(int)sizeof(double));
}
void PtexWriterBase::writeMeta(PtexMetaData* data)
{
int nkeys = data->numKeys();
for (int i = 0; i < nkeys; i++) {
const char* key = 0;
MetaDataType type;
data->getKey(i, key, type);
int count;
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);
int index;
if (iter != _metamap.end()) {
// see if we already have this entry - if so, overwrite it
index = iter->second;
}
else {
// allocate a new entry
index = (int)_metadata.size();
_metadata.resize(index+1);
_metamap[key] = index;
}
MetaEntry& m = _metadata[index];
m.key = key;
m.datatype = t;
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};
int remain = size;
while (remain > 0) {
remain -= writeBlock(fp, zeros, remain < BlockSize ? remain : BlockSize);
}
return size;
}
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;
}
return size;
}
int PtexWriterBase::writeZipBlock(FILE* fp, const void* data, int size, bool finishArg)
{
if (!_ok) return 0;
void* buff = alloca(BlockSize);
_zstream.next_in = (Bytef*) const_cast<void*>(data);
_zstream.avail_in = size;
while (1) {
_zstream.next_out = (Bytef*)buff;
_zstream.avail_out = BlockSize;
int zresult = deflate(&_zstream, finishArg ? Z_FINISH : Z_NO_FLUSH);
int sizeval = BlockSize - _zstream.avail_out;
if (sizeval > 0) writeBlock(fp, buff, sizeval);
if (zresult == Z_STREAM_END) break;
if (zresult != Z_OK) {
setError("PtexWriter error: data compression internal error");
break;
}
if (!finishArg && _zstream.avail_out != 0)
// waiting for more input
break;
}
if (!finishArg) return 0;
int total = (int)_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;
}
return size;
}
int PtexWriterBase::copyBlock(FILE* dst, FILE* src, FilePos pos, int size)
{
if (size <= 0) return 0;
fseeko(src, pos, SEEK_SET);
int remain = size;
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;
}
return size;
}
Ptex::Res PtexWriterBase::calcTileRes(Res faceres)
{
// desired number of tiles = floor(log2(facesize / tilesize))
int facesize = faceres.size() * _pixelSize;
int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
if (ntileslog2 == 0) return faceres;
// number of tiles is defined as:
// ntileslog2 = ureslog2 + vreslog2 - (tile_ureslog2 + tile_vreslog2)
// rearranging to solve for the tile res:
// tile_ureslog2 + tile_vreslog2 = ureslog2 + vreslog2 - ntileslog2
int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
// choose u and v sizes for roughly square result (u ~= v ~= n/2)
// and make sure tile isn't larger than face
Res tileres;
tileres.ulog2 = (int8_t)PtexUtils::min(int((n+1)/2), int(faceres.ulog2));
tileres.vlog2 = (int8_t)PtexUtils::min(int(n - tileres.ulog2), int(faceres.vlog2));
return tileres;
}
void PtexWriterBase::writeConstFaceBlock(FILE* fp, const void* data,
FaceDataHeader& fdh)
{
// write a single const face data block
// record level data for face and output the one pixel value
fdh.set(_pixelSize, enc_constant);
writeBlock(fp, data, _pixelSize);
}
void PtexWriterBase::writeFaceBlock(FILE* fp, const void* data, int stride,
Res res, FaceDataHeader& fdh)
{
// write a single face data block
// copy to temp buffer, and deinterleave
int ures = res.u(), vres = res.v();
int blockSize = ures*vres*_pixelSize;
bool useNew = blockSize > AllocaMax;
char* buff = useNew ? new char [blockSize] : (char*)alloca(blockSize);
PtexUtils::deinterleave(data, stride, ures, vres, buff,
ures*DataSize(datatype()),
datatype(), _header.nchannels);
// difference if needed
bool diff = (datatype() == dt_uint8 ||
datatype() == dt_uint16);
if (diff) PtexUtils::encodeDifference(buff, blockSize, datatype());
// compress and stream data to file, and record size in header
int zippedsize = writeZipBlock(fp, buff, blockSize);
// record compressed size and encoding in data header
fdh.set(zippedsize, diff ? enc_diffzipped : enc_zipped);
if (useNew) delete [] buff;
}
void PtexWriterBase::writeFaceData(FILE* fp, const void* data, int stride,
Res res, FaceDataHeader& fdh)
{
// determine whether to break into tiles
Res tileres = calcTileRes(res);
int ntilesu = res.ntilesu(tileres);
int ntilesv = res.ntilesv(tileres);
int ntiles = ntilesu * ntilesv;
if (ntiles == 1) {
// write single block
writeFaceBlock(fp, data, stride, res, fdh);
} else {
// write tiles to tilefp temp file
rewind(_tilefp);
// alloc tile header
std::vector<FaceDataHeader> tileHeader(ntiles);
int tileures = tileres.u();
int tilevres = tileres.v();
int tileustride = tileures*_pixelSize;
int tilevstride = tilevres*stride;
// output tiles
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* p = rowp;
const char* pend = p + ntilesu * tileustride;
for (; p != pend; tdh++, p += tileustride) {
// determine if tile is constant
if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize))
writeConstFaceBlock(_tilefp, p, *tdh);
else
writeFaceBlock(_tilefp, p, stride, tileres, *tdh);
datasize += tdh->blocksize();
}
}
// output compressed tile header
uint32_t tileheadersize = writeZipBlock(_tilefp, &tileHeader[0],
int(sizeof(FaceDataHeader)*tileHeader.size()));
// output tile data pre-header
int totalsize = 0;
totalsize += writeBlock(fp, &tileres, sizeof(Res));
totalsize += writeBlock(fp, &tileheadersize, sizeof(tileheadersize));
// copy compressed tile header from temp file
totalsize += copyBlock(fp, _tilefp, datasize, tileheadersize);
// copy tile data from temp file
totalsize += copyBlock(fp, _tilefp, 0, datasize);
fdh.set(totalsize, enc_tiled);
}
}
void PtexWriterBase::writeReduction(FILE* fp, const void* data, int stride, Res res)
{
// reduce and write to file
Ptex::Res newres((int8_t)(res.ulog2-1), (int8_t)(res.vlog2-1));
int buffsize = newres.size() * _pixelSize;
bool useNew = buffsize > AllocaMax;
char* buff = useNew ? new char [buffsize] : (char*)alloca(buffsize);
int dstride = newres.u() * _pixelSize;
_reduceFn(data, stride, res.u(), res.v(), buff, dstride, datatype(), _header.nchannels);
writeBlock(fp, buff, buffsize);
if (useNew) delete [] 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 = int(sizeof(keysize) + (size_t)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,
/* compress */ true),
_hasNewData(false),
_genmipmaps(genmipmaps),
_reader(0)
{
_tmpfp = OpenTempFile(_tmppath);
if (!_tmpfp) {
setError(fileError("Error creating temp file: ", _tmppath.c_str()));
return;
}
// data will be written to a ".new" path and then renamed to final location
_newpath = path; _newpath += ".new";
_levels.reserve(20);
_levels.resize(1);
// init faceinfo and set flags to -1 to mark as uninitialized
_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) {
// access reader implementation
// Note: we can assume we have a PtexReader because we opened the tex from the cache
_reader = static_cast< PtexReader*>(tex);
// copy border modes
setBorderModes(tex->uBorderMode(), tex->vBorderMode());
// copy edge filter mode
setEdgeFilterMode(tex->edgeFilterMode());
// copy meta data from existing file
PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
writeMeta(meta);
// see if we have any edits
_hasNewData = _reader->hasEdits();
}
}
PtexMainWriter::~PtexMainWriter()
{
if (_reader) _reader->release();
}
bool PtexMainWriter::close(Ptex::String& error)
{
// closing base writer will write all pending data via finish() method
// and will close _fp (which in this case is on the temp disk)
bool result = PtexWriterBase::close(error);
if (_reader) {
_reader->release();
_reader = 0;
}
if (_tmpfp) {
fclose(_tmpfp);
unlink(_tmppath.c_str());
_tmpfp = 0;
}
if (result && _hasNewData) {
// rename temppath into final location
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;
// auto-compute stride
if (stride == 0) stride = f.res.u()*_pixelSize;
// handle constant case
if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
return writeConstantFace(faceid, f, data);
// non-constant case, ...
// check and store face info
if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return 0;
// record position of current face
_levels.front().pos[faceid] = ftello(_tmpfp);
// write face data
writeFaceData(_tmpfp, data, stride, f.res, _levels.front().fdh[faceid]);
if (!_ok) return 0;
// premultiply (if needed) before making reductions; use temp copy of data
uint8_t* temp = 0;
if (_header.hasAlpha()) {
// first copy to temp buffer
int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
temp = new uint8_t [rowlen * nrows];
PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
// multiply alpha
PtexUtils::multalpha(temp, f.res.size(), datatype(), _header.nchannels,
_header.alphachan);
// override source buffer
data = temp;
stride = rowlen;
}
// generate first reduction (if needed)
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) delete [] temp;
_hasNewData = true;
return 1;
}
bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
{
if (!_ok) return 0;
// check and store face info
if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
// store face value in constant block
memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
_hasNewData = true;
return 1;
}
void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
{
// compute average value and store in _constdata block
uint8_t* constdata = &_constdata[faceid*_pixelSize];
PtexUtils::average(data, stride, res.u(), res.v(), constdata,
datatype(), _header.nchannels);
if (_header.hasAlpha())
PtexUtils::divalpha(constdata, 1, datatype(), _header.nchannels, _header.alphachan);
}
void PtexMainWriter::finish()
{
// do nothing if there's no new data to write
if (!_hasNewData) return;
// copy missing faces from _reader
if (_reader) {
for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
if (_faceinfo[i].flags == uint8_t(-1)) {
// copy face data
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 {
char* data = new char [size];
_reader->getData(i, data, 0);
writeFace(i, info, data, 0);
delete [] data;
}
}
}
}
else {
// just flag missing faces as constant (black)
for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
if (_faceinfo[i].flags == uint8_t(-1))
_faceinfo[i].flags = FaceInfo::flag_constant;
}
}
// write reductions to tmp file
if (_genmipmaps)
generateReductions();
// flag faces w/ constant neighborhoods
flagConstantNeighorhoods();
// update header
_header.nlevels = uint16_t(_levels.size());
_header.nfaces = uint32_t(_faceinfo.size());
// create new file
FILE* newfp = fopen(_newpath.c_str(), "wb+");
if (!newfp) {
setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
return;
}
// write blank header (to fill in later)
writeBlank(newfp, HeaderSize);
writeBlank(newfp, ExtHeaderSize);
// write compressed face info block
_header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0],
(int)sizeof(FaceInfo)*_header.nfaces);
// write compressed const data block
_header.constdatasize = writeZipBlock(newfp, &_constdata[0], int(_constdata.size()));
// write blank level info block (to fill in later)
FilePos levelInfoPos = ftello(newfp);
writeBlank(newfp, LevelInfoSize * _header.nlevels);
// write level data blocks (and record level info)
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;
// output compressed level data header
info.levelheadersize = writeZipBlock(newfp, &level.fdh[0],
(int)sizeof(FaceDataHeader)*nfaces);
info.leveldatasize = info.levelheadersize;
// copy level data from tmp file
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);
// write meta data (if any)
if (!_metadata.empty())
writeMetaData(newfp);
// update extheader for edit data position
_extheader.editdatapos = ftello(newfp);
// rewrite level info block
fseeko(newfp, levelInfoPos, SEEK_SET);
_header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels);
// rewrite header
fseeko(newfp, 0, SEEK_SET);
writeBlock(newfp, &_header, HeaderSize);
writeBlock(newfp, &_extheader, ExtHeaderSize);
fclose(newfp);
}
void PtexMainWriter::flagConstantNeighorhoods()
{
// for each constant face
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];
// check to see if neighborhood is constant
bool isConst = true;
bool isTriangle = _header.meshtype == mt_triangle;
int nedges = isTriangle ? 3 : 4;
for (int eid = 0; isConst && (eid < nedges); eid++) {
bool prevWasSubface = f.isSubface();
int prevFid = faceid;
// traverse around vertex in CW direction
int afid = f.adjface(eid);
int aeid = f.adjedge(eid);
int count = 0;
const int maxcount = 10; // max valence (as safety valve)
while (afid != faceid && afid >= 0 && ++count < maxcount) {
// check if neighbor is constant, and has the same value as face
FaceInfo& af = _faceinfo[afid];
if (!af.isConstant() ||
0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
{ isConst = false; break; }
// if vertex is a T vertex between subface and main face, we can stop
bool isSubface = af.isSubface();
bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
if (isT) break;
prevWasSubface = isSubface;
// traverse around vertex in CW direction
prevFid = afid;
aeid = (aeid + 1) % nedges;
afid = af.adjface(aeid);
aeid = af.adjedge(aeid);
}
if (afid < 0) {
// hit boundary edge, check boundary mode
if (_extheader.ubordermode != Ptex::m_clamp || _extheader.vbordermode != Ptex::m_clamp) {
isConst = false;
}
// and traverse CCW neighbors too
if (isConst) {
aeid = (aeid - 1 + nedges) % nedges;
afid = f.adjface(aeid);
aeid = f.adjedge(aeid);
count = 0;
while (afid != faceid && afid >= 0 && ++count < maxcount) {
// check if neighbor is constant, and has the same value as face
FaceInfo& af = _faceinfo[afid];
if (!af.isConstant() ||
0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
{ isConst = false; break; }
// traverse around vertex in CCW direction
prevFid = afid;
aeid = (aeid - 1 + nedges) % nedges;
afid = af.adjface(aeid);
aeid = af.adjedge(aeid);
// if traversing to a subface, switch to secondary subface (afid points to primary/CW subface)
bool isSubface = af.isSubface();
if (isSubface && !prevWasSubface) {
aeid = (aeid + 3) % 4;
afid = af.adjface(aeid);
aeid = (af.adjedge(aeid) + 3) % 4;
}
prevWasSubface = isSubface;
}
}
}
}
if (isConst) f.flags |= FaceInfo::flag_nbconstant;
}
}
void PtexMainWriter::generateReductions()
{
// first generate "rfaceids", reduction faceids,
// which are faceids reordered by decreasing smaller dimension
int nfaces = _header.nfaces;
_rfaceids.resize(nfaces);
_faceids_r.resize(nfaces);
PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
// determine how many faces in each level, and resize _levels
// traverse in reverse rfaceid order to find number of faces
// larger than cutoff size of each level
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) {
// i == last face for current level
int size = rfaceid+1;
_levels.push_back(LevelRec());
LevelRec& level = _levels.back();
level.pos.resize(size);
level.fdh.resize(size);
cutoffres++;
}
}
// generate and cache reductions (including const data)
// first, find largest face and allocate tmp buffer
int buffsize = 0;
for (int i = 0; i < nfaces; i++)
buffsize = PtexUtils::max(buffsize, _faceinfo[i].res.size());
buffsize *= _pixelSize;
char* buff = new char [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++) {
// output current reduction for face (previously generated)
int faceid = _faceids_r[rfaceid];
Res res = _faceinfo[faceid].res;
res.ulog2 = (int8_t)(res.ulog2 - i);
res.vlog2 = (int8_t)(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;
// write a new reduction if needed for next level
if (rfaceid < nextsize) {
fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
writeReduction(_tmpfp, buff, stride, res);
}
else {
// the last reduction for each face is its constant value
storeConstValue(faceid, buff, stride, res);
}
}
}
fseeko(_tmpfp, 0, SEEK_END);
delete [] buff;
}
void PtexMainWriter::writeMetaData(FILE* fp)
{
std::vector<MetaEntry*> lmdEntries; // large meta data items
// write small meta data items in a single zip block
for (int i = 0, n = (int)_metadata.size(); i < n; i++) {
MetaEntry& e = _metadata[i];
#ifndef PTEX_NO_LARGE_METADATA_BLOCKS
if (int(e.data.size()) > MetaDataThreshold) {
// skip large items, but record for later
lmdEntries.push_back(&e);
}
else
#endif
{
// add small item to zip block
_header.metadatamemsize += writeMetaDataBlock(fp, e);
}
}
if (_header.metadatamemsize) {
// finish zip block
_header.metadatazipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
}
// write compatibility barrier
writeBlank(fp, sizeof(uint64_t));
// write large items as separate blocks
int nLmd = (int)lmdEntries.size();
if (nLmd > 0) {
// write data records to tmp file and accumulate zip sizes for lmd header
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], (int)e->data.size());
}
// write lmd header records as single zip block
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 = (uint32_t)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 +=
(uint32_t)(sizeof(keysize) + (size_t)keysize + sizeof(datatype) +
sizeof(datasize) + sizeof(zipsize));
}
_extheader.lmdheaderzipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
// copy data records
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,
/* compress */ false),
_fp(fp)
{
// note: incremental saves are not compressed (see compress flag above)
// to improve save time in the case where in incremental save is followed by
// a full save (which ultimately it always should be). With a compressed
// incremental save, the data would be compressed twice and decompressed once
// on every save vs. just compressing once.
// make sure existing header matches
if (!fread(&_header, 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 == 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;
}
// read extended header
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;
}
// seek to end of file to append
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;
// handle constant case
if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
return writeConstantFace(faceid, f, data);
// init headers
uint8_t edittype = et_editfacedata;
uint32_t editsize;
EditFaceDataHeader efdh;
efdh.faceid = faceid;
// check and store face info
if (!storeFaceInfo(faceid, efdh.faceinfo, f))
return 0;
// record position and skip headers
FilePos pos = ftello(_fp);
writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(efdh));
// must compute constant (average) val first
uint8_t* constval = new uint8_t [_pixelSize];
if (_header.hasAlpha()) {
// must premult alpha before averaging
// first copy to temp buffer
int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
uint8_t* temp = new uint8_t [rowlen * nrows];
PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
// multiply alpha
PtexUtils::multalpha(temp, f.res.size(), datatype(), _header.nchannels,
_header.alphachan);
// average
PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
datatype(), _header.nchannels);
// unmult alpha
PtexUtils::divalpha(constval, 1, datatype(), _header.nchannels,
_header.alphachan);
delete [] temp;
}
else {
// average
PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
datatype(), _header.nchannels);
}
// write const val
writeBlock(_fp, constval, _pixelSize);
delete [] constval;
// write face data
writeFaceData(_fp, data, stride, f.res, efdh.fdh);
// update editsize in header
editsize = (uint32_t)(sizeof(efdh) + (size_t)_pixelSize + efdh.fdh.blocksize());
// rewind and write headers
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)
{
// init headers
uint8_t edittype = et_editfacedata;
uint32_t editsize;
EditFaceDataHeader efdh;
efdh.faceid = faceid;
efdh.fdh.set(0, enc_constant);
editsize = (uint32_t)sizeof(efdh) + _pixelSize;
// check and store face info
if (!storeFaceInfo(faceid, efdh.faceinfo, f, FaceInfo::flag_constant))
return 0;
// write headers
writeBlock(_fp, &edittype, sizeof(edittype));
writeBlock(_fp, &editsize, sizeof(editsize));
writeBlock(_fp, &efdh, sizeof(efdh));
// write data
writeBlock(_fp, data, _pixelSize);
return 1;
}
void PtexIncrWriter::writeMetaDataEdit()
{
// init headers
uint8_t edittype = et_editmetadata;
uint32_t editsize;
EditMetaDataHeader emdh;
emdh.metadatazipsize = 0;
emdh.metadatamemsize = 0;
// record position and skip headers
FilePos pos = ftello(_fp);
writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(emdh));
// write meta data
for (size_t i = 0, n = _metadata.size(); i < n; i++) {
MetaEntry& e = _metadata[i];
emdh.metadatamemsize += writeMetaDataBlock(_fp, e);
}
// finish zip block
emdh.metadatazipsize = writeZipBlock(_fp, 0, 0, /*finish*/ true);
// update headers
editsize = (uint32_t)(sizeof(emdh) + emdh.metadatazipsize);
// rewind and write headers
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)
{
// closing base writer will write all pending data via finish() method
bool result = PtexWriterBase::close(error);
if (_fp) {
fclose(_fp);
_fp = 0;
}
return result;
}
void PtexIncrWriter::finish()
{
// write meta data edit block (if any)
if (!_metadata.empty()) writeMetaDataEdit();
// rewrite extheader for updated editdatasize
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);
}
}
PTEX_NAMESPACE_END
Magic
const uint32_t Magic
Definition PtexIO.h:104
ExtHeaderSize
const int ExtHeaderSize
Definition PtexIO.h:106
AllocaMax
const int AllocaMax
Definition PtexIO.h:116
HeaderSize
const int HeaderSize
Definition PtexIO.h:105
LevelInfoSize
const int LevelInfoSize
Definition PtexIO.h:107
BlockSize
const int BlockSize
Definition PtexIO.h:114
LittleEndian
bool LittleEndian()
Definition PtexIO.h:119
et_editfacedata
@ et_editfacedata
Definition PtexIO.h:92
et_editmetadata
@ et_editmetadata
Definition PtexIO.h:92
TileSize
const int TileSize
Definition PtexIO.h:115
enc_diffzipped
@ enc_diffzipped
Definition PtexIO.h:81
enc_zipped
@ enc_zipped
Definition PtexIO.h:81
enc_constant
@ enc_constant
Definition PtexIO.h:81
enc_tiled
@ enc_tiled
Definition PtexIO.h:81
MetaDataThreshold
const int MetaDataThreshold
Definition PtexIO.h:117
PtexPlatform.h
Platform-specific classes, functions, and includes.
FilePos
off_t FilePos
Definition PtexPlatform.h:99
PtexUtils.h
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
PtexFileMinorVersion
#define PtexFileMinorVersion
Definition PtexVersion.h:41
PtexFileMajorVersion
#define PtexFileMajorVersion
Definition PtexVersion.h:40
PtexWriter.h
Ptexture.h
Public API classes for reading, writing, caching, and filtering Ptex files.
AutoLock
Automatically acquire and release lock within enclosing scope.
Definition PtexMutex.h:43
Mutex
Definition PtexPlatform.h:137
PtexIncrWriter
Definition PtexWriter.h:181
PtexIncrWriter::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:1395
PtexIncrWriter::PtexIncrWriter
PtexIncrWriter(const char *path, FILE *fp, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces)
Definition PtexWriter.cpp:1217
PtexIncrWriter::finish
virtual void finish()
Definition PtexWriter.cpp:1407
PtexIncrWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition PtexWriter.cpp:1338
PtexIncrWriter::_fp
FILE * _fp
Definition PtexWriter.h:197
PtexIncrWriter::~PtexIncrWriter
virtual ~PtexIncrWriter()
Definition PtexWriter.cpp:1265
PtexIncrWriter::writeFace
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition PtexWriter.cpp:1270
PtexIncrWriter::writeMetaDataEdit
void writeMetaDataEdit()
Definition PtexWriter.cpp:1362
PtexMainWriter
Definition PtexWriter.h:129
PtexMainWriter::generateReductions
void generateReductions()
Definition PtexWriter.cpp:1079
PtexMainWriter::_tmppath
std::string _tmppath
Definition PtexWriter.h:155
PtexMainWriter::_constdata
std::vector< uint8_t > _constdata
Definition PtexWriter.h:160
PtexMainWriter::_newpath
std::string _newpath
Definition PtexWriter.h:154
PtexMainWriter::_rfaceids
std::vector< uint32_t > _rfaceids
Definition PtexWriter.h:161
PtexMainWriter::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:780
PtexMainWriter::_genmipmaps
bool _genmipmaps
Definition PtexWriter.h:158
PtexMainWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition PtexWriter.cpp:863
PtexMainWriter::_reader
PtexReader * _reader
Definition PtexWriter.h:177
PtexMainWriter::_tmpfp
FILE * _tmpfp
Definition PtexWriter.h:156
PtexMainWriter::finish
virtual void finish()
Definition PtexWriter.cpp:890
PtexMainWriter::_faceids_r
std::vector< uint32_t > _faceids_r
Definition PtexWriter.h:162
PtexMainWriter::storeConstValue
void storeConstValue(int faceid, const void *data, int stride, Res res)
Definition PtexWriter.cpp:878
PtexMainWriter::_faceinfo
std::vector< FaceInfo > _faceinfo
Definition PtexWriter.h:159
PtexMainWriter::writeMetaData
void writeMetaData(FILE *fp)
Definition PtexWriter.cpp:1150
PtexMainWriter::~PtexMainWriter
virtual ~PtexMainWriter()
Definition PtexWriter.cpp:774
PtexMainWriter::_hasNewData
bool _hasNewData
Definition PtexWriter.h:157
PtexMainWriter::MinReductionLog2
static const int MinReductionLog2
Definition PtexWriter.h:164
PtexMainWriter::writeFace
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition PtexWriter.cpp:806
PtexMainWriter::PtexMainWriter
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
Definition PtexWriter.cpp:723
PtexMainWriter::_rpos
std::vector< FilePos > _rpos
Definition PtexWriter.h:175
PtexMainWriter::_levels
std::vector< LevelRec > _levels
Definition PtexWriter.h:174
PtexMainWriter::flagConstantNeighorhoods
void flagConstantNeighorhoods()
Definition PtexWriter.cpp:996
PtexMetaData
Meta data accessor.
Definition Ptexture.h:328
PtexMetaData::numKeys
virtual int numKeys()=0
Query number of meta data entries stored in file.
PtexMetaData::getValue
virtual void getValue(const char *key, const char *&value)=0
Query the value of a given meta data entry.
PtexMetaData::getKey
virtual void getKey(int index, const char *&key, Ptex::MetaDataType &type)=0
Query the name and type of a meta data entry.
PtexPtr
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1032
PtexReader
Definition PtexReader.h:52
PtexReader::release
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition PtexReader.h:56
PtexReader::getMetaData
virtual PtexMetaData * getMetaData()
Access meta data.
Definition PtexReader.cpp:335
PtexReader::getData
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
Definition PtexReader.cpp:711
PtexReader::getFaceInfo
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
Definition PtexReader.cpp:267
PtexReader::hasEdits
virtual bool hasEdits()
True if the file has edit blocks.
Definition PtexReader.h:98
PtexTexture
Interface for reading data from a ptex file.
Definition Ptexture.h:457
PtexTexture::meshType
virtual Ptex::MeshType meshType()=0
Type of mesh for which texture data is defined.
PtexTexture::uBorderMode
virtual Ptex::BorderMode uBorderMode()=0
Mode for filtering texture access beyond mesh border.
PtexTexture::hasMipMaps
virtual bool hasMipMaps()=0
True if the file has mipmaps.
PtexTexture::numFaces
virtual int numFaces()=0
Number of faces stored in file.
PtexTexture::dataType
virtual Ptex::DataType dataType()=0
Type of data stored in file.
PtexTexture::alphaChannel
virtual int alphaChannel()=0
Index of alpha channel (if any).
PtexTexture::edgeFilterMode
virtual Ptex::EdgeFilterMode edgeFilterMode()=0
Mode for filtering textures across edges.
PtexTexture::open
static PtexTexture * open(const char *path, Ptex::String &error, bool premultiply=0)
Open a ptex file for reading.
Definition PtexReader.cpp:59
PtexTexture::hasEdits
virtual bool hasEdits()=0
True if the file has edit blocks.
PtexTexture::vBorderMode
virtual Ptex::BorderMode vBorderMode()=0
Mode for filtering texture access beyond mesh border.
PtexTexture::numChannels
virtual int numChannels()=0
Number of channels stored in file.
PtexWriterBase
Definition PtexWriter.h:50
PtexWriterBase::_metamap
std::map< std::string, int > _metamap
Definition PtexWriter.h:122
PtexWriterBase::datatype
DataType datatype() const
Definition PtexWriter.h:80
PtexWriterBase::writeBlank
int writeBlank(FILE *fp, int size)
Definition PtexWriter.cpp:487
PtexWriterBase::setEdgeFilterMode
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
Definition PtexWriter.h:57
PtexWriterBase::_path
std::string _path
Definition PtexWriter.h:115
PtexWriterBase::copyBlock
int copyBlock(FILE *dst, FILE *src, FilePos pos, int size)
Definition PtexWriter.cpp:551
PtexWriterBase::setError
void setError(const std::string &error)
Definition PtexWriter.h:110
PtexWriterBase::writeZipBlock
int writeZipBlock(FILE *fp, const void *data, int size, bool finish=true)
Definition PtexWriter.cpp:510
PtexWriterBase::writeMeta
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
Definition PtexWriter.cpp:361
PtexWriterBase::_zstream
z_stream_s _zstream
Definition PtexWriter.h:123
PtexWriterBase::_ok
bool _ok
Definition PtexWriter.h:113
PtexWriterBase::finish
virtual void finish()=0
PtexWriterBase::addMetaData
virtual void addMetaData(const char *key, MetaDataType t, const void *value, int size)
Definition PtexWriter.cpp:453
PtexWriterBase::release
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition PtexWriter.cpp:301
PtexWriterBase::writeBlock
int writeBlock(FILE *fp, const void *data, int size)
Definition PtexWriter.cpp:499
PtexWriterBase::_tilefp
FILE * _tilefp
Definition PtexWriter.h:117
PtexWriterBase::_tilepath
std::string _tilepath
Definition PtexWriter.h:116
PtexWriterBase::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:316
PtexWriterBase::writeFaceData
void writeFaceData(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Definition PtexWriter.cpp:629
PtexWriterBase::readBlock
int readBlock(FILE *fp, void *data, int size)
Definition PtexWriter.cpp:541
PtexWriterBase::_metadata
std::vector< MetaEntry > _metadata
Definition PtexWriter.h:121
PtexWriterBase::writeConstFaceBlock
void writeConstFaceBlock(FILE *fp, const void *data, FaceDataHeader &fdh)
Definition PtexWriter.cpp:592
PtexWriterBase::writeMetaDataBlock
int writeMetaDataBlock(FILE *fp, MetaEntry &val)
Definition PtexWriter.cpp:707
PtexWriterBase::_extheader
ExtHeader _extheader
Definition PtexWriter.h:119
PtexWriterBase::_reduceFn
PtexUtils::ReduceFn * _reduceFn
Definition PtexWriter.h:125
PtexWriterBase::writeReduction
void writeReduction(FILE *fp, const void *data, int stride, Res res)
Definition PtexWriter.cpp:690
PtexWriterBase::storeFaceInfo
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
Definition PtexWriter.cpp:329
PtexWriterBase::setBorderModes
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
Definition PtexWriter.h:52
PtexWriterBase::~PtexWriterBase
virtual ~PtexWriterBase()
Definition PtexWriter.cpp:310
PtexWriterBase::getError
void getError(Ptex::String &error)
Definition PtexWriter.h:75
PtexWriterBase::PtexWriterBase
PtexWriterBase(const char *path, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool compress)
Definition PtexWriter.cpp:261
PtexWriterBase::_header
Header _header
Definition PtexWriter.h:118
PtexWriterBase::_pixelSize
int _pixelSize
Definition PtexWriter.h:120
PtexWriterBase::ok
bool ok(Ptex::String &error)
Definition PtexWriter.h:71
PtexWriterBase::writeFaceBlock
void writeFaceBlock(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Definition PtexWriter.cpp:602
PtexWriterBase::calcTileRes
Res calcTileRes(Res faceres)
Definition PtexWriter.cpp:570
PtexWriter
Interface for writing data to a ptex file.
Definition Ptexture.h:810
PtexWriter::edit
static PtexWriter * edit(const char *path, bool incremental, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, Ptex::String &error, bool genmipmaps=true)
Open an existing texture file for writing.
Definition PtexWriter.cpp:186
PtexWriter::applyEdits
static bool applyEdits(const char *path, Ptex::String &error)
Apply edits to a file.
Definition PtexWriter.cpp:242
PtexWriter::open
static PtexWriter * open(const char *path, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, Ptex::String &error, bool genmipmaps=true)
Open a new texture file for writing.
Definition PtexWriter.cpp:167
Ptex::String
Memory-managed string.
Definition Ptexture.h:296
Ptex::String::c_str
const char * c_str() const
Definition Ptexture.h:304
PTEX_NAMESPACE_BEGIN
Definition PtexSeparableKernel.cpp:42
PTEX_NAMESPACE_BEGIN::checkFormat
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
Definition PtexWriter.cpp:133
PTEX_NAMESPACE_BEGIN::OpenTempFile
FILE * OpenTempFile(std::string &tmppath)
Definition PtexWriter.cpp:78
PTEX_NAMESPACE_BEGIN::fileError
std::string fileError(const char *message, const char *path)
Definition PtexWriter.cpp:126
PtexUtils::genRfaceids
void genRfaceids(const FaceInfo *faces, int nfaces, uint32_t *rfaceids, uint32_t *faceids)
Definition PtexUtils.cpp:630
PtexUtils::isConstant
bool isConstant(const void *data, int stride, int ures, int vres, int pixelSize)
Definition PtexUtils.cpp:147
PtexUtils::min
T min(T a, T b)
Definition PtexUtils.h:148
PtexUtils::divalpha
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition PtexUtils.cpp:618
PtexUtils::encodeDifference
void encodeDifference(void *data, int size, DataType dt)
Definition PtexUtils.cpp:251
PtexUtils::reduce
void reduce(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:299
PtexUtils::deinterleave
void deinterleave(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:226
PtexUtils::max
T max(T a, T b)
Definition PtexUtils.h:151
PtexUtils::reduceTri
void reduceTri(const void *src, int sstride, int w, int, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:406
PtexUtils::copy
void copy(const void *src, int sstride, void *dst, int dstride, int vres, int rowlen)
Definition PtexUtils.cpp:438
PtexUtils::floor_log2
uint32_t floor_log2(uint32_t x)
Definition PtexUtils.h:69
PtexUtils::multalpha
void multalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition PtexUtils.cpp:579
PtexUtils::average
void average(const void *src, int sstride, int uw, int vw, void *dst, DataType dt, int nchan)
Definition PtexUtils.cpp:522
Ptex::DataType
DataType
Type of data stored in texture file.
Definition Ptexture.h:72
Ptex::dt_float
@ dt_float
Single-precision (32-bit) floating point.
Definition Ptexture.h:76
Ptex::MeshType
MeshType
Type of base mesh for which the textures are defined.
Definition Ptexture.h:66
Ptex::mt_quad
@ mt_quad
Mesh is quad-based.
Definition Ptexture.h:68
Ptex::m_clamp
@ m_clamp
texel access is clamped to border
Definition Ptexture.h:87
EditFaceDataHeader
Definition PtexIO.h:93
EditFaceDataHeader::faceid
uint32_t faceid
Definition PtexIO.h:94
EditFaceDataHeader::fdh
FaceDataHeader fdh
Definition PtexIO.h:96
EditFaceDataHeader::faceinfo
FaceInfo faceinfo
Definition PtexIO.h:95
EditMetaDataHeader
Definition PtexIO.h:98
EditMetaDataHeader::metadatazipsize
uint32_t metadatazipsize
Definition PtexIO.h:99
EditMetaDataHeader::metadatamemsize
uint32_t metadatamemsize
Definition PtexIO.h:100
ExtHeader::vbordermode
uint16_t vbordermode
Definition PtexIO.h:67
ExtHeader::ubordermode
uint16_t ubordermode
Definition PtexIO.h:65
ExtHeader::lmddatasize
uint64_t lmddatasize
Definition PtexIO.h:71
ExtHeader::editdatapos
uint64_t editdatapos
Definition PtexIO.h:73
ExtHeader::lmdheadermemsize
uint32_t lmdheadermemsize
Definition PtexIO.h:70
ExtHeader::lmdheaderzipsize
uint32_t lmdheaderzipsize
Definition PtexIO.h:69
ExtHeader::editdatasize
uint64_t editdatasize
Definition PtexIO.h:72
FaceDataHeader
Definition PtexIO.h:82
FaceDataHeader::set
void set(uint32_t blocksizeArg, Encoding encodingArg)
Definition PtexIO.h:88
FaceDataHeader::blocksize
uint32_t blocksize() const
Definition PtexIO.h:84
Header::metadatamemsize
uint32_t metadatamemsize
Definition PtexIO.h:60
Header::faceinfosize
uint32_t faceinfosize
Definition PtexIO.h:54
Header::nlevels
uint16_t nlevels
Definition PtexIO.h:51
Header::nchannels
uint16_t nchannels
Definition PtexIO.h:50
Header::meshtype
uint32_t meshtype
Definition PtexIO.h:47
Header::constdatasize
uint32_t constdatasize
Definition PtexIO.h:55
Header::levelinfosize
uint32_t levelinfosize
Definition PtexIO.h:56
Header::extheadersize
uint32_t extheadersize
Definition PtexIO.h:53
Header::minorversion
uint32_t minorversion
Definition PtexIO.h:57
Header::metadatazipsize
uint32_t metadatazipsize
Definition PtexIO.h:59
Header::datatype
uint32_t datatype
Definition PtexIO.h:48
Header::pixelSize
int pixelSize() const
Definition PtexIO.h:61
Header::alphachan
int32_t alphachan
Definition PtexIO.h:49
Header::magic
uint32_t magic
Definition PtexIO.h:45
Header::nfaces
uint32_t nfaces
Definition PtexIO.h:52
Header::leveldatasize
uint64_t leveldatasize
Definition PtexIO.h:58
Header::version
uint32_t version
Definition PtexIO.h:46
Header::hasAlpha
bool hasAlpha() const
Definition PtexIO.h:62
LevelInfo
Definition PtexIO.h:75
LevelInfo::levelheadersize
uint32_t levelheadersize
Definition PtexIO.h:77
LevelInfo::leveldatasize
uint64_t leveldatasize
Definition PtexIO.h:76
LevelInfo::nfaces
uint32_t nfaces
Definition PtexIO.h:78
PtexMainWriter::LevelRec
Definition PtexWriter.h:165
PtexMainWriter::LevelRec::pos
std::vector< FilePos > pos
Definition PtexWriter.h:171
PtexMainWriter::LevelRec::fdh
std::vector< FaceDataHeader > fdh
Definition PtexWriter.h:172
PtexWriterBase::MetaEntry
Definition PtexWriter.h:82
PtexWriterBase::MetaEntry::key
std::string key
Definition PtexWriter.h:83
PtexWriterBase::MetaEntry::datatype
MetaDataType datatype
Definition PtexWriter.h:84
PtexWriterBase::MetaEntry::data
std::vector< uint8_t > data
Definition PtexWriter.h:85
Ptex::FaceInfo
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
Ptex::FaceInfo::res
Res res
Resolution of face.
Definition Ptexture.h:230
Ptex::FaceInfo::isConstant
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition Ptexture.h:262
Ptex::Res
Pixel resolution of a given texture.
Definition Ptexture.h:159
Ptex::Res::size
int size() const
Total size of specified texture in texels (u * v).
Definition Ptexture.h:182
Ptex::Res::u
int u() const
U resolution in texels.
Definition Ptexture.h:173