Ptex
PtexWriter.cpp
Go to the documentation of this file.
1/*
2PTEX SOFTWARE
3Copyright 2014 Disney Enterprises, Inc. All rights reserved
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
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
22Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33OF 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
57namespace {
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
100PtexWriter* 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
119PtexWriter* 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
166PtexWriter* 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
192bool 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
224void PtexMainWriter::writeMeta(const char* key, const char* value)
225{
226 addMetaData(key, mdt_string, value, int(strlen(value)+1));
227}
228
229
230void PtexMainWriter::writeMeta(const char* key, const int8_t* value, int count)
231{
232 addMetaData(key, mdt_int8, value, count);
233}
234
235
236void 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
242void 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
248void PtexMainWriter::writeMeta(const char* key, const float* value, int count)
249{
250 addMetaData(key, mdt_float, value, count*(int)sizeof(float));
251}
252
253
254void 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
316void PtexMainWriter::addMetaData(const char* key, MetaDataType t,
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
350size_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
361void 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
369libdeflate_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
384void PtexMainWriter::releaseCompressor(libdeflate_compressor* compressor)
385{
387 _compressors.push_back(compressor);
388}
389
390
391void 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
424void 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()),
435 datatype(), _header.nchannels);
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
450void 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
515void 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;
539 _header.minorversion = PtexFileMinorVersion;
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);
546 _pixelSize = _header.pixelSize();
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
576 PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
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
613bool 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,
659 _header.alphachan);
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
695bool 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
709void 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,
714 datatype(), _header.nchannels);
715 if (_header.hasAlpha())
716 PtexUtils::divalpha(constdata, 1, datatype(), _header.nchannels, _header.alphachan);
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);
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
982 if (_extheader.ubordermode != Ptex::m_clamp || _extheader.vbordermode != Ptex::m_clamp) {
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
AutoLock< Mutex > AutoMutex
Definition PtexMutex.h:51
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.
Res calcTileRes(Res faceres)
void releaseCompressor(libdeflate_compressor *compressor)
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)
std::vector< libdeflate_compressor * > _compressors
Definition PtexWriter.h:127
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::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)
std::map< std::string, int > _metamap
Definition PtexWriter.h:125
virtual bool close(Ptex::String &error)
Close the file.
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
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.
void compressDataBlock(libdeflate_compressor *compressor, std::vector< std::byte > &compressedData, const void *data, size_t size)
void setError(const std::string &error)
Definition PtexWriter.h:112
void storeConstValue(int faceid, const void *data, int stride, Res res)
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)
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.
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)
std::string _path
Definition PtexWriter.h:120
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)
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)
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
Definition PtexWriter.h:62
void flagConstantNeighorhoods()
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
libdeflate_compressor * getCompressor()
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
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.
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).
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.
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)
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)
Definition PtexUtils.h:79
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.
Definition Ptexture.h:72
@ dt_float
Single-precision (32-bit) floating point.
Definition Ptexture.h:76
MeshType
Type of base mesh for which the textures are defined.
Definition Ptexture.h:66
@ mt_quad
Mesh is quad-based.
Definition Ptexture.h:68
@ m_clamp
texel access is clamped to border
Definition Ptexture.h:87
void set(size_t blocksizeArg, Encoding encodingArg)
Definition PtexIO.h:89
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