47#include <libdeflate.h>
57 std::string
fileError(
const char* message,
const char* path)
59 std::stringstream str;
60 str << message << path <<
"\n" << strerror(errno);
69 error =
"PtexWriter doesn't currently support big-endian cpu's";
74 error =
"PtexWriter error: Invalid mesh type";
79 error =
"PtexWriter error: Invalid data type";
84 error =
"PtexWriter error: Invalid number of channels";
88 if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
89 error =
"PtexWriter error: Invalid alpha channel";
100 int nchannels,
int alphachan,
int nfaces,
103 if (!checkFormat(mt, dt, nchannels, alphachan, error))
107 mt, dt, nchannels, alphachan, nfaces,
119 int nchannels,
int alphachan,
int nfaces,
122 if (!checkFormat(mt, dt, nchannels, alphachan, error))
126 FILE* fp = fopen(path,
"rb+");
127 if (!fp && errno != ENOENT) {
128 error = fileError(
"Can't open ptex file for update: ", path).c_str();
141 bool headerMatch = (mt == tex->
meshType() &&
147 std::stringstream str;
148 str <<
"PtexWriter::edit error: header doesn't match existing file, "
149 <<
"conversions not supported";
150 error = str.str().c_str();
166 int nchannels,
int alphachan,
int nfaces,
170 return edit(path, mt, dt, nchannels, alphachan, nfaces, error, genmipmaps);
185 std::cerr << error.
c_str() << std::endl;
192 if (faceid < 0 ||
size_t(faceid) >=
_header.nfaces) {
193 setError(
"PtexWriter error: faceid out of range");
197 if (
_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
198 setError(
"PtexWriter error: asymmetric face res not supported for triangle textures");
206 if (
_header.meshtype == mt_triangle) {
213 f.flags &= FaceInfo::flag_subface;
217 f.flags |= (uint8_t)flags;
224 addMetaData(key, mdt_string, value,
int(strlen(value)+1));
236 addMetaData(key, mdt_int16, value, count*(
int)
sizeof(int16_t));
242 addMetaData(key, mdt_int32, value, count*(
int)
sizeof(int32_t));
248 addMetaData(key, mdt_float, value, count*(
int)
sizeof(
float));
254 addMetaData(key, mdt_double, value, count*(
int)
sizeof(
double));
261 for (
int i = 0; i < nkeys; i++) {
264 data->
getKey(i, key, type);
283 const int16_t* val=0;
290 const int32_t* val=0;
315 const void* value,
int size)
317 if (strlen(key) > 255) {
318 std::stringstream str;
319 str <<
"PtexWriter error: meta data key too long (max=255) \"" << key <<
"\"";
324 std::stringstream str;
325 str <<
"PtexWriter error: meta data size <= 0 for \"" << key <<
"\"";
328 std::map<std::string,int>::iterator iter =
_metamap.find(key);
332 index = iter->second;
344 memcpy(&m.
data[0], value, size);
351 if (!fwrite(data, size, 1, fp)) {
352 setError(
"PtexWriter error: file write failed");
361 size_t previousSize = dataBlock.size();
362 dataBlock.resize(previousSize+size);
363 memcpy(dataBlock.data() + previousSize, data, size);
370 libdeflate_compressor* compressor;
375 const int compressionLevel = 4;
376 compressor = libdeflate_alloc_compressor(compressionLevel);
391 compressedData.resize(libdeflate_zlib_compress_bound(compressor, size));
392 int compressedSize = int(libdeflate_zlib_compress(compressor, data, size, compressedData.data(), compressedData.size()));
393 if (!compressedSize) {
394 setError(
"PtexWriter error: compression failed");
396 compressedData.resize(compressedSize);
405 if (ntileslog2 == 0)
return faceres;
411 int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
416 tileres.ulog2 = (int8_t)std::min(
int((n+1)/2),
int(faceres.ulog2));
417 tileres.vlog2 = (int8_t)std::min(
int(n - tileres.ulog2),
int(faceres.vlog2));
423 Res res,
const void* uncompressedData,
int stride)
428 int ures = res.u(), vres = res.v();
430 std::vector<std::byte> temp(tempSize);
436 bool diff = (
datatype() == dt_uint8 ||
449 Res res,
const void* uncompressedData)
454 size_t ntilesu = res.ntilesu(tileRes);
455 size_t ntilesv = res.ntilesv(tileRes);
456 size_t ntiles = ntilesu * ntilesv;
462 std::vector<std::vector<std::byte>> tiles(ntiles);
463 std::vector<FaceDataHeader> tileHeader(ntiles);
464 size_t tileures = tileRes.u();
465 size_t tilevres = tileRes.v();
467 size_t tilevstride = tilevres*stride;
470 std::vector<std::byte>* tile = tiles.data();
472 const std::byte* rowp =
reinterpret_cast<const std::byte*
>(uncompressedData);
473 const std::byte* rowpend = rowp + ntilesv * tilevstride;
474 for (; rowp != rowpend; rowp += tilevstride) {
475 const std::byte* p = rowp;
476 const std::byte* pend = p + ntilesu * tileustride;
477 for (; p != pend; tile++, tdh++, p += tileustride) {
491 std::vector<std::byte> compressedTileHeader;
492 compressDataBlock(compressor, compressedTileHeader,
reinterpret_cast<std::byte*
>(tileHeader.data()),
494 uint32_t compressedTileHeaderSize = compressedTileHeader.size();
496 size_t totalSize =
sizeof(tileRes) +
sizeof(compressedTileHeaderSize) + compressedTileHeaderSize;
497 for (
auto& tile : tiles) {
498 totalSize += tile.size();
500 compressedData.reserve(totalSize);
502 addToDataBlock(compressedData, &compressedTileHeaderSize,
sizeof(compressedTileHeaderSize));
503 addToDataBlock(compressedData, compressedTileHeader.data(), compressedTileHeaderSize);
504 for (
auto& tile : tiles) {
515 uint8_t keysize = uint8_t(val.
key.size()+1);
517 uint32_t datasize = uint32_t(val.
data.size());
528 int nchannels,
int alphachan,
int nfaces,
bool genmipmaps)
541 _header.nchannels = (uint16_t)nchannels;
548 if (mt == mt_triangle)
558 for (
int i = 0; i < nfaces; i++)
_faceinfo[i].flags = uint8_t(-1);
582 for (libdeflate_compressor* compressor :
_compressors) {
583 libdeflate_free_compressor(compressor);
601 unlink(
_path.c_str());
603 setError(fileError(
"Can't write to ptex file: ",
_path.c_str()).c_str());
613 if (!
_ok)
return false;
623 if (stride == 0) stride = f.res.u()*
_pixelSize;
638 nlevels += std::max(0, std::min(f.res.ulog2, f.res.vlog2) -
MinReductionLog2);
641 face.
fdh.resize(nlevels);
646 face.
faceData[0].resize(rowlen * nrows);
652 std::vector<std::byte> premultData;
658 data = premultData.data();
663 for (
int level = 1; level < nlevels; level++) {
678 premultData.shrink_to_fit();
681 for (
int level = 0; level < nlevels; level++) {
682 Ptex::Res res((int8_t)(f.res.ulog2-level), (int8_t)(f.res.vlog2-level));
683 std::vector<std::byte> compressedData;
685 face.
faceData[level] = std::move(compressedData);
721 uint32_t nfaces =
_header.nfaces;
724 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
725 if (
_faceinfo[faceid].flags == uint8_t(-1)) {
736 char* data =
new char [size];
737 _reader->getData(faceid, data, 0);
748 face.
fdh.resize(nlevels);
750 for (
int level = 0; level < nlevels; level++) {
759 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
760 if (
_faceinfo[faceid].flags == uint8_t(-1))
761 _faceinfo[faceid].flags = FaceInfo::flag_constant;
776 std::vector<std::byte> compressedFaceInfo;
781 std::vector<std::byte> compressedConstData;
785 std::vector<LevelInfo> levelInfo(1);
786 for (
auto& face :
_faces) {
787 size_t nlevelsThisFace = face.faceData.size();
788 if (nlevelsThisFace > levelInfo.size()) {
789 levelInfo.resize(nlevelsThisFace);
791 for (
size_t level = 0; level < nlevelsThisFace; level++) {
792 levelInfo[level].leveldatasize += face.faceData[level].size();
793 levelInfo[level].nfaces++;
796 levelInfo[0].nfaces =
_header.nfaces;
798 int nlevels = int(levelInfo.size());
801 std::vector<std::vector<std::byte>> compressedLevelDataHeaders(nlevels);
802 std::vector<std::vector<size_t>> largeFaceHeaders(nlevels);
803 size_t totalLevelDataSize = 0;
804 for (
int level = 0; level < nlevels; level++) {
805 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
806 std::vector<FaceDataHeader> levelDataHeader(nfacesThisLevel);
807 for (uint32_t f = 0; f < nfacesThisLevel; f++) {
808 uint32_t faceId = level==0? f :
_faceids_r[f];
809 if (
_faces[faceId].fdh.size() >
size_t(level)) {
810 levelDataHeader[f] =
_faces[faceId].fdh[level];
811 if (levelDataHeader[f].isLargeFace()) {
813 largeFaceHeaders[level].push_back(
_faces[faceId].faceData[level].size());
818 levelInfo[level].levelheadersize = uint32_t(compressedLevelDataHeaders[level].size());
819 levelInfo[level].leveldatasize += levelInfo[level].levelheadersize +
sizeof(size_t) * largeFaceHeaders[level].size();
820 totalLevelDataSize += levelInfo[level].leveldatasize;
824 std::vector<MetaEntry*> lmdEntries;
825 std::vector<std::byte> metaData, compressedMetaData;
826 for (
size_t i = 0, n =
_metadata.size(); i < n; i++) {
830 lmdEntries.push_back(&e);
836 if (!metaData.empty()) {
837 compressDataBlock(compressor, compressedMetaData, metaData.data(), metaData.size());
841 size_t nLmd = lmdEntries.size();
842 std::vector<std::byte> lmdHeader, compressedLmdHeader;
843 std::vector<std::vector<std::byte>> compressedLargeMetaData(nLmd);
846 for (
size_t i = 0; i < nLmd; i++) {
850 uint8_t keysize = uint8_t(e->
key.size()+1);
852 uint32_t datasize = (uint32_t)e->
data.size();
853 uint32_t zipsize = (uint32_t)compressedLargeMetaData[i].size();
861 compressDataBlock(compressor, compressedLmdHeader, lmdHeader.data(), lmdHeader.size());
864 compressor =
nullptr;
868 _header.faceinfosize = compressedFaceInfo.size();
869 _header.constdatasize = compressedConstData.size();
871 _header.leveldatasize = totalLevelDataSize;
872 _header.metadatamemsize = uint32_t(metaData.size());
873 _header.metadatazipsize = uint32_t(compressedMetaData.size());
874 _extheader.lmdheadermemsize = lmdHeader.size();
875 _extheader.lmdheaderzipsize = compressedLmdHeader.size();
878 FILE* newfp = fopen(
_newpath.c_str(),
"wb+");
889 writeBlock(newfp, compressedFaceInfo.data(), compressedFaceInfo.size());
892 writeBlock(newfp, compressedConstData.data(), compressedConstData.size());
898 for (
int level = 0; level < nlevels; level++) {
900 writeBlock(newfp, compressedLevelDataHeaders[level].data(), compressedLevelDataHeaders[level].size());
903 if (!largeFaceHeaders[level].empty()) {
904 writeBlock(newfp, largeFaceHeaders[level].data(),
sizeof(
size_t) * largeFaceHeaders[level].size());
908 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
909 for (uint32_t rfaceId = 0; rfaceId < nfacesThisLevel; rfaceId++) {
910 uint32_t faceId = level==0? rfaceId :
_faceids_r[rfaceId];
911 if (
_faces[faceId].faceData.size() >
size_t(level)) {
918 if (!compressedMetaData.empty()) {
919 writeBlock(newfp, compressedMetaData.data(), compressedMetaData.size());
923 uint64_t compatibilityBarrier = 0;
924 writeBlock(newfp, &compatibilityBarrier,
sizeof(compatibilityBarrier));
927 if (!compressedLmdHeader.empty()) {
928 writeBlock(newfp, compressedLmdHeader.data(), compressedLmdHeader.size());
929 for (
auto& lmd : compressedLargeMetaData) {
940 for (
int faceid = 0, n =
int(
_faceinfo.size()); faceid < n; faceid++) {
942 if (!f.isConstant())
continue;
947 bool isTriangle =
_header.meshtype == mt_triangle;
948 int nedges = isTriangle ? 3 : 4;
949 for (
int eid = 0; isConst && (eid < nedges); eid++) {
950 bool prevWasSubface = f.isSubface();
951 int prevFid = faceid;
954 int afid = f.adjface(eid);
955 int aeid = f.adjedge(eid);
957 const int maxcount = 10;
958 while (afid != faceid && afid >= 0 && ++count < maxcount) {
961 if (!af.isConstant() ||
963 { isConst =
false;
break; }
966 bool isSubface = af.isSubface();
967 bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
969 prevWasSubface = isSubface;
973 aeid = (aeid + 1) % nedges;
974 afid = af.adjface(aeid);
975 aeid = af.adjedge(aeid);
986 aeid = (aeid - 1 + nedges) % nedges;
987 afid = f.adjface(aeid);
988 aeid = f.adjedge(aeid);
990 while (afid != faceid && afid >= 0 && ++count < maxcount) {
993 if (!af.isConstant() ||
995 { isConst =
false;
break; }
999 aeid = (aeid - 1 + nedges) % nedges;
1000 afid = af.adjface(aeid);
1001 aeid = af.adjedge(aeid);
1004 bool isSubface = af.isSubface();
1005 if (isSubface && !prevWasSubface) {
1006 aeid = (aeid + 3) % 4;
1007 afid = af.adjface(aeid);
1008 aeid = (af.adjedge(aeid) + 3) % 4;
1010 prevWasSubface = isSubface;
1015 if (isConst) f.flags |= FaceInfo::flag_nbconstant;
const int MetaDataThreshold
AutoLock< Mutex > AutoMutex
#define PTEX_NAMESPACE_END
#define PtexFileMinorVersion
#define PtexFileMajorVersion
Public API classes for reading, writing, caching, and filtering Ptex files.
Res calcTileRes(Res faceres)
void releaseCompressor(libdeflate_compressor *compressor)
void getError(Ptex::String &error)
void compressFaceDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData, int stride)
std::vector< libdeflate_compressor * > _compressors
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
void addToDataBlock(std::vector< std::byte > &dataBlock, const void *data, size_t size)
std::vector< uint32_t > _rfaceids
size_t writeBlock(FILE *fp, const void *data, size_t size)
std::map< std::string, int > _metamap
virtual bool close(Ptex::String &error)
Close the file.
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
bool ok(Ptex::String &error)
std::vector< uint32_t > _faceids_r
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
void compressDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, const void *data, size_t size)
void setError(const std::string &error)
void storeConstValue(int faceid, const void *data, int stride, Res res)
std::vector< FaceInfo > _faceinfo
void addMetaData(const char *key, MetaDataType t, const void *value, int size)
void compressFaceData(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData)
virtual ~PtexMainWriter()
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
std::vector< MetaEntry > _metadata
PtexUtils::ReduceFn * _reduceFn
static const int MinReductionLog2
void addToMetaDataBlock(std::vector< std::byte > &dataBlock, const MetaEntry &val)
DataType datatype() const
std::vector< FaceRec > _faces
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
std::vector< std::byte > _constdata
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
void flagConstantNeighorhoods()
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
libdeflate_compressor * getCompressor()
Smart-pointer for acquiring and releasing API objects.
Interface for reading data from a ptex file.
virtual Ptex::MeshType meshType()=0
Type of mesh for which texture data is defined.
virtual Ptex::BorderMode uBorderMode()=0
Mode for filtering texture access beyond mesh border.
virtual int numFaces()=0
Number of faces stored in file.
virtual Ptex::DataType dataType()=0
Type of data stored in file.
virtual int alphaChannel()=0
Index of alpha channel (if any).
virtual Ptex::EdgeFilterMode edgeFilterMode()=0
Mode for filtering textures across edges.
static PtexTexture * open(const char *path, Ptex::String &error, bool premultiply=0)
Open a ptex file for reading.
virtual Ptex::BorderMode vBorderMode()=0
Mode for filtering texture access beyond mesh border.
virtual int numChannels()=0
Number of channels stored in file.
Interface for writing data to a ptex file.
static bool applyEdits(const char *path, Ptex::String &error)
Obsolete (returns true).
static PtexWriter * edit(const char *path, 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.
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.
const char * c_str() const
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
std::string fileError(const char *message, const char *path)
void genRfaceids(const FaceInfo *faces, int nfaces, uint32_t *rfaceids, uint32_t *faceids)
bool isConstant(const void *data, int stride, int ures, int vres, int pixelSize)
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
void reduce(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
void deinterleave(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
void encodeDifference(void *data, size_t size, DataType dt)
void reduceTri(const void *src, int sstride, int w, int, void *dst, int dstride, DataType dt, int nchan)
void copy(const void *src, int sstride, void *dst, int dstride, int vres, int rowlen)
uint32_t floor_log2(uint32_t x)
void multalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
void average(const void *src, int sstride, int uw, int vw, void *dst, DataType dt, int nchan)
DataType
Type of data stored in texture file.
@ dt_float
Single-precision (32-bit) floating point.
MeshType
Type of base mesh for which the textures are defined.
@ mt_quad
Mesh is quad-based.
@ m_clamp
texel access is clamped to border
std::vector< std::vector< std::byte > > faceData
std::vector< FaceDataHeader > fdh
std::vector< uint8_t > data
Information about a face, as stored in the Ptex file header.
Res res
Resolution of face.
bool isConstant() const
Determine if face is constant (by checking a flag).
Pixel resolution of a given texture.
int8_t ulog2
log base 2 of u resolution, in texels
int v() const
V resolution in texels.
size_t size64() const
Total size of specified texture in texels (u * v), allowing arbitrarily large textures.
int u() const
U resolution in texels.
int8_t vlog2
log base 2 of v resolution, in texels