Ptex
PtexWriter.cpp
Go to the documentation of this file.
1 /*
2 PTEX SOFTWARE
3 Copyright 2014 Disney Enterprises, Inc. All rights reserved
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
8 
9  * Redistributions of source code must retain the above copyright
10  notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13  notice, this list of conditions and the following disclaimer in
14  the documentation and/or other materials provided with the
15  distribution.
16 
17  * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18  Studios" or the names of its contributors may NOT be used to
19  endorse or promote products derived from this software without
20  specific prior written permission from Walt Disney Pictures.
21 
22 Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25 FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26 IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 */
35 
36 #include "PtexPlatform.h"
37 #include <errno.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <algorithm>
43 #include <iostream>
44 #include <sstream>
45 #if defined(__FreeBSD__)
46  #include <unistd.h>
47  #include <stddef.h>
48 #endif
49 #include <libdeflate.h>
50 
51 #include "Ptexture.h"
52 #include "PtexUtils.h"
53 #include "PtexWriter.h"
54 
56 
57 namespace {
58 
59  std::string fileError(const char* message, const char* path)
60  {
61  std::stringstream str;
62  str << message << path << "\n" << strerror(errno);
63  return str.str();
64  }
65 
66  bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan,
67  Ptex::String& error)
68  {
69  // check to see if given file attributes are valid
70  if (!LittleEndian()) {
71  error = "PtexWriter doesn't currently support big-endian cpu's";
72  return 0;
73  }
74 
75  if (mt < Ptex::mt_triangle || mt > Ptex::mt_quad) {
76  error = "PtexWriter error: Invalid mesh type";
77  return 0;
78  }
79 
80  if (dt < Ptex::dt_uint8 || dt > Ptex::dt_float) {
81  error = "PtexWriter error: Invalid data type";
82  return 0;
83  }
84 
85  if (nchannels <= 0) {
86  error = "PtexWriter error: Invalid number of channels";
87  return 0;
88  }
89 
90  if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
91  error = "PtexWriter error: Invalid alpha channel";
92  return 0;
93  }
94 
95  return 1;
96  }
97 }
98 
99 
100 PtexWriter* PtexWriter::open(const char* path,
102  int nchannels, int alphachan, int nfaces,
103  Ptex::String& error, bool genmipmaps)
104 {
105  if (!checkFormat(mt, dt, nchannels, alphachan, error))
106  return 0;
107 
108  PtexMainWriter* w = new PtexMainWriter(path, 0,
109  mt, dt, nchannels, alphachan, nfaces,
110  genmipmaps);
111  if (!w->ok(error)) {
112  w->release();
113  return 0;
114  }
115  return w;
116 }
117 
118 
119 PtexWriter* PtexWriter::edit(const char* path,
121  int nchannels, int alphachan, int nfaces,
122  Ptex::String& error, bool genmipmaps)
123 {
124  if (!checkFormat(mt, dt, nchannels, alphachan, error))
125  return 0;
126 
127  // try to open existing file (it might not exist)
128  FILE* fp = fopen(path, "rb+");
129  if (!fp && errno != ENOENT) {
130  error = fileError("Can't open ptex file for update: ", path).c_str();
131  }
132 
133  PtexTexture* tex = 0;
134  if (fp) {
135  // got an existing file, close and reopen with PtexReader
136  fclose(fp);
137 
138  // open reader for existing file
139  tex = PtexTexture::open(path, error);
140  if (!tex) return 0;
141 
142  // make sure header matches
143  bool headerMatch = (mt == tex->meshType() &&
144  dt == tex->dataType() &&
145  nchannels == tex->numChannels() &&
146  alphachan == tex->alphaChannel() &&
147  nfaces == tex->numFaces());
148  if (!headerMatch) {
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();
153  return 0;
154  }
155  }
156  PtexMainWriter* w = new PtexMainWriter(path, tex, mt, dt, nchannels, alphachan,
157  nfaces, genmipmaps);
158 
159  if (!w->ok(error)) {
160  w->release();
161  return 0;
162  }
163  return w;
164 }
165 
166 PtexWriter* PtexWriter::edit(const char* path, bool /*incremental*/,
168  int nchannels, int alphachan, int nfaces,
169  Ptex::String& error, bool genmipmaps)
170 {
171  // This function is deprecated
172  return edit(path, mt, dt, nchannels, alphachan, nfaces, error, genmipmaps);
173 }
174 
176 {
177  // This function is obsolete
178  return 1;
179 }
180 
181 
183 {
184  Ptex::String error;
185  // close writer if app didn't, and report error if any
186  if (!close(error))
187  std::cerr << error.c_str() << std::endl;
188  delete this;
189 }
190 
191 
192 bool PtexMainWriter::storeFaceInfo(int faceid, FaceInfo& f, const FaceInfo& src, int flags)
193 {
194  if (faceid < 0 || size_t(faceid) >= _header.nfaces) {
195  setError("PtexWriter error: faceid out of range");
196  return 0;
197  }
198 
199  if (_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
200  setError("PtexWriter error: asymmetric face res not supported for triangle textures");
201  return 0;
202  }
203 
204  // copy all values
205  f = src;
206 
207  // and clear extraneous ones
208  if (_header.meshtype == mt_triangle) {
209  f.flags = 0; // no user-settable flags on triangles
210  f.adjfaces[3] = -1;
211  f.adjedges &= 0x3f; // clear all but bottom six bits
212  }
213  else {
214  // clear non-user-settable flags
215  f.flags &= FaceInfo::flag_subface;
216  }
217 
218  // set new flags
219  f.flags |= (uint8_t)flags;
220  return 1;
221 }
222 
223 
224 void PtexMainWriter::writeMeta(const char* key, const char* value)
225 {
226  addMetaData(key, mdt_string, value, int(strlen(value)+1));
227 }
228 
229 
230 void PtexMainWriter::writeMeta(const char* key, const int8_t* value, int count)
231 {
232  addMetaData(key, mdt_int8, value, count);
233 }
234 
235 
236 void PtexMainWriter::writeMeta(const char* key, const int16_t* value, int count)
237 {
238  addMetaData(key, mdt_int16, value, count*(int)sizeof(int16_t));
239 }
240 
241 
242 void PtexMainWriter::writeMeta(const char* key, const int32_t* value, int count)
243 {
244  addMetaData(key, mdt_int32, value, count*(int)sizeof(int32_t));
245 }
246 
247 
248 void PtexMainWriter::writeMeta(const char* key, const float* value, int count)
249 {
250  addMetaData(key, mdt_float, value, count*(int)sizeof(float));
251 }
252 
253 
254 void PtexMainWriter::writeMeta(const char* key, const double* value, int count)
255 {
256  addMetaData(key, mdt_double, value, count*(int)sizeof(double));
257 }
258 
259 
261 {
262  int nkeys = data->numKeys();
263  for (int i = 0; i < nkeys; i++) {
264  const char* key = 0;
265  MetaDataType type;
266  data->getKey(i, key, type);
267  int count;
268  switch (type) {
269  case mdt_string:
270  {
271  const char* val=0;
272  data->getValue(key, val);
273  writeMeta(key, val);
274  }
275  break;
276  case mdt_int8:
277  {
278  const int8_t* val=0;
279  data->getValue(key, val, count);
280  writeMeta(key, val, count);
281  }
282  break;
283  case mdt_int16:
284  {
285  const int16_t* val=0;
286  data->getValue(key, val, count);
287  writeMeta(key, val, count);
288  }
289  break;
290  case mdt_int32:
291  {
292  const int32_t* val=0;
293  data->getValue(key, val, count);
294  writeMeta(key, val, count);
295  }
296  break;
297  case mdt_float:
298  {
299  const float* val=0;
300  data->getValue(key, val, count);
301  writeMeta(key, val, count);
302  }
303  break;
304  case mdt_double:
305  {
306  const double* val=0;
307  data->getValue(key, val, count);
308  writeMeta(key, val, count);
309  }
310  break;
311  }
312  }
313 }
314 
315 
317  const void* value, int size)
318 {
319  if (strlen(key) > 255) {
320  std::stringstream str;
321  str << "PtexWriter error: meta data key too long (max=255) \"" << key << "\"";
322  setError(str.str());
323  return;
324  }
325  if (size <= 0) {
326  std::stringstream str;
327  str << "PtexWriter error: meta data size <= 0 for \"" << key << "\"";
328  setError(str.str());
329  }
330  std::map<std::string,int>::iterator iter = _metamap.find(key);
331  int index;
332  if (iter != _metamap.end()) {
333  // see if we already have this entry - if so, overwrite it
334  index = iter->second;
335  }
336  else {
337  // allocate a new entry
338  index = (int)_metadata.size();
339  _metadata.resize(index+1);
340  _metamap[key] = index;
341  }
342  MetaEntry& m = _metadata[index];
343  m.key = key;
344  m.datatype = t;
345  m.data.resize(size);
346  memcpy(&m.data[0], value, size);
347 }
348 
349 
350 size_t PtexMainWriter::writeBlock(FILE* fp, const void* data, size_t size)
351 {
352  if (!_ok) return 0;
353  if (!fwrite(data, size, 1, fp)) {
354  setError("PtexWriter error: file write failed");
355  return 0;
356  }
357  return size;
358 }
359 
360 
361 void PtexMainWriter::addToDataBlock(std::vector<std::byte>& dataBlock, const void* data, size_t size)
362 {
363  size_t previousSize = dataBlock.size();
364  dataBlock.resize(previousSize+size);
365  memcpy(dataBlock.data() + previousSize, data, size);
366 }
367 
368 
369 libdeflate_compressor* PtexMainWriter::getCompressor()
370 {
372  libdeflate_compressor* compressor;
373  if (!_compressors.empty()) {
374  compressor = _compressors.back();
375  _compressors.pop_back();
376  } else {
377  const int compressionLevel = 4;
378  compressor = libdeflate_alloc_compressor(compressionLevel);
379  }
380  return compressor;
381 }
382 
383 
384 void PtexMainWriter::releaseCompressor(libdeflate_compressor* compressor)
385 {
387  _compressors.push_back(compressor);
388 }
389 
390 
391 void PtexMainWriter::compressDataBlock(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, const void* data, size_t size)
392 {
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");
397  }
398  compressedData.resize(compressedSize);
399 }
400 
401 
403 {
404  // desired number of tiles = floor(log2(facesize / tilesize))
405  size_t facesize = faceres.size64() * _pixelSize;
406  int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
407  if (ntileslog2 == 0) return faceres;
408 
409  // number of tiles is defined as:
410  // ntileslog2 = ureslog2 + vreslog2 - (tile_ureslog2 + tile_vreslog2)
411  // rearranging to solve for the tile res:
412  // tile_ureslog2 + tile_vreslog2 = ureslog2 + vreslog2 - ntileslog2
413  int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
414 
415  // choose u and v sizes for roughly square result (u ~= v ~= n/2)
416  // and make sure tile isn't larger than face
417  Res tileres;
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));
420  return tileres;
421 }
422 
423 
424 void PtexMainWriter::compressFaceDataBlock(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, FaceDataHeader& fdh,
425  Res res, const void* uncompressedData, int stride)
426 {
427  // compress a single face data block; could be a whole face or just a tile
428 
429  // first, copy to temp buffer, and deinterleave
430  int ures = res.u(), vres = res.v();
431  size_t tempSize = ures*vres*_pixelSize;
432  std::vector<std::byte> temp(tempSize);
433  PtexUtils::deinterleave(uncompressedData, stride, ures, vres, temp.data(),
434  ures*DataSize(datatype()),
436 
437  // difference if needed
438  bool diff = (datatype() == dt_uint8 ||
439  datatype() == dt_uint16);
440  if (diff) PtexUtils::encodeDifference(temp.data(), tempSize, datatype());
441 
442  // compress
443  compressDataBlock(compressor, compressedData, temp.data(), tempSize);
444 
445  // record compressed size and encoding in data header
446  fdh.set(compressedData.size(), diff ? enc_diffzipped : enc_zipped);
447 }
448 
449 
450 void PtexMainWriter::compressFaceData(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, FaceDataHeader& fdh,
451  Res res, const void* uncompressedData)
452 {
453  // determine whether to break into tiles
454  int stride = res.u() * _pixelSize;
455  Res tileRes = calcTileRes(res);
456  size_t ntilesu = res.ntilesu(tileRes);
457  size_t ntilesv = res.ntilesv(tileRes);
458  size_t ntiles = ntilesu * ntilesv;
459  if (ntiles == 1) {
460  // output single block
461  compressFaceDataBlock(compressor, compressedData, fdh, res, uncompressedData, stride);
462  } else {
463  // alloc tiles
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();
468  size_t tileustride = tileures*_pixelSize;
469  size_t tilevstride = tilevres*stride;
470 
471  // compress tiles
472  std::vector<std::byte>* tile = tiles.data();
473  FaceDataHeader* tdh = tileHeader.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) {
480  // determine if tile is constant
481  if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize)) {
482  // output a const tile
483  tile->assign(p, p + _pixelSize);
484  tdh->set(_pixelSize, enc_constant);
485  } else {
486  // output a compressed tile
487  compressFaceDataBlock(compressor, *tile, *tdh, tileRes, p, stride);
488  }
489  }
490  }
491 
492  // compress tile header
493  std::vector<std::byte> compressedTileHeader;
494  compressDataBlock(compressor, compressedTileHeader, reinterpret_cast<std::byte*>(tileHeader.data()),
495  ntiles * sizeof(FaceDataHeader));
496  uint32_t compressedTileHeaderSize = compressedTileHeader.size();
497 
498  size_t totalSize = sizeof(tileRes) + sizeof(compressedTileHeaderSize) + compressedTileHeaderSize;
499  for (auto& tile : tiles) {
500  totalSize += tile.size();
501  }
502  compressedData.reserve(totalSize);
503  addToDataBlock(compressedData, &tileRes, sizeof(tileRes));
504  addToDataBlock(compressedData, &compressedTileHeaderSize, sizeof(compressedTileHeaderSize));
505  addToDataBlock(compressedData, compressedTileHeader.data(), compressedTileHeaderSize);
506  for (auto& tile : tiles) {
507  addToDataBlock(compressedData, tile.data(), tile.size());
508  }
509 
510  fdh.set(totalSize, enc_tiled);
511  }
512 }
513 
514 
515 void PtexMainWriter::addToMetaDataBlock(std::vector<std::byte>& metaDataBlock, const MetaEntry& val)
516 {
517  uint8_t keysize = uint8_t(val.key.size()+1);
518  uint8_t datatype = val.datatype;
519  uint32_t datasize = uint32_t(val.data.size());
520  addToDataBlock(metaDataBlock, &keysize, sizeof(keysize));
521  addToDataBlock(metaDataBlock, val.key.c_str(), keysize);
522  addToDataBlock(metaDataBlock, &datatype, sizeof(datatype));
523  addToDataBlock(metaDataBlock, &datasize, sizeof(datasize));
524  addToDataBlock(metaDataBlock, &val.data[0], datasize);
525 }
526 
527 
530  int nchannels, int alphachan, int nfaces, bool genmipmaps)
531  : _ok(true),
532  _path(path),
533  _genmipmaps(genmipmaps),
534  _reader(0)
535 {
536  memset(&_header, 0, sizeof(_header));
537  _header.magic = Magic;
540  _header.meshtype = mt;
541  _header.datatype = dt;
542  _header.alphachan = alphachan;
543  _header.nchannels = (uint16_t)nchannels;
544  _header.nfaces = nfaces;
545  _header.extheadersize = sizeof(_extheader);
547 
548  memset(&_extheader, 0, sizeof(_extheader));
549 
550  if (mt == mt_triangle)
552  else
554 
555  // data will be written to a ".new" path and then renamed to final location
556  _newpath = path; _newpath += ".new";
557 
558  // init faceinfo and set flags to -1 to mark as uninitialized
559  _faceinfo.resize(nfaces);
560  for (int i = 0; i < nfaces; i++) _faceinfo[i].flags = uint8_t(-1);
561  _faces.resize(nfaces);
562  _constdata.resize(nfaces*_pixelSize);
563 
564  if (tex) {
565  // access reader implementation
566  // Note: we can assume we have a PtexReader because we opened the tex from the cache
567  _reader = static_cast<PtexReader*>(tex);
568 
569  // copy border modes
570  setBorderModes(tex->uBorderMode(), tex->vBorderMode());
571 
572  // copy edge filter mode
574 
575  // copy meta data from existing file
577  writeMeta(meta);
578  }
579 }
580 
581 
583 {
584  for (libdeflate_compressor* compressor : _compressors) {
585  libdeflate_free_compressor(compressor);
586  }
587  if (_reader) _reader->release();
588 }
589 
590 
592 {
593  if (_ok) finish();
594  if (_reader) {
595  if (!_reader->ok()) {
596  _ok = false;
597  }
598  _reader->release();
599  _reader = 0;
600  }
601  if (_ok) {
602  // rename temppath into final location
603  unlink(_path.c_str());
604  if (rename(_newpath.c_str(), _path.c_str()) == -1) {
605  setError(fileError("Can't write to ptex file: ", _path.c_str()).c_str());
606  unlink(_newpath.c_str());
607  }
608  }
609  if (!_ok) getError(error);
610  return _ok;
611 }
612 
613 bool PtexMainWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
614 {
615  if (!_ok) return false;
616 
617  FaceRec& face = _faces[faceid];
618  AutoMutex lock(face.mutex);
619 
620  // reset face data in case it was written previously
621  face.faceData.clear();
622  face.fdh.clear();
623 
624  // auto-compute stride
625  if (stride == 0) stride = f.res.u()*_pixelSize;
626 
627  // handle constant case
628  if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize)) {
629  return writeConstantFace(faceid, f, data);
630  }
631 
632  // non-constant case, ...
633 
634  // check and store face info
635  if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return false;
636 
637  // determine how many mipmap levels for face
638  int nlevels = 1;
639  if (_genmipmaps) {
640  nlevels += std::max(0, std::min(f.res.ulog2, f.res.vlog2) - MinReductionLog2);
641  }
642  face.faceData.resize(nlevels);
643  face.fdh.resize(nlevels);
644 
645  // copy data into face level 0
646  Ptex::Res res = f.res;
647  size_t rowlen = res.u() * _pixelSize, nrows = res.v();
648  face.faceData[0].resize(rowlen * nrows);
649  PtexUtils::copy(data, stride, face.faceData[0].data(), rowlen, nrows, rowlen);
650  data = face.faceData[0].data();
651  stride = rowlen;
652 
653  // premultiply into temp copy (if needed)
654  std::vector<std::byte> premultData;
655  if (_header.hasAlpha()) {
656  // copy to temp buffer, and premultiply alpha
657  premultData = face.faceData[0];
658  PtexUtils::multalpha(premultData.data(), res.size64(), datatype(), _header.nchannels,
660  data = premultData.data();
661  }
662 
663  // generate reductions (as needed)
664  libdeflate_compressor* compressor = getCompressor();
665  for (int level = 1; level < nlevels; level++) {
666  Ptex::Res nextres((int8_t)(res.ulog2-1), (int8_t)(res.vlog2-1));
667  face.faceData[level].resize(nextres.size64() * _pixelSize);
668  int dstride = nextres.u() * _pixelSize;
669  _reduceFn(data, stride, res.u(), res.v(), face.faceData[level].data(), dstride, datatype(), _header.nchannels);
670  data = face.faceData[level].data();
671  stride = dstride;
672  res = nextres;
673  }
674 
675  // compute and store constant value from last level (note: level 0 would be more accurate, but slower)
676  storeConstValue(faceid, data, stride, res);
677 
678  // free premultData (if allocated) as it is no longer needed
679  premultData.clear();
680  premultData.shrink_to_fit();
681 
682  // compress face data for each level
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;
686  compressFaceData(compressor, compressedData, face.fdh[level], res, face.faceData[level].data());
687  face.faceData[level] = std::move(compressedData);
688  }
689  releaseCompressor(compressor);
690 
691  return true;
692 }
693 
694 
695 bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
696 {
697  if (!_ok) return 0;
698 
699  // check and store face info
700  if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
701 
702  // store face value in constant block
703  memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
704  return 1;
705 }
706 
707 
708 
709 void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
710 {
711  // compute average value and store in _constdata block
712  std::byte* constdata = &_constdata[faceid*_pixelSize];
713  PtexUtils::average(data, stride, res.u(), res.v(), constdata,
715  if (_header.hasAlpha())
717 }
718 
719 
720 
722 {
723  uint32_t nfaces = _header.nfaces;
724  // copy missing faces from _reader
725  if (_reader) {
726  for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
727  if (_faceinfo[faceid].flags == uint8_t(-1)) {
728  // copy constant data
729  memcpy(&_constdata[faceid*_pixelSize], _reader->getConstantData(faceid), _pixelSize);
730 
731  // update faceinfo and copy face data
732  const Ptex::FaceInfo& info = _reader->getFaceInfo(faceid);
733  if (info.isConstant()) {
734  storeFaceInfo(faceid, _faceinfo[faceid], info, FaceInfo::flag_constant);
735  } else if (_genmipmaps && !_reader->hasMipMaps()) {
736  // read uncompressed data and generate mipmaps
737  size_t size = _pixelSize * info.res.size64();
738  char* data = new char [size];
739  _reader->getData(faceid, data, 0);
740  writeFace(faceid, info, data, 0);
741  delete [] data;
742  } else {
743  // read compressed data, including mipmaps if any
744  FaceRec& face = _faces[faceid];
745  storeFaceInfo(faceid, _faceinfo[faceid], info);
746  int nlevels = 1;
747  if (_genmipmaps) {
748  nlevels += std::max(0, std::min(info.res.ulog2, info.res.vlog2) - MinReductionLog2);
749  }
750  face.fdh.resize(nlevels);
751  face.faceData.resize(nlevels);
752  for (int level = 0; level < nlevels; level++) {
753  _reader->getCompressedData(faceid, level, face.fdh[level], face.faceData[level]);
754  }
755  }
756  }
757  }
758  }
759  else {
760  // just flag missing faces as constant (black)
761  for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
762  if (_faceinfo[faceid].flags == uint8_t(-1))
763  _faceinfo[faceid].flags = FaceInfo::flag_constant;
764  }
765  }
766 
767  // generate "rfaceids", reduction faceids, which are faceids reordered by decreasing smaller dimension
768  if (_genmipmaps) {
769  _rfaceids.resize(nfaces);
770  _faceids_r.resize(nfaces);
771  PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
772  }
773 
774  // flag faces w/ constant neighborhoods
776 
777  // compress face info block
778  std::vector<std::byte> compressedFaceInfo;
779  libdeflate_compressor* compressor = getCompressor();
780  compressDataBlock(compressor, compressedFaceInfo, _faceinfo.data(), _faceinfo.size()*sizeof(FaceInfo));
781 
782  // compress const data block
783  std::vector<std::byte> compressedConstData;
784  compressDataBlock(compressor, compressedConstData, _constdata.data(), _constdata.size());
785 
786  // create level info and compress level headers
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);
792  }
793  for (size_t level = 0; level < nlevelsThisFace; level++) {
794  levelInfo[level].leveldatasize += face.faceData[level].size();
795  levelInfo[level].nfaces++;
796  }
797  }
798  levelInfo[0].nfaces = _header.nfaces; // constant faces weren't counted in the loop above, but level 0 needs them
799 
800  int nlevels = int(levelInfo.size());
801 
802  // gather fdh for faces in each level into LevelInfo, and compress level data headers
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()) {
814  // large faces have a compressed size too big for the fdh; store in largeFaceHeader
815  largeFaceHeaders[level].push_back(_faces[faceId].faceData[level].size());
816  }
817  }
818  }
819  compressDataBlock(compressor, compressedLevelDataHeaders[level], levelDataHeader.data(), nfacesThisLevel * sizeof(FaceDataHeader));
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;
823  }
824 
825  // compress meta data
826  std::vector<MetaEntry*> lmdEntries; // large meta data items
827  std::vector<std::byte> metaData, compressedMetaData;
828  for (size_t i = 0, n = _metadata.size(); i < n; i++) {
829  MetaEntry& e = _metadata[i];
830  if (e.data.size() > MetaDataThreshold) {
831  // skip large items, but record for later
832  lmdEntries.push_back(&e);
833  } else {
834  // add small item to meta data block
835  addToMetaDataBlock(metaData, e);
836  }
837  }
838  if (!metaData.empty()) {
839  compressDataBlock(compressor, compressedMetaData, metaData.data(), metaData.size());
840  }
841 
842  // compress large meta data
843  size_t nLmd = lmdEntries.size();
844  std::vector<std::byte> lmdHeader, compressedLmdHeader;
845  std::vector<std::vector<std::byte>> compressedLargeMetaData(nLmd);
846  if (nLmd > 0) {
847  // compress lmd data items
848  for (size_t i = 0; i < nLmd; i++) {
849  MetaEntry* e= lmdEntries[i];
850  compressDataBlock(compressor, compressedLargeMetaData[i], &e->data[0], e->data.size());
851 
852  uint8_t keysize = uint8_t(e->key.size()+1);
853  uint8_t datatype = e->datatype;
854  uint32_t datasize = (uint32_t)e->data.size();
855  uint32_t zipsize = (uint32_t)compressedLargeMetaData[i].size();
856 
857  addToDataBlock(lmdHeader, &keysize, sizeof(keysize));
858  addToDataBlock(lmdHeader, e->key.c_str(), keysize);
859  addToDataBlock(lmdHeader, &datatype, sizeof(datatype));
860  addToDataBlock(lmdHeader, &datasize, sizeof(datasize));
861  addToDataBlock(lmdHeader, &zipsize, sizeof(zipsize));
862  }
863  compressDataBlock(compressor, compressedLmdHeader, lmdHeader.data(), lmdHeader.size());
864  }
865  releaseCompressor(compressor);
866  compressor = nullptr;
867 
868  // init header
869  _header.nlevels = nlevels;
870  _header.faceinfosize = compressedFaceInfo.size();
871  _header.constdatasize = compressedConstData.size();
872  _header.levelinfosize = LevelInfoSize * nlevels;
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();
878 
879  // create new file
880  FILE* newfp = fopen(_newpath.c_str(), "wb+");
881  if (!newfp) {
882  setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
883  return;
884  }
885 
886  // write header
887  writeBlock(newfp, &_header, HeaderSize);
889 
890  // write face info block
891  writeBlock(newfp, compressedFaceInfo.data(), compressedFaceInfo.size());
892 
893  // write const data block
894  writeBlock(newfp, compressedConstData.data(), compressedConstData.size());
895 
896  // write level info block
897  writeBlock(newfp, &levelInfo[0], LevelInfoSize * nlevels);
898 
899  // write level data blocks
900  for (int level = 0; level < nlevels; level++) {
901  // write level data header
902  writeBlock(newfp, compressedLevelDataHeaders[level].data(), compressedLevelDataHeaders[level].size());
903 
904  // write large face header, if present
905  if (!largeFaceHeaders[level].empty()) {
906  writeBlock(newfp, largeFaceHeaders[level].data(), sizeof(size_t) * largeFaceHeaders[level].size());
907  }
908 
909  // write compressed face data for faces in level
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());
915  }
916  }
917  }
918 
919  // write meta data
920  if (!compressedMetaData.empty()) {
921  writeBlock(newfp, compressedMetaData.data(), compressedMetaData.size());
922  }
923 
924  // write compatibility barrier
925  uint64_t compatibilityBarrier = 0;
926  writeBlock(newfp, &compatibilityBarrier, sizeof(compatibilityBarrier));
927 
928  // write large meta data
929  if (!compressedLmdHeader.empty()) {
930  writeBlock(newfp, compressedLmdHeader.data(), compressedLmdHeader.size());
931  for (auto& lmd : compressedLargeMetaData) {
932  writeBlock(newfp, lmd.data(), lmd.size());
933  }
934  }
935  fclose(newfp);
936 }
937 
938 
940 {
941  // for each constant face
942  for (int faceid = 0, n = int(_faceinfo.size()); faceid < n; faceid++) {
943  FaceInfo& f = _faceinfo[faceid];
944  if (!f.isConstant()) continue;
945  std::byte* constdata = &_constdata[faceid*_pixelSize];
946 
947  // check to see if neighborhood is constant
948  bool isConst = true;
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;
954 
955  // traverse around vertex in CW direction
956  int afid = f.adjface(eid);
957  int aeid = f.adjedge(eid);
958  int count = 0;
959  const int maxcount = 10; // max valence (as safety valve)
960  while (afid != faceid && afid >= 0 && ++count < maxcount) {
961  // check if neighbor is constant, and has the same value as face
962  FaceInfo& af = _faceinfo[afid];
963  if (!af.isConstant() ||
964  0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
965  { isConst = false; break; }
966 
967  // if vertex is a T vertex between subface and main face, we can stop
968  bool isSubface = af.isSubface();
969  bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
970  if (isT) break;
971  prevWasSubface = isSubface;
972 
973  // traverse around vertex in CW direction
974  prevFid = afid;
975  aeid = (aeid + 1) % nedges;
976  afid = af.adjface(aeid);
977  aeid = af.adjedge(aeid);
978  }
979 
980  if (afid < 0) {
981  // hit boundary edge, check boundary mode
983  isConst = false;
984  }
985 
986  // and traverse CCW neighbors too
987  if (isConst) {
988  aeid = (aeid - 1 + nedges) % nedges;
989  afid = f.adjface(aeid);
990  aeid = f.adjedge(aeid);
991  count = 0;
992  while (afid != faceid && afid >= 0 && ++count < maxcount) {
993  // check if neighbor is constant, and has the same value as face
994  FaceInfo& af = _faceinfo[afid];
995  if (!af.isConstant() ||
996  0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
997  { isConst = false; break; }
998 
999  // traverse around vertex in CCW direction
1000  prevFid = afid;
1001  aeid = (aeid - 1 + nedges) % nedges;
1002  afid = af.adjface(aeid);
1003  aeid = af.adjedge(aeid);
1004 
1005  // if traversing to a subface, switch to secondary subface (afid points to primary/CW subface)
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;
1011  }
1012  prevWasSubface = isSubface;
1013  }
1014  }
1015  }
1016  }
1017  if (isConst) f.flags |= FaceInfo::flag_nbconstant;
1018  }
1019 }
1020 
1021 
const uint32_t Magic
Definition: PtexIO.h:99
const int ExtHeaderSize
Definition: PtexIO.h:101
const int HeaderSize
Definition: PtexIO.h:100
const int LevelInfoSize
Definition: PtexIO.h:102
bool LittleEndian()
Definition: PtexIO.h:112
const int TileSize
Definition: PtexIO.h:108
@ enc_diffzipped
Definition: PtexIO.h:81
@ enc_zipped
Definition: PtexIO.h:81
@ enc_constant
Definition: PtexIO.h:81
@ enc_tiled
Definition: PtexIO.h:81
const int MetaDataThreshold
Definition: PtexIO.h:110
Platform-specific classes, functions, and includes.
#define PTEX_NAMESPACE_END
Definition: PtexVersion.h:62
#define PtexFileMinorVersion
Definition: PtexVersion.h:41
#define PtexFileMajorVersion
Definition: PtexVersion.h:40
Public API classes for reading, writing, caching, and filtering Ptex files.
Automatically acquire and release lock within enclosing scope.
Definition: PtexMutex.h:43
Res calcTileRes(Res faceres)
Definition: PtexWriter.cpp:402
void releaseCompressor(libdeflate_compressor *compressor)
Definition: PtexWriter.cpp:384
void getError(Ptex::String &error)
Definition: PtexWriter.h:83
void compressFaceDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData, int stride)
Definition: PtexWriter.cpp:424
std::vector< libdeflate_compressor * > _compressors
Definition: PtexWriter.h:127
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
Definition: PtexWriter.cpp:192
void addToDataBlock(std::vector< std::byte > &dataBlock, const void *data, size_t size)
Definition: PtexWriter.cpp:361
std::string _newpath
Definition: PtexWriter.h:132
std::vector< uint32_t > _rfaceids
Definition: PtexWriter.h:136
size_t writeBlock(FILE *fp, const void *data, size_t size)
Definition: PtexWriter.cpp:350
std::map< std::string, int > _metamap
Definition: PtexWriter.h:125
virtual bool close(Ptex::String &error)
Close the file.
Definition: PtexWriter.cpp:591
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition: PtexWriter.cpp:695
PtexReader * _reader
Definition: PtexWriter.h:147
bool ok(Ptex::String &error)
Definition: PtexWriter.h:79
std::vector< uint32_t > _faceids_r
Definition: PtexWriter.h:137
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
Definition: PtexWriter.cpp:224
void compressDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, const void *data, size_t size)
Definition: PtexWriter.cpp:391
void setError(const std::string &error)
Definition: PtexWriter.h:112
void storeConstValue(int faceid, const void *data, int stride, Res res)
Definition: PtexWriter.cpp:709
std::vector< FaceInfo > _faceinfo
Definition: PtexWriter.h:134
ExtHeader _extheader
Definition: PtexWriter.h:122
void addMetaData(const char *key, MetaDataType t, const void *value, int size)
Definition: PtexWriter.cpp:316
void compressFaceData(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, FaceDataHeader &fdh, Res res, const void *uncompressedData)
Definition: PtexWriter.cpp:450
virtual ~PtexMainWriter()
Definition: PtexWriter.cpp:582
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
Definition: PtexWriter.h:67
std::vector< MetaEntry > _metadata
Definition: PtexWriter.h:124
PtexUtils::ReduceFn * _reduceFn
Definition: PtexWriter.h:130
static const int MinReductionLog2
Definition: PtexWriter.h:139
void addToMetaDataBlock(std::vector< std::byte > &dataBlock, const MetaEntry &val)
Definition: PtexWriter.cpp:515
std::string _path
Definition: PtexWriter.h:120
Header _header
Definition: PtexWriter.h:121
DataType datatype() const
Definition: PtexWriter.h:90
std::vector< FaceRec > _faces
Definition: PtexWriter.h:145
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition: PtexWriter.cpp:613
Mutex _compressorMutex
Definition: PtexWriter.h:128
std::vector< std::byte > _constdata
Definition: PtexWriter.h:135
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
Definition: PtexWriter.cpp:528
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
Definition: PtexWriter.h:62
void flagConstantNeighorhoods()
Definition: PtexWriter.cpp:939
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexWriter.cpp:182
libdeflate_compressor * getCompressor()
Definition: PtexWriter.cpp:369
Meta data accessor.
Definition: Ptexture.h:331
virtual int numKeys()=0
Query number of meta data entries stored in file.
virtual void getValue(const char *key, const char *&value)=0
Query the value of a given meta data entry.
virtual void getKey(int index, const char *&key, Ptex::MetaDataType &type)=0
Query the name and type of a meta data entry.
Smart-pointer for acquiring and releasing API objects.
Definition: Ptexture.h:1067
bool ok() const
Definition: PtexReader.h:65
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexReader.h:57
virtual PtexMetaData * getMetaData()
Access meta data.
Definition: PtexReader.cpp:337
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
Definition: PtexReader.cpp:643
virtual bool hasMipMaps()
True if the file has mipmaps.
Definition: PtexReader.h:100
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
Definition: PtexReader.cpp:269
void getCompressedData(int faceid, int level, FaceDataHeader &fdh, std::vector< std::byte > &data)
Definition: PtexReader.cpp:892
virtual void * getConstantData(int faceid) final
Access the constant (or average) data value for a given face.
Definition: PtexReader.h:108
Interface for reading data from a ptex file.
Definition: Ptexture.h:460
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.
Definition: PtexReader.cpp:61
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.
Definition: Ptexture.h:819
static bool applyEdits(const char *path, Ptex::String &error)
Obsolete (returns true).
Definition: PtexWriter.cpp:175
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.
Definition: PtexWriter.cpp:119
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:100
Memory-managed string.
Definition: Ptexture.h:299
const char * c_str() const
Definition: Ptexture.h:307
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
Definition: PtexWriter.cpp:66
std::string fileError(const char *message, const char *path)
Definition: PtexWriter.cpp:59
void genRfaceids(const FaceInfo *faces, int nfaces, uint32_t *rfaceids, uint32_t *faceids)
Definition: PtexUtils.cpp:630
bool isConstant(const void *data, int stride, int ures, int vres, int pixelSize)
Definition: PtexUtils.cpp:147
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition: PtexUtils.cpp:618
void reduce(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition: PtexUtils.cpp:299
void deinterleave(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition: PtexUtils.cpp:226
void encodeDifference(void *data, size_t size, DataType dt)
Definition: PtexUtils.cpp:251
void reduceTri(const void *src, int sstride, int w, int, void *dst, int dstride, DataType dt, int nchan)
Definition: PtexUtils.cpp:406
void copy(const void *src, int sstride, void *dst, int dstride, int vres, int rowlen)
Definition: PtexUtils.cpp:438
uint32_t floor_log2(uint32_t x)
Definition: PtexUtils.h:79
void multalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition: PtexUtils.cpp:579
void average(const void *src, int sstride, int uw, int vw, void *dst, DataType dt, int nchan)
Definition: PtexUtils.cpp:522
int DataSize(DataType dt)
Look up size of given data type (in bytes).
Definition: Ptexture.h:130
DataType
Type of data stored in texture file.
Definition: Ptexture.h:72
@ dt_float
Single-precision (32-bit) floating point.
Definition: Ptexture.h:76
@ dt_uint16
Unsigned, 16-bit integer.
Definition: Ptexture.h:74
@ dt_uint8
Unsigned, 8-bit integer.
Definition: Ptexture.h:73
MeshType
Type of base mesh for which the textures are defined.
Definition: Ptexture.h:66
@ mt_triangle
Mesh is triangle-based.
Definition: Ptexture.h:67
@ mt_quad
Mesh is quad-based.
Definition: Ptexture.h:68
@ m_clamp
texel access is clamped to border
Definition: Ptexture.h:87
MetaDataType
Type of meta data entry.
Definition: Ptexture.h:102
@ mdt_string
Null-terminated string.
Definition: Ptexture.h:103
@ mdt_float
Single-precision (32-bit) floating point.
Definition: Ptexture.h:107
@ mdt_int32
Signed 32-bit integer.
Definition: Ptexture.h:106
@ mdt_int8
Signed 8-bit integer.
Definition: Ptexture.h:104
@ mdt_double
Double-precision (32-bit) floating point.
Definition: Ptexture.h:108
@ mdt_int16
Signed 16-bit integer.
Definition: Ptexture.h:105
uint16_t vbordermode
Definition: PtexIO.h:67
uint16_t ubordermode
Definition: PtexIO.h:65
uint32_t lmdheadermemsize
Definition: PtexIO.h:70
uint32_t lmdheaderzipsize
Definition: PtexIO.h:69
void set(size_t blocksizeArg, Encoding encodingArg)
Definition: PtexIO.h:89
uint32_t data
Definition: PtexIO.h:83
uint32_t metadatamemsize
Definition: PtexIO.h:60
uint32_t faceinfosize
Definition: PtexIO.h:54
uint16_t nlevels
Definition: PtexIO.h:51
uint16_t nchannels
Definition: PtexIO.h:50
uint32_t meshtype
Definition: PtexIO.h:47
uint32_t constdatasize
Definition: PtexIO.h:55
uint32_t levelinfosize
Definition: PtexIO.h:56
uint32_t extheadersize
Definition: PtexIO.h:53
uint32_t minorversion
Definition: PtexIO.h:57
uint32_t metadatazipsize
Definition: PtexIO.h:59
uint32_t datatype
Definition: PtexIO.h:48
int pixelSize() const
Definition: PtexIO.h:61
int32_t alphachan
Definition: PtexIO.h:49
uint32_t magic
Definition: PtexIO.h:45
uint32_t nfaces
Definition: PtexIO.h:52
uint64_t leveldatasize
Definition: PtexIO.h:58
uint32_t version
Definition: PtexIO.h:46
bool hasAlpha() const
Definition: PtexIO.h:62
std::vector< std::vector< std::byte > > faceData
Definition: PtexWriter.h:141
std::vector< FaceDataHeader > fdh
Definition: PtexWriter.h:142
std::vector< uint8_t > data
Definition: PtexWriter.h:95
Information about a face, as stored in the Ptex file header.
Definition: Ptexture.h:232
Res res
Resolution of face.
Definition: Ptexture.h:233
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition: Ptexture.h:265
Pixel resolution of a given texture.
Definition: Ptexture.h:159
int8_t ulog2
log base 2 of u resolution, in texels
Definition: Ptexture.h:160
int v() const
V resolution in texels.
Definition: Ptexture.h:176
size_t size64() const
Total size of specified texture in texels (u * v), allowing arbitrarily large textures.
Definition: Ptexture.h:185
int u() const
U resolution in texels.
Definition: Ptexture.h:173
int8_t vlog2
log base 2 of v resolution, in texels
Definition: Ptexture.h:161