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 #include <unistd.h>
46 #include <stddef.h>
47#include <libdeflate.h>
48
49#include "Ptexture.h"
50#include "PtexUtils.h"
51#include "PtexWriter.h"
52
54
55namespace {
56
57 std::string fileError(const char* message, const char* path)
58 {
59 std::stringstream str;
60 str << message << path << "\n" << strerror(errno);
61 return str.str();
62 }
63
64 bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan,
65 Ptex::String& error)
66 {
67 // check to see if given file attributes are valid
68 if (!LittleEndian()) {
69 error = "PtexWriter doesn't currently support big-endian cpu's";
70 return 0;
71 }
72
73 if (mt < Ptex::mt_triangle || mt > Ptex::mt_quad) {
74 error = "PtexWriter error: Invalid mesh type";
75 return 0;
76 }
77
78 if (dt < Ptex::dt_uint8 || dt > Ptex::dt_float) {
79 error = "PtexWriter error: Invalid data type";
80 return 0;
81 }
82
83 if (nchannels <= 0) {
84 error = "PtexWriter error: Invalid number of channels";
85 return 0;
86 }
87
88 if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
89 error = "PtexWriter error: Invalid alpha channel";
90 return 0;
91 }
92
93 return 1;
94 }
95}
96
97
98PtexWriter* PtexWriter::open(const char* path,
100 int nchannels, int alphachan, int nfaces,
101 Ptex::String& error, bool genmipmaps)
102{
103 if (!checkFormat(mt, dt, nchannels, alphachan, error))
104 return 0;
105
106 PtexMainWriter* w = new PtexMainWriter(path, 0,
107 mt, dt, nchannels, alphachan, nfaces,
108 genmipmaps);
109 if (!w->ok(error)) {
110 w->release();
111 return 0;
112 }
113 return w;
114}
115
116
117PtexWriter* PtexWriter::edit(const char* path,
119 int nchannels, int alphachan, int nfaces,
120 Ptex::String& error, bool genmipmaps)
121{
122 if (!checkFormat(mt, dt, nchannels, alphachan, error))
123 return 0;
124
125 // try to open existing file (it might not exist)
126 FILE* fp = fopen(path, "rb+");
127 if (!fp && errno != ENOENT) {
128 error = fileError("Can't open ptex file for update: ", path).c_str();
129 }
130
131 PtexTexture* tex = 0;
132 if (fp) {
133 // got an existing file, close and reopen with PtexReader
134 fclose(fp);
135
136 // open reader for existing file
137 tex = PtexTexture::open(path, error);
138 if (!tex) return 0;
139
140 // make sure header matches
141 bool headerMatch = (mt == tex->meshType() &&
142 dt == tex->dataType() &&
143 nchannels == tex->numChannels() &&
144 alphachan == tex->alphaChannel() &&
145 nfaces == tex->numFaces());
146 if (!headerMatch) {
147 std::stringstream str;
148 str << "PtexWriter::edit error: header doesn't match existing file, "
149 << "conversions not supported";
150 error = str.str().c_str();
151 return 0;
152 }
153 }
154 PtexMainWriter* w = new PtexMainWriter(path, tex, mt, dt, nchannels, alphachan,
155 nfaces, genmipmaps);
156
157 if (!w->ok(error)) {
158 w->release();
159 return 0;
160 }
161 return w;
162}
163
164PtexWriter* PtexWriter::edit(const char* path, bool /*incremental*/,
166 int nchannels, int alphachan, int nfaces,
167 Ptex::String& error, bool genmipmaps)
168{
169 // This function is deprecated
170 return edit(path, mt, dt, nchannels, alphachan, nfaces, error, genmipmaps);
171}
172
174{
175 // This function is obsolete
176 return 1;
177}
178
179
181{
182 Ptex::String error;
183 // close writer if app didn't, and report error if any
184 if (!close(error))
185 std::cerr << error.c_str() << std::endl;
186 delete this;
187}
188
189
190bool PtexMainWriter::storeFaceInfo(int faceid, FaceInfo& f, const FaceInfo& src, int flags)
191{
192 if (faceid < 0 || size_t(faceid) >= _header.nfaces) {
193 setError("PtexWriter error: faceid out of range");
194 return 0;
195 }
196
197 if (_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
198 setError("PtexWriter error: asymmetric face res not supported for triangle textures");
199 return 0;
200 }
201
202 // copy all values
203 f = src;
204
205 // and clear extraneous ones
206 if (_header.meshtype == mt_triangle) {
207 f.flags = 0; // no user-settable flags on triangles
208 f.adjfaces[3] = -1;
209 f.adjedges &= 0x3f; // clear all but bottom six bits
210 }
211 else {
212 // clear non-user-settable flags
213 f.flags &= FaceInfo::flag_subface;
214 }
215
216 // set new flags
217 f.flags |= (uint8_t)flags;
218 return 1;
219}
220
221
222void PtexMainWriter::writeMeta(const char* key, const char* value)
223{
224 addMetaData(key, mdt_string, value, int(strlen(value)+1));
225}
226
227
228void PtexMainWriter::writeMeta(const char* key, const int8_t* value, int count)
229{
230 addMetaData(key, mdt_int8, value, count);
231}
232
233
234void PtexMainWriter::writeMeta(const char* key, const int16_t* value, int count)
235{
236 addMetaData(key, mdt_int16, value, count*(int)sizeof(int16_t));
237}
238
239
240void PtexMainWriter::writeMeta(const char* key, const int32_t* value, int count)
241{
242 addMetaData(key, mdt_int32, value, count*(int)sizeof(int32_t));
243}
244
245
246void PtexMainWriter::writeMeta(const char* key, const float* value, int count)
247{
248 addMetaData(key, mdt_float, value, count*(int)sizeof(float));
249}
250
251
252void PtexMainWriter::writeMeta(const char* key, const double* value, int count)
253{
254 addMetaData(key, mdt_double, value, count*(int)sizeof(double));
255}
256
257
259{
260 int nkeys = data->numKeys();
261 for (int i = 0; i < nkeys; i++) {
262 const char* key = 0;
263 MetaDataType type;
264 data->getKey(i, key, type);
265 int count;
266 switch (type) {
267 case mdt_string:
268 {
269 const char* val=0;
270 data->getValue(key, val);
271 writeMeta(key, val);
272 }
273 break;
274 case mdt_int8:
275 {
276 const int8_t* val=0;
277 data->getValue(key, val, count);
278 writeMeta(key, val, count);
279 }
280 break;
281 case mdt_int16:
282 {
283 const int16_t* val=0;
284 data->getValue(key, val, count);
285 writeMeta(key, val, count);
286 }
287 break;
288 case mdt_int32:
289 {
290 const int32_t* val=0;
291 data->getValue(key, val, count);
292 writeMeta(key, val, count);
293 }
294 break;
295 case mdt_float:
296 {
297 const float* val=0;
298 data->getValue(key, val, count);
299 writeMeta(key, val, count);
300 }
301 break;
302 case mdt_double:
303 {
304 const double* val=0;
305 data->getValue(key, val, count);
306 writeMeta(key, val, count);
307 }
308 break;
309 }
310 }
311}
312
313
314void PtexMainWriter::addMetaData(const char* key, MetaDataType t,
315 const void* value, int size)
316{
317 if (strlen(key) > 255) {
318 std::stringstream str;
319 str << "PtexWriter error: meta data key too long (max=255) \"" << key << "\"";
320 setError(str.str());
321 return;
322 }
323 if (size <= 0) {
324 std::stringstream str;
325 str << "PtexWriter error: meta data size <= 0 for \"" << key << "\"";
326 setError(str.str());
327 }
328 std::map<std::string,int>::iterator iter = _metamap.find(key);
329 int index;
330 if (iter != _metamap.end()) {
331 // see if we already have this entry - if so, overwrite it
332 index = iter->second;
333 }
334 else {
335 // allocate a new entry
336 index = (int)_metadata.size();
337 _metadata.resize(index+1);
338 _metamap[key] = index;
339 }
340 MetaEntry& m = _metadata[index];
341 m.key = key;
342 m.datatype = t;
343 m.data.resize(size);
344 memcpy(&m.data[0], value, size);
345}
346
347
348size_t PtexMainWriter::writeBlock(FILE* fp, const void* data, size_t size)
349{
350 if (!_ok) return 0;
351 if (!fwrite(data, size, 1, fp)) {
352 setError("PtexWriter error: file write failed");
353 return 0;
354 }
355 return size;
356}
357
358
359void PtexMainWriter::addToDataBlock(std::vector<std::byte>& dataBlock, const void* data, size_t size)
360{
361 size_t previousSize = dataBlock.size();
362 dataBlock.resize(previousSize+size);
363 memcpy(dataBlock.data() + previousSize, data, size);
364}
365
366
367libdeflate_compressor* PtexMainWriter::getCompressor()
368{
370 libdeflate_compressor* compressor;
371 if (!_compressors.empty()) {
372 compressor = _compressors.back();
373 _compressors.pop_back();
374 } else {
375 const int compressionLevel = 4;
376 compressor = libdeflate_alloc_compressor(compressionLevel);
377 }
378 return compressor;
379}
380
381
382void PtexMainWriter::releaseCompressor(libdeflate_compressor* compressor)
383{
385 _compressors.push_back(compressor);
386}
387
388
389void PtexMainWriter::compressDataBlock(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, const void* data, size_t size)
390{
391 compressedData.resize(libdeflate_zlib_compress_bound(compressor, size));
392 int compressedSize = int(libdeflate_zlib_compress(compressor, data, size, compressedData.data(), compressedData.size()));
393 if (!compressedSize) {
394 setError("PtexWriter error: compression failed");
395 }
396 compressedData.resize(compressedSize);
397}
398
399
401{
402 // desired number of tiles = floor(log2(facesize / tilesize))
403 size_t facesize = faceres.size64() * _pixelSize;
404 int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
405 if (ntileslog2 == 0) return faceres;
406
407 // number of tiles is defined as:
408 // ntileslog2 = ureslog2 + vreslog2 - (tile_ureslog2 + tile_vreslog2)
409 // rearranging to solve for the tile res:
410 // tile_ureslog2 + tile_vreslog2 = ureslog2 + vreslog2 - ntileslog2
411 int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
412
413 // choose u and v sizes for roughly square result (u ~= v ~= n/2)
414 // and make sure tile isn't larger than face
415 Res tileres;
416 tileres.ulog2 = (int8_t)std::min(int((n+1)/2), int(faceres.ulog2));
417 tileres.vlog2 = (int8_t)std::min(int(n - tileres.ulog2), int(faceres.vlog2));
418 return tileres;
419}
420
421
422void PtexMainWriter::compressFaceDataBlock(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, FaceDataHeader& fdh,
423 Res res, const void* uncompressedData, int stride)
424{
425 // compress a single face data block; could be a whole face or just a tile
426
427 // first, copy to temp buffer, and deinterleave
428 int ures = res.u(), vres = res.v();
429 size_t tempSize = ures*vres*_pixelSize;
430 std::vector<std::byte> temp(tempSize);
431 PtexUtils::deinterleave(uncompressedData, stride, ures, vres, temp.data(),
432 ures*DataSize(datatype()),
433 datatype(), _header.nchannels);
434
435 // difference if needed
436 bool diff = (datatype() == dt_uint8 ||
437 datatype() == dt_uint16);
438 if (diff) PtexUtils::encodeDifference(temp.data(), tempSize, datatype());
439
440 // compress
441 compressDataBlock(compressor, compressedData, temp.data(), tempSize);
442
443 // record compressed size and encoding in data header
444 fdh.set(compressedData.size(), diff ? enc_diffzipped : enc_zipped);
445}
446
447
448void PtexMainWriter::compressFaceData(libdeflate_compressor* compressor, std::vector<std::byte>& compressedData, FaceDataHeader& fdh,
449 Res res, const void* uncompressedData)
450{
451 // determine whether to break into tiles
452 int stride = res.u() * _pixelSize;
453 Res tileRes = calcTileRes(res);
454 size_t ntilesu = res.ntilesu(tileRes);
455 size_t ntilesv = res.ntilesv(tileRes);
456 size_t ntiles = ntilesu * ntilesv;
457 if (ntiles == 1) {
458 // output single block
459 compressFaceDataBlock(compressor, compressedData, fdh, res, uncompressedData, stride);
460 } else {
461 // alloc tiles
462 std::vector<std::vector<std::byte>> tiles(ntiles);
463 std::vector<FaceDataHeader> tileHeader(ntiles);
464 size_t tileures = tileRes.u();
465 size_t tilevres = tileRes.v();
466 size_t tileustride = tileures*_pixelSize;
467 size_t tilevstride = tilevres*stride;
468
469 // compress tiles
470 std::vector<std::byte>* tile = tiles.data();
471 FaceDataHeader* tdh = tileHeader.data();
472 const std::byte* rowp = reinterpret_cast<const std::byte*>(uncompressedData);
473 const std::byte* rowpend = rowp + ntilesv * tilevstride;
474 for (; rowp != rowpend; rowp += tilevstride) {
475 const std::byte* p = rowp;
476 const std::byte* pend = p + ntilesu * tileustride;
477 for (; p != pend; tile++, tdh++, p += tileustride) {
478 // determine if tile is constant
479 if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize)) {
480 // output a const tile
481 tile->assign(p, p + _pixelSize);
482 tdh->set(_pixelSize, enc_constant);
483 } else {
484 // output a compressed tile
485 compressFaceDataBlock(compressor, *tile, *tdh, tileRes, p, stride);
486 }
487 }
488 }
489
490 // compress tile header
491 std::vector<std::byte> compressedTileHeader;
492 compressDataBlock(compressor, compressedTileHeader, reinterpret_cast<std::byte*>(tileHeader.data()),
493 ntiles * sizeof(FaceDataHeader));
494 uint32_t compressedTileHeaderSize = compressedTileHeader.size();
495
496 size_t totalSize = sizeof(tileRes) + sizeof(compressedTileHeaderSize) + compressedTileHeaderSize;
497 for (auto& tile : tiles) {
498 totalSize += tile.size();
499 }
500 compressedData.reserve(totalSize);
501 addToDataBlock(compressedData, &tileRes, sizeof(tileRes));
502 addToDataBlock(compressedData, &compressedTileHeaderSize, sizeof(compressedTileHeaderSize));
503 addToDataBlock(compressedData, compressedTileHeader.data(), compressedTileHeaderSize);
504 for (auto& tile : tiles) {
505 addToDataBlock(compressedData, tile.data(), tile.size());
506 }
507
508 fdh.set(totalSize, enc_tiled);
509 }
510}
511
512
513void PtexMainWriter::addToMetaDataBlock(std::vector<std::byte>& metaDataBlock, const MetaEntry& val)
514{
515 uint8_t keysize = uint8_t(val.key.size()+1);
516 uint8_t datatype = val.datatype;
517 uint32_t datasize = uint32_t(val.data.size());
518 addToDataBlock(metaDataBlock, &keysize, sizeof(keysize));
519 addToDataBlock(metaDataBlock, val.key.c_str(), keysize);
520 addToDataBlock(metaDataBlock, &datatype, sizeof(datatype));
521 addToDataBlock(metaDataBlock, &datasize, sizeof(datasize));
522 addToDataBlock(metaDataBlock, &val.data[0], datasize);
523}
524
525
528 int nchannels, int alphachan, int nfaces, bool genmipmaps)
529 : _ok(true),
530 _path(path),
531 _genmipmaps(genmipmaps),
532 _reader(0)
533{
534 memset(&_header, 0, sizeof(_header));
535 _header.magic = Magic;
537 _header.minorversion = PtexFileMinorVersion;
538 _header.meshtype = mt;
539 _header.datatype = dt;
540 _header.alphachan = alphachan;
541 _header.nchannels = (uint16_t)nchannels;
542 _header.nfaces = nfaces;
543 _header.extheadersize = sizeof(_extheader);
544 _pixelSize = _header.pixelSize();
545
546 memset(&_extheader, 0, sizeof(_extheader));
547
548 if (mt == mt_triangle)
550 else
552
553 // data will be written to a ".new" path and then renamed to final location
554 _newpath = path; _newpath += ".new";
555
556 // init faceinfo and set flags to -1 to mark as uninitialized
557 _faceinfo.resize(nfaces);
558 for (int i = 0; i < nfaces; i++) _faceinfo[i].flags = uint8_t(-1);
559 _faces.resize(nfaces);
560 _constdata.resize(nfaces*_pixelSize);
561
562 if (tex) {
563 // access reader implementation
564 // Note: we can assume we have a PtexReader because we opened the tex from the cache
565 _reader = static_cast<PtexReader*>(tex);
566
567 // copy border modes
568 setBorderModes(tex->uBorderMode(), tex->vBorderMode());
569
570 // copy edge filter mode
572
573 // copy meta data from existing file
574 PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
575 writeMeta(meta);
576 }
577}
578
579
581{
582 for (libdeflate_compressor* compressor : _compressors) {
583 libdeflate_free_compressor(compressor);
584 }
585 if (_reader) _reader->release();
586}
587
588
590{
591 if (_ok) finish();
592 if (_reader) {
593 if (!_reader->ok()) {
594 _ok = false;
595 }
596 _reader->release();
597 _reader = 0;
598 }
599 if (_ok) {
600 // rename temppath into final location
601 unlink(_path.c_str());
602 if (rename(_newpath.c_str(), _path.c_str()) == -1) {
603 setError(fileError("Can't write to ptex file: ", _path.c_str()).c_str());
604 unlink(_newpath.c_str());
605 }
606 }
607 if (!_ok) getError(error);
608 return _ok;
609}
610
611bool PtexMainWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
612{
613 if (!_ok) return false;
614
615 FaceRec& face = _faces[faceid];
616 AutoMutex lock(face.mutex);
617
618 // reset face data in case it was written previously
619 face.faceData.clear();
620 face.fdh.clear();
621
622 // auto-compute stride
623 if (stride == 0) stride = f.res.u()*_pixelSize;
624
625 // handle constant case
626 if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize)) {
627 return writeConstantFace(faceid, f, data);
628 }
629
630 // non-constant case, ...
631
632 // check and store face info
633 if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return false;
634
635 // determine how many mipmap levels for face
636 int nlevels = 1;
637 if (_genmipmaps) {
638 nlevels += std::max(0, std::min(f.res.ulog2, f.res.vlog2) - MinReductionLog2);
639 }
640 face.faceData.resize(nlevels);
641 face.fdh.resize(nlevels);
642
643 // copy data into face level 0
644 Ptex::Res res = f.res;
645 size_t rowlen = res.u() * _pixelSize, nrows = res.v();
646 face.faceData[0].resize(rowlen * nrows);
647 PtexUtils::copy(data, stride, face.faceData[0].data(), rowlen, nrows, rowlen);
648 data = face.faceData[0].data();
649 stride = rowlen;
650
651 // premultiply into temp copy (if needed)
652 std::vector<std::byte> premultData;
653 if (_header.hasAlpha()) {
654 // copy to temp buffer, and premultiply alpha
655 premultData = face.faceData[0];
656 PtexUtils::multalpha(premultData.data(), res.size64(), datatype(), _header.nchannels,
657 _header.alphachan);
658 data = premultData.data();
659 }
660
661 // generate reductions (as needed)
662 libdeflate_compressor* compressor = getCompressor();
663 for (int level = 1; level < nlevels; level++) {
664 Ptex::Res nextres((int8_t)(res.ulog2-1), (int8_t)(res.vlog2-1));
665 face.faceData[level].resize(nextres.size64() * _pixelSize);
666 int dstride = nextres.u() * _pixelSize;
667 _reduceFn(data, stride, res.u(), res.v(), face.faceData[level].data(), dstride, datatype(), _header.nchannels);
668 data = face.faceData[level].data();
669 stride = dstride;
670 res = nextres;
671 }
672
673 // compute and store constant value from last level (note: level 0 would be more accurate, but slower)
674 storeConstValue(faceid, data, stride, res);
675
676 // free premultData (if allocated) as it is no longer needed
677 premultData.clear();
678 premultData.shrink_to_fit();
679
680 // compress face data for each level
681 for (int level = 0; level < nlevels; level++) {
682 Ptex::Res res((int8_t)(f.res.ulog2-level), (int8_t)(f.res.vlog2-level));
683 std::vector<std::byte> compressedData;
684 compressFaceData(compressor, compressedData, face.fdh[level], res, face.faceData[level].data());
685 face.faceData[level] = std::move(compressedData);
686 }
687 releaseCompressor(compressor);
688
689 return true;
690}
691
692
693bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
694{
695 if (!_ok) return 0;
696
697 // check and store face info
698 if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
699
700 // store face value in constant block
701 memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
702 return 1;
703}
704
705
706
707void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
708{
709 // compute average value and store in _constdata block
710 std::byte* constdata = &_constdata[faceid*_pixelSize];
711 PtexUtils::average(data, stride, res.u(), res.v(), constdata,
712 datatype(), _header.nchannels);
713 if (_header.hasAlpha())
714 PtexUtils::divalpha(constdata, 1, datatype(), _header.nchannels, _header.alphachan);
715}
716
717
718
720{
721 uint32_t nfaces = _header.nfaces;
722 // copy missing faces from _reader
723 if (_reader) {
724 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
725 if (_faceinfo[faceid].flags == uint8_t(-1)) {
726 // copy constant data
727 memcpy(&_constdata[faceid*_pixelSize], _reader->getConstantData(faceid), _pixelSize);
728
729 // update faceinfo and copy face data
730 const Ptex::FaceInfo& info = _reader->getFaceInfo(faceid);
731 if (info.isConstant()) {
732 storeFaceInfo(faceid, _faceinfo[faceid], info, FaceInfo::flag_constant);
733 } else if (_genmipmaps && !_reader->hasMipMaps()) {
734 // read uncompressed data and generate mipmaps
735 size_t size = _pixelSize * info.res.size64();
736 char* data = new char [size];
737 _reader->getData(faceid, data, 0);
738 writeFace(faceid, info, data, 0);
739 delete [] data;
740 } else {
741 // read compressed data, including mipmaps if any
742 FaceRec& face = _faces[faceid];
743 storeFaceInfo(faceid, _faceinfo[faceid], info);
744 int nlevels = 1;
745 if (_genmipmaps) {
746 nlevels += std::max(0, std::min(info.res.ulog2, info.res.vlog2) - MinReductionLog2);
747 }
748 face.fdh.resize(nlevels);
749 face.faceData.resize(nlevels);
750 for (int level = 0; level < nlevels; level++) {
751 _reader->getCompressedData(faceid, level, face.fdh[level], face.faceData[level]);
752 }
753 }
754 }
755 }
756 }
757 else {
758 // just flag missing faces as constant (black)
759 for (uint32_t faceid = 0; faceid < nfaces; faceid++) {
760 if (_faceinfo[faceid].flags == uint8_t(-1))
761 _faceinfo[faceid].flags = FaceInfo::flag_constant;
762 }
763 }
764
765 // generate "rfaceids", reduction faceids, which are faceids reordered by decreasing smaller dimension
766 if (_genmipmaps) {
767 _rfaceids.resize(nfaces);
768 _faceids_r.resize(nfaces);
770 }
771
772 // flag faces w/ constant neighborhoods
774
775 // compress face info block
776 std::vector<std::byte> compressedFaceInfo;
777 libdeflate_compressor* compressor = getCompressor();
778 compressDataBlock(compressor, compressedFaceInfo, _faceinfo.data(), _faceinfo.size()*sizeof(FaceInfo));
779
780 // compress const data block
781 std::vector<std::byte> compressedConstData;
782 compressDataBlock(compressor, compressedConstData, _constdata.data(), _constdata.size());
783
784 // create level info and compress level headers
785 std::vector<LevelInfo> levelInfo(1);
786 for (auto& face : _faces) {
787 size_t nlevelsThisFace = face.faceData.size();
788 if (nlevelsThisFace > levelInfo.size()) {
789 levelInfo.resize(nlevelsThisFace);
790 }
791 for (size_t level = 0; level < nlevelsThisFace; level++) {
792 levelInfo[level].leveldatasize += face.faceData[level].size();
793 levelInfo[level].nfaces++;
794 }
795 }
796 levelInfo[0].nfaces = _header.nfaces; // constant faces weren't counted in the loop above, but level 0 needs them
797
798 int nlevels = int(levelInfo.size());
799
800 // gather fdh for faces in each level into LevelInfo, and compress level data headers
801 std::vector<std::vector<std::byte>> compressedLevelDataHeaders(nlevels);
802 std::vector<std::vector<size_t>> largeFaceHeaders(nlevels);
803 size_t totalLevelDataSize = 0;
804 for (int level = 0; level < nlevels; level++) {
805 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
806 std::vector<FaceDataHeader> levelDataHeader(nfacesThisLevel);
807 for (uint32_t f = 0; f < nfacesThisLevel; f++) {
808 uint32_t faceId = level==0? f : _faceids_r[f];
809 if (_faces[faceId].fdh.size() > size_t(level)) {
810 levelDataHeader[f] = _faces[faceId].fdh[level];
811 if (levelDataHeader[f].isLargeFace()) {
812 // large faces have a compressed size too big for the fdh; store in largeFaceHeader
813 largeFaceHeaders[level].push_back(_faces[faceId].faceData[level].size());
814 }
815 }
816 }
817 compressDataBlock(compressor, compressedLevelDataHeaders[level], levelDataHeader.data(), nfacesThisLevel * sizeof(FaceDataHeader));
818 levelInfo[level].levelheadersize = uint32_t(compressedLevelDataHeaders[level].size());
819 levelInfo[level].leveldatasize += levelInfo[level].levelheadersize + sizeof(size_t) * largeFaceHeaders[level].size();
820 totalLevelDataSize += levelInfo[level].leveldatasize;
821 }
822
823 // compress meta data
824 std::vector<MetaEntry*> lmdEntries; // large meta data items
825 std::vector<std::byte> metaData, compressedMetaData;
826 for (size_t i = 0, n = _metadata.size(); i < n; i++) {
827 MetaEntry& e = _metadata[i];
828 if (e.data.size() > MetaDataThreshold) {
829 // skip large items, but record for later
830 lmdEntries.push_back(&e);
831 } else {
832 // add small item to meta data block
833 addToMetaDataBlock(metaData, e);
834 }
835 }
836 if (!metaData.empty()) {
837 compressDataBlock(compressor, compressedMetaData, metaData.data(), metaData.size());
838 }
839
840 // compress large meta data
841 size_t nLmd = lmdEntries.size();
842 std::vector<std::byte> lmdHeader, compressedLmdHeader;
843 std::vector<std::vector<std::byte>> compressedLargeMetaData(nLmd);
844 if (nLmd > 0) {
845 // compress lmd data items
846 for (size_t i = 0; i < nLmd; i++) {
847 MetaEntry* e= lmdEntries[i];
848 compressDataBlock(compressor, compressedLargeMetaData[i], &e->data[0], e->data.size());
849
850 uint8_t keysize = uint8_t(e->key.size()+1);
851 uint8_t datatype = e->datatype;
852 uint32_t datasize = (uint32_t)e->data.size();
853 uint32_t zipsize = (uint32_t)compressedLargeMetaData[i].size();
854
855 addToDataBlock(lmdHeader, &keysize, sizeof(keysize));
856 addToDataBlock(lmdHeader, e->key.c_str(), keysize);
857 addToDataBlock(lmdHeader, &datatype, sizeof(datatype));
858 addToDataBlock(lmdHeader, &datasize, sizeof(datasize));
859 addToDataBlock(lmdHeader, &zipsize, sizeof(zipsize));
860 }
861 compressDataBlock(compressor, compressedLmdHeader, lmdHeader.data(), lmdHeader.size());
862 }
863 releaseCompressor(compressor);
864 compressor = nullptr;
865
866 // init header
867 _header.nlevels = nlevels;
868 _header.faceinfosize = compressedFaceInfo.size();
869 _header.constdatasize = compressedConstData.size();
870 _header.levelinfosize = LevelInfoSize * nlevels;
871 _header.leveldatasize = totalLevelDataSize;
872 _header.metadatamemsize = uint32_t(metaData.size());
873 _header.metadatazipsize = uint32_t(compressedMetaData.size());
874 _extheader.lmdheadermemsize = lmdHeader.size();
875 _extheader.lmdheaderzipsize = compressedLmdHeader.size();
876
877 // create new file
878 FILE* newfp = fopen(_newpath.c_str(), "wb+");
879 if (!newfp) {
880 setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
881 return;
882 }
883
884 // write header
885 writeBlock(newfp, &_header, HeaderSize);
887
888 // write face info block
889 writeBlock(newfp, compressedFaceInfo.data(), compressedFaceInfo.size());
890
891 // write const data block
892 writeBlock(newfp, compressedConstData.data(), compressedConstData.size());
893
894 // write level info block
895 writeBlock(newfp, &levelInfo[0], LevelInfoSize * nlevels);
896
897 // write level data blocks
898 for (int level = 0; level < nlevels; level++) {
899 // write level data header
900 writeBlock(newfp, compressedLevelDataHeaders[level].data(), compressedLevelDataHeaders[level].size());
901
902 // write large face header, if present
903 if (!largeFaceHeaders[level].empty()) {
904 writeBlock(newfp, largeFaceHeaders[level].data(), sizeof(size_t) * largeFaceHeaders[level].size());
905 }
906
907 // write compressed face data for faces in level
908 uint32_t nfacesThisLevel = levelInfo[level].nfaces;
909 for (uint32_t rfaceId = 0; rfaceId < nfacesThisLevel; rfaceId++) {
910 uint32_t faceId = level==0? rfaceId : _faceids_r[rfaceId];
911 if (_faces[faceId].faceData.size() > size_t(level)) {
912 writeBlock(newfp, _faces[faceId].faceData[level].data(), _faces[faceId].faceData[level].size());
913 }
914 }
915 }
916
917 // write meta data
918 if (!compressedMetaData.empty()) {
919 writeBlock(newfp, compressedMetaData.data(), compressedMetaData.size());
920 }
921
922 // write compatibility barrier
923 uint64_t compatibilityBarrier = 0;
924 writeBlock(newfp, &compatibilityBarrier, sizeof(compatibilityBarrier));
925
926 // write large meta data
927 if (!compressedLmdHeader.empty()) {
928 writeBlock(newfp, compressedLmdHeader.data(), compressedLmdHeader.size());
929 for (auto& lmd : compressedLargeMetaData) {
930 writeBlock(newfp, lmd.data(), lmd.size());
931 }
932 }
933 fclose(newfp);
934}
935
936
938{
939 // for each constant face
940 for (int faceid = 0, n = int(_faceinfo.size()); faceid < n; faceid++) {
941 FaceInfo& f = _faceinfo[faceid];
942 if (!f.isConstant()) continue;
943 std::byte* constdata = &_constdata[faceid*_pixelSize];
944
945 // check to see if neighborhood is constant
946 bool isConst = true;
947 bool isTriangle = _header.meshtype == mt_triangle;
948 int nedges = isTriangle ? 3 : 4;
949 for (int eid = 0; isConst && (eid < nedges); eid++) {
950 bool prevWasSubface = f.isSubface();
951 int prevFid = faceid;
952
953 // traverse around vertex in CW direction
954 int afid = f.adjface(eid);
955 int aeid = f.adjedge(eid);
956 int count = 0;
957 const int maxcount = 10; // max valence (as safety valve)
958 while (afid != faceid && afid >= 0 && ++count < maxcount) {
959 // check if neighbor is constant, and has the same value as face
960 FaceInfo& af = _faceinfo[afid];
961 if (!af.isConstant() ||
962 0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
963 { isConst = false; break; }
964
965 // if vertex is a T vertex between subface and main face, we can stop
966 bool isSubface = af.isSubface();
967 bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
968 if (isT) break;
969 prevWasSubface = isSubface;
970
971 // traverse around vertex in CW direction
972 prevFid = afid;
973 aeid = (aeid + 1) % nedges;
974 afid = af.adjface(aeid);
975 aeid = af.adjedge(aeid);
976 }
977
978 if (afid < 0) {
979 // hit boundary edge, check boundary mode
980 if (_extheader.ubordermode != Ptex::m_clamp || _extheader.vbordermode != Ptex::m_clamp) {
981 isConst = false;
982 }
983
984 // and traverse CCW neighbors too
985 if (isConst) {
986 aeid = (aeid - 1 + nedges) % nedges;
987 afid = f.adjface(aeid);
988 aeid = f.adjedge(aeid);
989 count = 0;
990 while (afid != faceid && afid >= 0 && ++count < maxcount) {
991 // check if neighbor is constant, and has the same value as face
992 FaceInfo& af = _faceinfo[afid];
993 if (!af.isConstant() ||
994 0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
995 { isConst = false; break; }
996
997 // traverse around vertex in CCW direction
998 prevFid = afid;
999 aeid = (aeid - 1 + nedges) % nedges;
1000 afid = af.adjface(aeid);
1001 aeid = af.adjedge(aeid);
1002
1003 // if traversing to a subface, switch to secondary subface (afid points to primary/CW subface)
1004 bool isSubface = af.isSubface();
1005 if (isSubface && !prevWasSubface) {
1006 aeid = (aeid + 3) % 4;
1007 afid = af.adjface(aeid);
1008 aeid = (af.adjedge(aeid) + 3) % 4;
1009 }
1010 prevWasSubface = isSubface;
1011 }
1012 }
1013 }
1014 }
1015 if (isConst) f.flags |= FaceInfo::flag_nbconstant;
1016 }
1017}
1018
1019
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