45#if defined(__FreeBSD__)
49#include <libdeflate.h>
59 std::string
fileError(
const char* message,
const char* path)
61 std::stringstream str;
62 str << message << path <<
"\n" << strerror(errno);
71 error =
"PtexWriter doesn't currently support big-endian cpu's";
76 error =
"PtexWriter error: Invalid mesh type";
81 error =
"PtexWriter error: Invalid data type";
86 error =
"PtexWriter error: Invalid number of channels";
90 if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
91 error =
"PtexWriter error: Invalid alpha channel";
102 int nchannels,
int alphachan,
int nfaces,
105 if (!checkFormat(mt, dt, nchannels, alphachan, error))
109 mt, dt, nchannels, alphachan, nfaces,
121 int nchannels,
int alphachan,
int nfaces,
124 if (!checkFormat(mt, dt, nchannels, alphachan, error))
128 FILE* fp = fopen(path,
"rb+");
129 if (!fp && errno != ENOENT) {
130 error = fileError(
"Can't open ptex file for update: ", path).c_str();
143 bool headerMatch = (mt == tex->
meshType() &&
149 std::stringstream str;
150 str <<
"PtexWriter::edit error: header doesn't match existing file, "
151 <<
"conversions not supported";
152 error = str.str().c_str();
168 int nchannels,
int alphachan,
int nfaces,
172 return edit(path, mt, dt, nchannels, alphachan, nfaces, error, genmipmaps);
187 std::cerr << error.
c_str() << std::endl;
194 if (faceid < 0 ||
size_t(faceid) >=
_header.nfaces) {
195 setError(
"PtexWriter error: faceid out of range");
199 if (
_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
200 setError(
"PtexWriter error: asymmetric face res not supported for triangle textures");
208 if (
_header.meshtype == mt_triangle) {
215 f.flags &= FaceInfo::flag_subface;
219 f.flags |= (uint8_t)flags;
226 addMetaData(key, mdt_string, value,
int(strlen(value)+1));
238 addMetaData(key, mdt_int16, value, count*(
int)
sizeof(int16_t));
244 addMetaData(key, mdt_int32, value, count*(
int)
sizeof(int32_t));
250 addMetaData(key, mdt_float, value, count*(
int)
sizeof(
float));
256 addMetaData(key, mdt_double, value, count*(
int)
sizeof(
double));
263 for (
int i = 0; i < nkeys; i++) {
266 data->
getKey(i, key, type);
285 const int16_t* val=0;
292 const int32_t* val=0;
317 const void* value,
int size)
319 if (strlen(key) > 255) {
320 std::stringstream str;
321 str <<
"PtexWriter error: meta data key too long (max=255) \"" << key <<
"\"";
326 std::stringstream str;
327 str <<
"PtexWriter error: meta data size <= 0 for \"" << key <<
"\"";
330 std::map<std::string,int>::iterator iter =
_metamap.find(key);
334 index = iter->second;
346 memcpy(&m.
data[0], value, size);
353 if (!fwrite(data, size, 1, fp)) {
354 setError(
"PtexWriter error: file write failed");
363 size_t previousSize = dataBlock.size();
364 dataBlock.resize(previousSize+size);
365 memcpy(dataBlock.data() + previousSize, data, size);
372 libdeflate_compressor* compressor;
377 const int compressionLevel = 4;
378 compressor = libdeflate_alloc_compressor(compressionLevel);
393 compressedData.resize(libdeflate_zlib_compress_bound(compressor, size));
394 int compressedSize = int(libdeflate_zlib_compress(compressor, data, size, compressedData.data(), compressedData.size()));
395 if (!compressedSize) {
396 setError(
"PtexWriter error: compression failed");
398 compressedData.resize(compressedSize);
407 if (ntileslog2 == 0)
return faceres;
413 int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
418 tileres.ulog2 = (int8_t)std::min(
int((n+1)/2),
int(faceres.ulog2));
419 tileres.vlog2 = (int8_t)std::min(
int(n - tileres.ulog2),
int(faceres.vlog2));
425 Res res,
const void* uncompressedData,
int stride)
430 int ures = res.u(), vres = res.v();
432 std::vector<std::byte> temp(tempSize);
438 bool diff = (
datatype() == dt_uint8 ||
451 Res res,
const void* uncompressedData)
456 size_t ntilesu = res.ntilesu(tileRes);
457 size_t ntilesv = res.ntilesv(tileRes);
458 size_t ntiles = ntilesu * ntilesv;
464 std::vector<std::vector<std::byte>> tiles(ntiles);
465 std::vector<FaceDataHeader> tileHeader(ntiles);
466 size_t tileures = tileRes.u();
467 size_t tilevres = tileRes.v();
469 size_t tilevstride = tilevres*stride;
472 std::vector<std::byte>* tile = tiles.data();
474 const std::byte* rowp =
reinterpret_cast<const std::byte*
>(uncompressedData);
475 const std::byte* rowpend = rowp + ntilesv * tilevstride;
476 for (; rowp != rowpend; rowp += tilevstride) {
477 const std::byte* p = rowp;
478 const std::byte* pend = p + ntilesu * tileustride;
479 for (; p != pend; tile++, tdh++, p += tileustride) {
493 std::vector<std::byte> compressedTileHeader;
494 compressDataBlock(compressor, compressedTileHeader,
reinterpret_cast<std::byte*
>(tileHeader.data()),
496 uint32_t compressedTileHeaderSize = compressedTileHeader.size();
498 size_t totalSize =
sizeof(tileRes) +
sizeof(compressedTileHeaderSize) + compressedTileHeaderSize;
499 for (
auto& tile : tiles) {
500 totalSize += tile.size();
502 compressedData.reserve(totalSize);
504 addToDataBlock(compressedData, &compressedTileHeaderSize,
sizeof(compressedTileHeaderSize));
505 addToDataBlock(compressedData, compressedTileHeader.data(), compressedTileHeaderSize);
506 for (
auto& tile : tiles) {
517 uint8_t keysize = uint8_t(val.
key.size()+1);
519 uint32_t datasize = uint32_t(val.
data.size());
530 int nchannels,
int alphachan,
int nfaces,
bool genmipmaps)
543 _header.nchannels = (uint16_t)nchannels;
550 if (mt == mt_triangle)
560 for (
int i = 0; i < nfaces; i++)
_faceinfo[i].flags = uint8_t(-1);
584 for (libdeflate_compressor* compressor :
_compressors) {
585 libdeflate_free_compressor(compressor);
603 unlink(
_path.c_str());
605 setError(fileError(
"Can't write to ptex file: ",
_path.c_str()).c_str());
615 if (!
_ok)
return false;
625 if (stride == 0) stride = f.res.u()*
_pixelSize;
640 nlevels += std::max(0, std::min(f.res.ulog2, f.res.vlog2) -
MinReductionLog2);
643 face.
fdh.resize(nlevels);
648 face.
faceData[0].resize(rowlen * nrows);
654 std::vector<std::byte> premultData;
660 data = premultData.data();
665 for (
int level = 1; level < nlevels; level++) {
680 premultData.shrink_to_fit();
683 for (
int level = 0; level < nlevels; level++) {
684 Ptex::Res res((int8_t)(f.res.ulog2-level), (int8_t)(f.res.vlog2-level));
685 std::vector<std::byte> compressedData;
687 face.
faceData[level] = std::move(compressedData);
723 uint32_t nfaces =
_header.nfaces;
726 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
727 if (
_faceinfo[faceid].flags == uint8_t(-1)) {
738 char* data =
new char [size];
739 _reader->getData(faceid, data, 0);
750 face.
fdh.resize(nlevels);
752 for (
int level = 0; level < nlevels; level++) {
761 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
762 if (
_faceinfo[faceid].flags == uint8_t(-1))
763 _faceinfo[faceid].flags = FaceInfo::flag_constant;
778 std::vector<std::byte> compressedFaceInfo;
783 std::vector<std::byte> compressedConstData;
787 std::vector<LevelInfo> levelInfo(1);
788 for (
auto& face :
_faces) {
789 size_t nlevelsThisFace = face.faceData.size();
790 if (nlevelsThisFace > levelInfo.size()) {
791 levelInfo.resize(nlevelsThisFace);
793 for (
size_t level = 0; level < nlevelsThisFace; level++) {
794 levelInfo[level].leveldatasize += face.faceData[level].size();
795 levelInfo[level].nfaces++;
798 levelInfo[0].nfaces =
_header.nfaces;
800 int nlevels = int(levelInfo.size());
803 std::vector<std::vector<std::byte>> compressedLevelDataHeaders(nlevels);
804 std::vector<std::vector<size_t>> largeFaceHeaders(nlevels);
805 size_t totalLevelDataSize = 0;
806 for (
int level = 0; level < nlevels; level++) {
807 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
808 std::vector<FaceDataHeader> levelDataHeader(nfacesThisLevel);
809 for (uint32_t f = 0; f < nfacesThisLevel; f++) {
810 uint32_t faceId = level==0? f :
_faceids_r[f];
811 if (
_faces[faceId].fdh.size() >
size_t(level)) {
812 levelDataHeader[f] =
_faces[faceId].fdh[level];
813 if (levelDataHeader[f].isLargeFace()) {
815 largeFaceHeaders[level].push_back(
_faces[faceId].faceData[level].size());
820 levelInfo[level].levelheadersize = uint32_t(compressedLevelDataHeaders[level].size());
821 levelInfo[level].leveldatasize += levelInfo[level].levelheadersize +
sizeof(size_t) * largeFaceHeaders[level].size();
822 totalLevelDataSize += levelInfo[level].leveldatasize;
826 std::vector<MetaEntry*> lmdEntries;
827 std::vector<std::byte> metaData, compressedMetaData;
828 for (
size_t i = 0, n =
_metadata.size(); i < n; i++) {
832 lmdEntries.push_back(&e);
838 if (!metaData.empty()) {
839 compressDataBlock(compressor, compressedMetaData, metaData.data(), metaData.size());
843 size_t nLmd = lmdEntries.size();
844 std::vector<std::byte> lmdHeader, compressedLmdHeader;
845 std::vector<std::vector<std::byte>> compressedLargeMetaData(nLmd);
848 for (
size_t i = 0; i < nLmd; i++) {
852 uint8_t keysize = uint8_t(e->
key.size()+1);
854 uint32_t datasize = (uint32_t)e->
data.size();
855 uint32_t zipsize = (uint32_t)compressedLargeMetaData[i].size();
863 compressDataBlock(compressor, compressedLmdHeader, lmdHeader.data(), lmdHeader.size());
866 compressor =
nullptr;
870 _header.faceinfosize = compressedFaceInfo.size();
871 _header.constdatasize = compressedConstData.size();
873 _header.leveldatasize = totalLevelDataSize;
874 _header.metadatamemsize = uint32_t(metaData.size());
875 _header.metadatazipsize = uint32_t(compressedMetaData.size());
876 _extheader.lmdheadermemsize = lmdHeader.size();
877 _extheader.lmdheaderzipsize = compressedLmdHeader.size();
880 FILE* newfp = fopen(
_newpath.c_str(),
"wb+");
891 writeBlock(newfp, compressedFaceInfo.data(), compressedFaceInfo.size());
894 writeBlock(newfp, compressedConstData.data(), compressedConstData.size());
900 for (
int level = 0; level < nlevels; level++) {
902 writeBlock(newfp, compressedLevelDataHeaders[level].data(), compressedLevelDataHeaders[level].size());
905 if (!largeFaceHeaders[level].empty()) {
906 writeBlock(newfp, largeFaceHeaders[level].data(),
sizeof(
size_t) * largeFaceHeaders[level].size());
910 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
911 for (uint32_t rfaceId = 0; rfaceId < nfacesThisLevel; rfaceId++) {
912 uint32_t faceId = level==0? rfaceId :
_faceids_r[rfaceId];
913 if (
_faces[faceId].faceData.size() >
size_t(level)) {
920 if (!compressedMetaData.empty()) {
921 writeBlock(newfp, compressedMetaData.data(), compressedMetaData.size());
925 uint64_t compatibilityBarrier = 0;
926 writeBlock(newfp, &compatibilityBarrier,
sizeof(compatibilityBarrier));
929 if (!compressedLmdHeader.empty()) {
930 writeBlock(newfp, compressedLmdHeader.data(), compressedLmdHeader.size());
931 for (
auto& lmd : compressedLargeMetaData) {
942 for (
int faceid = 0, n =
int(
_faceinfo.size()); faceid < n; faceid++) {
944 if (!f.isConstant())
continue;
949 bool isTriangle =
_header.meshtype == mt_triangle;
950 int nedges = isTriangle ? 3 : 4;
951 for (
int eid = 0; isConst && (eid < nedges); eid++) {
952 bool prevWasSubface = f.isSubface();
953 int prevFid = faceid;
956 int afid = f.adjface(eid);
957 int aeid = f.adjedge(eid);
959 const int maxcount = 10;
960 while (afid != faceid && afid >= 0 && ++count < maxcount) {
963 if (!af.isConstant() ||
965 { isConst =
false;
break; }
968 bool isSubface = af.isSubface();
969 bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
971 prevWasSubface = isSubface;
975 aeid = (aeid + 1) % nedges;
976 afid = af.adjface(aeid);
977 aeid = af.adjedge(aeid);
988 aeid = (aeid - 1 + nedges) % nedges;
989 afid = f.adjface(aeid);
990 aeid = f.adjedge(aeid);
992 while (afid != faceid && afid >= 0 && ++count < maxcount) {
995 if (!af.isConstant() ||
997 { isConst =
false;
break; }
1001 aeid = (aeid - 1 + nedges) % nedges;
1002 afid = af.adjface(aeid);
1003 aeid = af.adjedge(aeid);
1006 bool isSubface = af.isSubface();
1007 if (isSubface && !prevWasSubface) {
1008 aeid = (aeid + 3) % 4;
1009 afid = af.adjface(aeid);
1010 aeid = (af.adjedge(aeid) + 3) % 4;
1012 prevWasSubface = isSubface;
1017 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