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;
195 setError(
"PtexWriter error: faceid out of range");
200 setError(
"PtexWriter error: asymmetric face res not supported for triangle textures");
215 f.flags &= FaceInfo::flag_subface;
219 f.flags |= (uint8_t)flags;
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);
405 size_t facesize = faceres.size64() *
_pixelSize;
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);
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) {
483 tile->assign(p, p + _pixelSize);
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)
533 _genmipmaps(genmipmaps),
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());
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);
726 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
727 if (
_faceinfo[faceid].flags == uint8_t(-1)) {
733 if (info.isConstant()) {
737 size_t size = _pixelSize * info.res.size64();
738 char* data =
new char [size];
748 nlevels += std::max(0, std::min(info.res.ulog2, info.res.vlog2) -
MinReductionLog2);
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++;
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;
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)) {
914 writeBlock(newfp, _faces[faceId].faceData[level].data(), _faces[faceId].faceData[level].size());
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;
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;
bool isConstant(const void *data, int stride, int ures, int vres, int pixelSize)
virtual Ptex::BorderMode vBorderMode()=0
Mode for filtering texture access beyond mesh border.
virtual bool hasMipMaps()
True if the file has mipmaps.
virtual int numChannels()=0
Number of channels stored in file.
Interface for writing data to a ptex file.
uint32_t floor_log2(uint32_t x)
const int MetaDataThreshold
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
std::vector< MetaEntry > _metadata
size_t writeBlock(FILE *fp, const void *data, size_t size)
std::vector< libdeflate_compressor * > _compressors
texel access is clamped to border
void deinterleave(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
libdeflate_compressor * getCompressor()
std::vector< uint32_t > _rfaceids
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
void addMetaData(const char *key, MetaDataType t, const void *value, int size)
void storeConstValue(int faceid, const void *data, int stride, Res res)
Single-precision (32-bit) floating point.
DataType datatype() const
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
virtual PtexMetaData * getMetaData()
Access meta data.
std::vector< FaceInfo > _faceinfo
virtual bool close(Ptex::String &error)
Close the file.
int v() const
V resolution in texels.
virtual int alphaChannel()=0
Index of alpha channel (if any).
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.
void genRfaceids(const FaceInfo *faces, int nfaces, uint32_t *rfaceids, uint32_t *faceids)
static const int MinReductionLog2
void reduce(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
void setError(const std::string &error)
void getError(Ptex::String &error)
Double-precision (32-bit) floating point.
virtual Ptex::MeshType meshType()=0
Type of mesh for which texture data is defined.
size_t size64() const
Total size of specified texture in texels (u * v), allowing arbitrarily large textures.
void reduceTri(const void *src, int sstride, int w, int, void *dst, int dstride, DataType dt, int nchan)
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
int u() const
U resolution in texels.
int8_t vlog2
log base 2 of v resolution, in texels
static PtexTexture * open(const char *path, Ptex::String &error, bool premultiply=0)
Open a ptex file for reading.
void getCompressedData(int faceid, int level, FaceDataHeader &fdh, std::vector< std::byte > &data)
void average(const void *src, int sstride, int uw, int vw, void *dst, DataType dt, int nchan)
void compressDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, const void *data, size_t size)
Res calcTileRes(Res faceres)
MeshType
Type of base mesh for which the textures are defined.
std::vector< std::byte > _constdata
Unsigned, 16-bit integer.
#define PtexFileMinorVersion
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
std::vector< FaceRec > _faces
virtual void * getConstantData(int faceid) final
Access the constant (or average) data value for a given face.
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
std::string fileError(const char *message, const char *path)
virtual ~PtexMainWriter()
std::vector< FaceDataHeader > fdh
virtual int numFaces()=0
Number of faces stored in file.
PtexUtils::ReduceFn * _reduceFn
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
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.
void addToMetaDataBlock(std::vector< std::byte > &dataBlock, const MetaEntry &val)
std::vector< std::vector< std::byte > > faceData
#define PtexFileMajorVersion
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
void encodeDifference(void *data, size_t size, DataType dt)
bool ok(Ptex::String &error)
std::map< std::string, int > _metamap
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
virtual Ptex::DataType dataType()=0
Type of data stored in file.
Smart-pointer for acquiring and releasing API objects.
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
void flagConstantNeighorhoods()
void compressFaceData(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData)
virtual Ptex::EdgeFilterMode edgeFilterMode()=0
Mode for filtering textures across edges.
int8_t ulog2
log base 2 of u resolution, in texels
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Interface for reading data from a ptex file.
static bool applyEdits(const char *path, Ptex::String &error)
Obsolete (returns true).
std::vector< uint32_t > _faceids_r
Pixel resolution of a given texture.
void addToDataBlock(std::vector< std::byte > &dataBlock, const void *data, size_t size)
Automatically acquire and release lock within enclosing scope.
void releaseCompressor(libdeflate_compressor *compressor)
virtual Ptex::BorderMode uBorderMode()=0
Mode for filtering texture access beyond mesh border.
Single-precision (32-bit) floating point.
int DataSize(DataType dt)
Look up size of given data type (in bytes).
MetaDataType
Type of meta data entry.
void multalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Information about a face, as stored in the Ptex file header.
void compressFaceDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData, int stride)
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
DataType
Type of data stored in texture file.
const char * c_str() const
#define PTEX_NAMESPACE_END
void copy(const void *src, int sstride, void *dst, int dstride, int vres, int rowlen)
Public API classes for reading, writing, caching, and filtering Ptex files.
std::vector< uint8_t > data