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/* Ptex writer classes:
37
38 PtexIncrWriter implements "incremental" mode and simply appends
39 "edit" blocks to the end of the file.
40
41 PtexMainWriter implements both writing from scratch and updating
42 an existing file, either to add data or to "roll up" previous
43 incremental edits.
44
45 Because the various headers (faceinfo, levelinfo, etc.) are
46 variable-length and precede the data, and because the data size
47 is not known until it is compressed and written, all data
48 are written to a temp file and then copied at the end to the
49 final location. This happens during the "finish" phase.
50
51 Each time a texture is written to the file, a reduction of the
52 texture is also generated and stored. These reductions are stored
53 in a temporary form and recalled later as the resolution levels are
54 generated.
55
56 The final reduction for each face is averaged and stored in the
57 const data block.
58*/
59
60#include "PtexPlatform.h"
61#include <errno.h>
62#include <signal.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <algorithm>
67#include <iostream>
68#include <sstream>
69#if defined(__FreeBSD__)
70 #include <unistd.h>
71 #include <stddef.h>
72#endif
73
74#include "Ptexture.h"
75#include "PtexUtils.h"
76#include "PtexWriter.h"
77
79
80namespace {
81
82 FILE* OpenTempFile(std::string& tmppath)
83 {
84 static Mutex lock;
85 AutoMutex locker(lock);
86
87 // choose temp dir
88 static std::string tmpdir;
89 static int initialized = 0;
90 if (!initialized) {
91 initialized = 1;
92#ifdef PTEX_PLATFORM_WINDOWS
93 // use GetTempPath API (first call determines length of result)
94 DWORD result = ::GetTempPath(0, (LPTSTR) L"");
95 if (result > 0) {
96 std::vector<TCHAR> tempPath(result + 1);
97 result = ::GetTempPath(static_cast<DWORD>(tempPath.size()), &tempPath[0]);
98 if (result > 0 && result <= tempPath.size())
99 tmpdir = std::string(tempPath.begin(),
100 tempPath.begin() + static_cast<std::size_t>(result));
101 else
102 tmpdir = ".";
103 }
104#else
105 // try $TEMP or $TMP, use /tmp as last resort
106 const char* t = getenv("TEMP");
107 if (!t) t = getenv("TMP");
108 if (!t) t = "/tmp";
109 tmpdir = t;
110#endif
111 }
112
113 // build temp path
114
115#ifdef PTEX_PLATFORM_WINDOWS
116 // use process id and counter to make unique filename
117 std::stringstream s;
118 static int count = 0;
119 s << tmpdir << "/" << "PtexTmp" << _getpid() << "_" << ++count;
120 tmppath = s.str();
121 return fopen((char*) tmppath.c_str(), "wb+");
122#else
123 // use mkstemp to open unique file
124 tmppath = tmpdir + "/PtexTmpXXXXXX";
125 int fd = mkstemp(&tmppath[0]);
126 return fdopen(fd, "w+");
127#endif
128 }
129
130 std::string fileError(const char* message, const char* path)
131 {
132 std::stringstream str;
133 str << message << path << "\n" << strerror(errno);
134 return str.str();
135 }
136
137 bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan,
138 Ptex::String& error)
139 {
140 // check to see if given file attributes are valid
141 if (!LittleEndian()) {
142 error = "PtexWriter doesn't currently support big-endian cpu's";
143 return 0;
144 }
145
146 if (mt < Ptex::mt_triangle || mt > Ptex::mt_quad) {
147 error = "PtexWriter error: Invalid mesh type";
148 return 0;
149 }
150
151 if (dt < Ptex::dt_uint8 || dt > Ptex::dt_float) {
152 error = "PtexWriter error: Invalid data type";
153 return 0;
154 }
155
156 if (nchannels <= 0) {
157 error = "PtexWriter error: Invalid number of channels";
158 return 0;
159 }
160
161 if (alphachan != -1 && (alphachan < 0 || alphachan >= nchannels)) {
162 error = "PtexWriter error: Invalid alpha channel";
163 return 0;
164 }
165
166 return 1;
167 }
168}
169
170
171PtexWriter* PtexWriter::open(const char* path,
173 int nchannels, int alphachan, int nfaces,
174 Ptex::String& error, bool genmipmaps)
175{
176 if (!checkFormat(mt, dt, nchannels, alphachan, error))
177 return 0;
178
179 PtexMainWriter* w = new PtexMainWriter(path, 0,
180 mt, dt, nchannels, alphachan, nfaces,
181 genmipmaps);
182 if (!w->ok(error)) {
183 w->release();
184 return 0;
185 }
186 return w;
187}
188
189
190PtexWriter* PtexWriter::edit(const char* path, bool incremental,
192 int nchannels, int alphachan, int nfaces,
193 Ptex::String& error, bool genmipmaps)
194{
195 if (!checkFormat(mt, dt, nchannels, alphachan, error))
196 return 0;
197
198 // try to open existing file (it might not exist)
199 FILE* fp = fopen(path, "rb+");
200 if (!fp && errno != ENOENT) {
201 error = fileError("Can't open ptex file for update: ", path).c_str();
202 }
203
204 PtexWriterBase* w = 0;
205 // use incremental writer iff incremental mode requested and file exists
206 if (incremental && fp) {
207 w = new PtexIncrWriter(path, fp, mt, dt, nchannels, alphachan, nfaces);
208 }
209 // otherwise use main writer
210 else {
211 PtexTexture* tex = 0;
212 if (fp) {
213 // got an existing file, close and reopen with PtexReader
214 fclose(fp);
215
216 // open reader for existing file
217 tex = PtexTexture::open(path, error);
218 if (!tex) return 0;
219
220 // make sure header matches
221 bool headerMatch = (mt == tex->meshType() &&
222 dt == tex->dataType() &&
223 nchannels == tex->numChannels() &&
224 alphachan == tex->alphaChannel() &&
225 nfaces == tex->numFaces());
226 if (!headerMatch) {
227 std::stringstream str;
228 str << "PtexWriter::edit error: header doesn't match existing file, "
229 << "conversions not currently supported";
230 error = str.str().c_str();
231 return 0;
232 }
233 }
234 w = new PtexMainWriter(path, tex, mt, dt, nchannels, alphachan,
235 nfaces, genmipmaps);
236 }
237
238 if (!w->ok(error)) {
239 w->release();
240 return 0;
241 }
242 return w;
243}
244
245
246bool PtexWriter::applyEdits(const char* path, Ptex::String& error)
247{
248 // open reader for existing file
249 PtexTexture* tex = PtexTexture::open(path, error);
250 if (!tex) return 0;
251
252 // see if we have any edits to apply
253 if (tex->hasEdits()) {
254 // create non-incremental writer
255 PtexPtr<PtexWriter> w(new PtexMainWriter(path, tex, tex->meshType(), tex->dataType(),
256 tex->numChannels(), tex->alphaChannel(), tex->numFaces(),
257 tex->hasMipMaps()));
258 // close to rebuild file
259 if (!w->close(error)) return 0;
260 }
261 return 1;
262}
263
264
267 int nchannels, int alphachan, int nfaces,
268 bool compress)
269 : _ok(true),
270 _path(path),
271 _tilefp(0)
272{
273 memset(&_header, 0, sizeof(_header));
277 _header.meshtype = mt;
278 _header.datatype = dt;
279 _header.alphachan = alphachan;
280 _header.nchannels = (uint16_t)nchannels;
281 _header.nfaces = nfaces;
282 _header.nlevels = 0;
285
286 memset(&_extheader, 0, sizeof(_extheader));
287
288 if (mt == mt_triangle)
290 else
292
293 memset(&_zstream, 0, sizeof(_zstream));
294 deflateInit(&_zstream, compress ? Z_DEFAULT_COMPRESSION : 0);
295
296 // create temp file for writing tiles
297 // (must compress each tile before assembling a tiled face)
298 _tilefp = OpenTempFile(_tilepath);
299 if (!_tilefp) {
300 setError(fileError("Error creating temp file: ", _tilepath.c_str()));
301 }
302}
303
304
306{
307 Ptex::String error;
308 // close writer if app didn't, and report error if any
309 if (_tilefp && !close(error))
310 std::cerr << error.c_str() << std::endl;
311 delete this;
312}
313
315{
316 deflateEnd(&_zstream);
317}
318
319
321{
322 if (_ok) finish();
323 if (!_ok) getError(error);
324 if (_tilefp) {
325 fclose(_tilefp);
326 unlink(_tilepath.c_str());
327 _tilefp = 0;
328 }
329 return _ok;
330}
331
332
333bool PtexWriterBase::storeFaceInfo(int faceid, FaceInfo& f, const FaceInfo& src, int flags)
334{
335 if (faceid < 0 || size_t(faceid) >= _header.nfaces) {
336 setError("PtexWriter error: faceid out of range");
337 return 0;
338 }
339
340 if (_header.meshtype == mt_triangle && (f.res.ulog2 != f.res.vlog2)) {
341 setError("PtexWriter error: asymmetric face res not supported for triangle textures");
342 return 0;
343 }
344
345 // copy all values
346 f = src;
347
348 // and clear extraneous ones
349 if (_header.meshtype == mt_triangle) {
350 f.flags = 0; // no user-settable flags on triangles
351 f.adjfaces[3] = -1;
352 f.adjedges &= 0x3f; // clear all but bottom six bits
353 }
354 else {
355 // clear non-user-settable flags
356 f.flags &= FaceInfo::flag_subface;
357 }
358
359 // set new flags
360 f.flags |= (uint8_t)flags;
361 return 1;
362}
363
364
365void PtexWriterBase::writeMeta(const char* key, const char* value)
366{
367 addMetaData(key, mdt_string, value, int(strlen(value)+1));
368}
369
370
371void PtexWriterBase::writeMeta(const char* key, const int8_t* value, int count)
372{
373 addMetaData(key, mdt_int8, value, count);
374}
375
376
377void PtexWriterBase::writeMeta(const char* key, const int16_t* value, int count)
378{
379 addMetaData(key, mdt_int16, value, count*(int)sizeof(int16_t));
380}
381
382
383void PtexWriterBase::writeMeta(const char* key, const int32_t* value, int count)
384{
385 addMetaData(key, mdt_int32, value, count*(int)sizeof(int32_t));
386}
387
388
389void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
390{
391 addMetaData(key, mdt_float, value, count*(int)sizeof(float));
392}
393
394
395void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
396{
397 addMetaData(key, mdt_double, value, count*(int)sizeof(double));
398}
399
400
402{
403 int nkeys = data->numKeys();
404 for (int i = 0; i < nkeys; i++) {
405 const char* key = 0;
406 MetaDataType type;
407 data->getKey(i, key, type);
408 int count;
409 switch (type) {
410 case mdt_string:
411 {
412 const char* val=0;
413 data->getValue(key, val);
414 writeMeta(key, val);
415 }
416 break;
417 case mdt_int8:
418 {
419 const int8_t* val=0;
420 data->getValue(key, val, count);
421 writeMeta(key, val, count);
422 }
423 break;
424 case mdt_int16:
425 {
426 const int16_t* val=0;
427 data->getValue(key, val, count);
428 writeMeta(key, val, count);
429 }
430 break;
431 case mdt_int32:
432 {
433 const int32_t* val=0;
434 data->getValue(key, val, count);
435 writeMeta(key, val, count);
436 }
437 break;
438 case mdt_float:
439 {
440 const float* val=0;
441 data->getValue(key, val, count);
442 writeMeta(key, val, count);
443 }
444 break;
445 case mdt_double:
446 {
447 const double* val=0;
448 data->getValue(key, val, count);
449 writeMeta(key, val, count);
450 }
451 break;
452 }
453 }
454}
455
456
457void PtexWriterBase::addMetaData(const char* key, MetaDataType t,
458 const void* value, int size)
459{
460 if (strlen(key) > 255) {
461 std::stringstream str;
462 str << "PtexWriter error: meta data key too long (max=255) \"" << key << "\"";
463 setError(str.str());
464 return;
465 }
466 if (size <= 0) {
467 std::stringstream str;
468 str << "PtexWriter error: meta data size <= 0 for \"" << key << "\"";
469 setError(str.str());
470 }
471 std::map<std::string,int>::iterator iter = _metamap.find(key);
472 int index;
473 if (iter != _metamap.end()) {
474 // see if we already have this entry - if so, overwrite it
475 index = iter->second;
476 }
477 else {
478 // allocate a new entry
479 index = (int)_metadata.size();
480 _metadata.resize(index+1);
481 _metamap[key] = index;
482 }
483 MetaEntry& m = _metadata[index];
484 m.key = key;
485 m.datatype = t;
486 m.data.resize(size);
487 memcpy(&m.data[0], value, size);
488}
489
490
491int PtexWriterBase::writeBlank(FILE* fp, int size)
492{
493 if (!_ok) return 0;
494 static char zeros[BlockSize] = {0};
495 int remain = size;
496 while (remain > 0) {
497 remain -= writeBlock(fp, zeros, remain < BlockSize ? remain : BlockSize);
498 }
499 return size;
500}
501
502
503int PtexWriterBase::writeBlock(FILE* fp, const void* data, int size)
504{
505 if (!_ok) return 0;
506 if (!fwrite(data, size, 1, fp)) {
507 setError("PtexWriter error: file write failed");
508 return 0;
509 }
510 return size;
511}
512
513
514int PtexWriterBase::writeZipBlock(FILE* fp, const void* data, int size, bool finishArg)
515{
516 if (!_ok) return 0;
517 void* buff = alloca(BlockSize);
518 _zstream.next_in = (Bytef*) const_cast<void*>(data);
519 _zstream.avail_in = size;
520
521 while (1) {
522 _zstream.next_out = (Bytef*)buff;
523 _zstream.avail_out = BlockSize;
524 int zresult = deflate(&_zstream, finishArg ? Z_FINISH : Z_NO_FLUSH);
525 int sizeval = BlockSize - _zstream.avail_out;
526 if (sizeval > 0) writeBlock(fp, buff, sizeval);
527 if (zresult == Z_STREAM_END) break;
528 if (zresult != Z_OK) {
529 setError("PtexWriter error: data compression internal error");
530 break;
531 }
532 if (!finishArg && _zstream.avail_out != 0)
533 // waiting for more input
534 break;
535 }
536
537 if (!finishArg) return 0;
538
539 int total = (int)_zstream.total_out;
540 deflateReset(&_zstream);
541 return total;
542}
543
544
545int PtexWriterBase::readBlock(FILE* fp, void* data, int size)
546{
547 if (!fread(data, size, 1, fp)) {
548 setError("PtexWriter error: temp file read failed");
549 return 0;
550 }
551 return size;
552}
553
554
555int PtexWriterBase::copyBlock(FILE* dst, FILE* src, FilePos pos, int size)
556{
557 if (size <= 0) return 0;
558 fseeko(src, pos, SEEK_SET);
559 int remain = size;
560 void* buff = alloca(BlockSize);
561 while (remain) {
562 int nbytes = remain < BlockSize ? remain : BlockSize;
563 if (!fread(buff, nbytes, 1, src)) {
564 setError("PtexWriter error: temp file read failed");
565 return 0;
566 }
567 if (!writeBlock(dst, buff, nbytes)) break;
568 remain -= nbytes;
569 }
570 return size;
571}
572
573
575{
576 // desired number of tiles = floor(log2(facesize / tilesize))
577 int facesize = faceres.size() * _pixelSize;
578 int ntileslog2 = PtexUtils::floor_log2(facesize/TileSize);
579 if (ntileslog2 == 0) return faceres;
580
581 // number of tiles is defined as:
582 // ntileslog2 = ureslog2 + vreslog2 - (tile_ureslog2 + tile_vreslog2)
583 // rearranging to solve for the tile res:
584 // tile_ureslog2 + tile_vreslog2 = ureslog2 + vreslog2 - ntileslog2
585 int n = faceres.ulog2 + faceres.vlog2 - ntileslog2;
586
587 // choose u and v sizes for roughly square result (u ~= v ~= n/2)
588 // and make sure tile isn't larger than face
589 Res tileres;
590 tileres.ulog2 = (int8_t)PtexUtils::min(int((n+1)/2), int(faceres.ulog2));
591 tileres.vlog2 = (int8_t)PtexUtils::min(int(n - tileres.ulog2), int(faceres.vlog2));
592 return tileres;
593}
594
595
596void PtexWriterBase::writeConstFaceBlock(FILE* fp, const void* data,
597 FaceDataHeader& fdh)
598{
599 // write a single const face data block
600 // record level data for face and output the one pixel value
602 writeBlock(fp, data, _pixelSize);
603}
604
605
606void PtexWriterBase::writeFaceBlock(FILE* fp, const void* data, int stride,
607 Res res, FaceDataHeader& fdh)
608{
609 // write a single face data block
610 // copy to temp buffer, and deinterleave
611 int ures = res.u(), vres = res.v();
612 int blockSize = ures*vres*_pixelSize;
613 bool useNew = blockSize > AllocaMax;
614 char* buff = useNew ? new char [blockSize] : (char*)alloca(blockSize);
615 PtexUtils::deinterleave(data, stride, ures, vres, buff,
616 ures*DataSize(datatype()),
618
619 // difference if needed
620 bool diff = (datatype() == dt_uint8 ||
621 datatype() == dt_uint16);
622 if (diff) PtexUtils::encodeDifference(buff, blockSize, datatype());
623
624 // compress and stream data to file, and record size in header
625 int zippedsize = writeZipBlock(fp, buff, blockSize);
626
627 // record compressed size and encoding in data header
628 fdh.set(zippedsize, diff ? enc_diffzipped : enc_zipped);
629 if (useNew) delete [] buff;
630}
631
632
633void PtexWriterBase::writeFaceData(FILE* fp, const void* data, int stride,
634 Res res, FaceDataHeader& fdh)
635{
636 // determine whether to break into tiles
637 Res tileres = calcTileRes(res);
638 int ntilesu = res.ntilesu(tileres);
639 int ntilesv = res.ntilesv(tileres);
640 int ntiles = ntilesu * ntilesv;
641 if (ntiles == 1) {
642 // write single block
643 writeFaceBlock(fp, data, stride, res, fdh);
644 } else {
645 // write tiles to tilefp temp file
646 rewind(_tilefp);
647
648 // alloc tile header
649 std::vector<FaceDataHeader> tileHeader(ntiles);
650 int tileures = tileres.u();
651 int tilevres = tileres.v();
652 int tileustride = tileures*_pixelSize;
653 int tilevstride = tilevres*stride;
654
655 // output tiles
656 FaceDataHeader* tdh = &tileHeader[0];
657 int datasize = 0;
658 const char* rowp = (const char*) data;
659 const char* rowpend = rowp + ntilesv * tilevstride;
660 for (; rowp != rowpend; rowp += tilevstride) {
661 const char* p = rowp;
662 const char* pend = p + ntilesu * tileustride;
663 for (; p != pend; tdh++, p += tileustride) {
664 // determine if tile is constant
665 if (PtexUtils::isConstant(p, stride, tileures, tilevres, _pixelSize))
667 else
668 writeFaceBlock(_tilefp, p, stride, tileres, *tdh);
669 datasize += tdh->blocksize();
670 }
671 }
672
673 // output compressed tile header
674 uint32_t tileheadersize = writeZipBlock(_tilefp, &tileHeader[0],
675 int(sizeof(FaceDataHeader)*tileHeader.size()));
676
677
678 // output tile data pre-header
679 int totalsize = 0;
680 totalsize += writeBlock(fp, &tileres, sizeof(Res));
681 totalsize += writeBlock(fp, &tileheadersize, sizeof(tileheadersize));
682
683 // copy compressed tile header from temp file
684 totalsize += copyBlock(fp, _tilefp, datasize, tileheadersize);
685
686 // copy tile data from temp file
687 totalsize += copyBlock(fp, _tilefp, 0, datasize);
688
689 fdh.set(totalsize, enc_tiled);
690 }
691}
692
693
694void PtexWriterBase::writeReduction(FILE* fp, const void* data, int stride, Res res)
695{
696 // reduce and write to file
697 Ptex::Res newres((int8_t)(res.ulog2-1), (int8_t)(res.vlog2-1));
698 int buffsize = newres.size() * _pixelSize;
699 bool useNew = buffsize > AllocaMax;
700 char* buff = useNew ? new char [buffsize] : (char*)alloca(buffsize);
701
702 int dstride = newres.u() * _pixelSize;
703 _reduceFn(data, stride, res.u(), res.v(), buff, dstride, datatype(), _header.nchannels);
704 writeBlock(fp, buff, buffsize);
705
706 if (useNew) delete [] buff;
707}
708
709
710
712{
713 uint8_t keysize = uint8_t(val.key.size()+1);
714 uint8_t datatype = val.datatype;
715 uint32_t datasize = uint32_t(val.data.size());
716 writeZipBlock(fp, &keysize, sizeof(keysize), false);
717 writeZipBlock(fp, val.key.c_str(), keysize, false);
718 writeZipBlock(fp, &datatype, sizeof(datatype), false);
719 writeZipBlock(fp, &datasize, sizeof(datasize), false);
720 writeZipBlock(fp, &val.data[0], datasize, false);
721 int memsize = int(sizeof(keysize) + (size_t)keysize + sizeof(datatype)
722 + sizeof(datasize) + datasize);
723 return memsize;
724}
725
726
729 int nchannels, int alphachan, int nfaces, bool genmipmaps)
730 : PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
731 /* compress */ true),
732 _hasNewData(false),
733 _genmipmaps(genmipmaps),
734 _reader(0)
735{
736 _tmpfp = OpenTempFile(_tmppath);
737 if (!_tmpfp) {
738 setError(fileError("Error creating temp file: ", _tmppath.c_str()));
739 return;
740 }
741
742 // data will be written to a ".new" path and then renamed to final location
743 _newpath = path; _newpath += ".new";
744
745 _levels.reserve(20);
746 _levels.resize(1);
747
748 // init faceinfo and set flags to -1 to mark as uninitialized
749 _faceinfo.resize(nfaces);
750 for (int i = 0; i < nfaces; i++) _faceinfo[i].flags = uint8_t(-1);
751
752 _levels.front().pos.resize(nfaces);
753 _levels.front().fdh.resize(nfaces);
754 _rpos.resize(nfaces);
755 _constdata.resize(nfaces*_pixelSize);
756
757 if (tex) {
758 // access reader implementation
759 // Note: we can assume we have a PtexReader because we opened the tex from the cache
760 _reader = static_cast<PtexReader*>(tex);
761
762 // copy border modes
763 setBorderModes(tex->uBorderMode(), tex->vBorderMode());
764
765 // copy edge filter mode
767
768 // copy meta data from existing file
770 writeMeta(meta);
771
772 // see if we have any edits
774 }
775}
776
777
782
783
785{
786 // closing base writer will write all pending data via finish() method
787 // and will close _fp (which in this case is on the temp disk)
788 bool result = PtexWriterBase::close(error);
789 if (_reader) {
790 _reader->release();
791 _reader = 0;
792 }
793 if (_tmpfp) {
794 fclose(_tmpfp);
795 unlink(_tmppath.c_str());
796 _tmpfp = 0;
797 }
798 if (result && _hasNewData) {
799 // rename temppath into final location
800 unlink(_path.c_str());
801 if (rename(_newpath.c_str(), _path.c_str()) == -1) {
802 error = fileError("Can't write to ptex file: ", _path.c_str()).c_str();
803 unlink(_newpath.c_str());
804 result = false;
805 }
806 }
807 return result;
808}
809
810bool PtexMainWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
811{
812 if (!_ok) return 0;
813
814 // auto-compute stride
815 if (stride == 0) stride = f.res.u()*_pixelSize;
816
817 // handle constant case
818 if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
819 return writeConstantFace(faceid, f, data);
820
821 // non-constant case, ...
822
823 // check and store face info
824 if (!storeFaceInfo(faceid, _faceinfo[faceid], f)) return 0;
825
826 // record position of current face
827 _levels.front().pos[faceid] = ftello(_tmpfp);
828
829 // write face data
830 writeFaceData(_tmpfp, data, stride, f.res, _levels.front().fdh[faceid]);
831 if (!_ok) return 0;
832
833 // premultiply (if needed) before making reductions; use temp copy of data
834 uint8_t* temp = 0;
835 if (_header.hasAlpha()) {
836 // first copy to temp buffer
837 int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
838 temp = new uint8_t [rowlen * nrows];
839 PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
840
841 // multiply alpha
842 PtexUtils::multalpha(temp, f.res.size(), datatype(), _header.nchannels,
844
845 // override source buffer
846 data = temp;
847 stride = rowlen;
848 }
849
850 // generate first reduction (if needed)
851 if (_genmipmaps &&
852 (f.res.ulog2 > MinReductionLog2 && f.res.vlog2 > MinReductionLog2))
853 {
854 _rpos[faceid] = ftello(_tmpfp);
855 writeReduction(_tmpfp, data, stride, f.res);
856 }
857 else {
858 storeConstValue(faceid, data, stride, f.res);
859 }
860
861 if (temp) delete [] temp;
862 _hasNewData = true;
863 return 1;
864}
865
866
867bool PtexMainWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
868{
869 if (!_ok) return 0;
870
871 // check and store face info
872 if (!storeFaceInfo(faceid, _faceinfo[faceid], f, FaceInfo::flag_constant)) return 0;
873
874 // store face value in constant block
875 memcpy(&_constdata[faceid*_pixelSize], data, _pixelSize);
876 _hasNewData = true;
877 return 1;
878}
879
880
881
882void PtexMainWriter::storeConstValue(int faceid, const void* data, int stride, Res res)
883{
884 // compute average value and store in _constdata block
885 uint8_t* constdata = &_constdata[faceid*_pixelSize];
886 PtexUtils::average(data, stride, res.u(), res.v(), constdata,
888 if (_header.hasAlpha())
890}
891
892
893
895{
896 // do nothing if there's no new data to write
897 if (!_hasNewData) return;
898
899 // copy missing faces from _reader
900 if (_reader) {
901 for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
902 if (_faceinfo[i].flags == uint8_t(-1)) {
903 // copy face data
904 const Ptex::FaceInfo& info = _reader->getFaceInfo(i);
905 int size = _pixelSize * info.res.size();
906 if (info.isConstant()) {
908 if (data) {
909 writeConstantFace(i, info, data->getData());
910 }
911 } else {
912 char* data = new char [size];
913 _reader->getData(i, data, 0);
914 writeFace(i, info, data, 0);
915 delete [] data;
916 }
917 }
918 }
919 }
920 else {
921 // just flag missing faces as constant (black)
922 for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) {
923 if (_faceinfo[i].flags == uint8_t(-1))
924 _faceinfo[i].flags = FaceInfo::flag_constant;
925 }
926 }
927
928 // write reductions to tmp file
929 if (_genmipmaps)
931
932 // flag faces w/ constant neighborhoods
934
935 // update header
936 _header.nlevels = uint16_t(_levels.size());
937 _header.nfaces = uint32_t(_faceinfo.size());
938
939 // create new file
940 FILE* newfp = fopen(_newpath.c_str(), "wb+");
941 if (!newfp) {
942 setError(fileError("Can't write to ptex file: ", _newpath.c_str()));
943 return;
944 }
945
946 // write blank header (to fill in later)
947 writeBlank(newfp, HeaderSize);
949
950 // write compressed face info block
952 (int)sizeof(FaceInfo)*_header.nfaces);
953
954 // write compressed const data block
955 _header.constdatasize = writeZipBlock(newfp, &_constdata[0], int(_constdata.size()));
956
957 // write blank level info block (to fill in later)
958 FilePos levelInfoPos = ftello(newfp);
960
961 // write level data blocks (and record level info)
962 std::vector<LevelInfo> levelinfo(_header.nlevels);
963 for (int li = 0; li < _header.nlevels; li++)
964 {
965 LevelInfo& info = levelinfo[li];
966 LevelRec& level = _levels[li];
967 int nfaces = int(level.fdh.size());
968 info.nfaces = nfaces;
969 // output compressed level data header
970 info.levelheadersize = writeZipBlock(newfp, &level.fdh[0],
971 (int)sizeof(FaceDataHeader)*nfaces);
972 info.leveldatasize = info.levelheadersize;
973 // copy level data from tmp file
974 for (int fi = 0; fi < nfaces; fi++)
975 info.leveldatasize += copyBlock(newfp, _tmpfp, level.pos[fi],
976 level.fdh[fi].blocksize());
978 }
979 rewind(_tmpfp);
980
981 // write meta data (if any)
982 if (!_metadata.empty())
983 writeMetaData(newfp);
984
985 // update extheader for edit data position
986 _extheader.editdatapos = ftello(newfp);
987
988 // rewrite level info block
989 fseeko(newfp, levelInfoPos, SEEK_SET);
991
992 // rewrite header
993 fseeko(newfp, 0, SEEK_SET);
994 writeBlock(newfp, &_header, HeaderSize);
996 fclose(newfp);
997}
998
999
1001{
1002 // for each constant face
1003 for (int faceid = 0, n = int(_faceinfo.size()); faceid < n; faceid++) {
1004 FaceInfo& f = _faceinfo[faceid];
1005 if (!f.isConstant()) continue;
1006 uint8_t* constdata = &_constdata[faceid*_pixelSize];
1007
1008 // check to see if neighborhood is constant
1009 bool isConst = true;
1010 bool isTriangle = _header.meshtype == mt_triangle;
1011 int nedges = isTriangle ? 3 : 4;
1012 for (int eid = 0; isConst && (eid < nedges); eid++) {
1013 bool prevWasSubface = f.isSubface();
1014 int prevFid = faceid;
1015
1016 // traverse around vertex in CW direction
1017 int afid = f.adjface(eid);
1018 int aeid = f.adjedge(eid);
1019 int count = 0;
1020 const int maxcount = 10; // max valence (as safety valve)
1021 while (afid != faceid && afid >= 0 && ++count < maxcount) {
1022 // check if neighbor is constant, and has the same value as face
1023 FaceInfo& af = _faceinfo[afid];
1024 if (!af.isConstant() ||
1025 0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
1026 { isConst = false; break; }
1027
1028 // if vertex is a T vertex between subface and main face, we can stop
1029 bool isSubface = af.isSubface();
1030 bool isT = !isTriangle && prevWasSubface && !isSubface && af.adjface(aeid) == prevFid;
1031 if (isT) break;
1032 prevWasSubface = isSubface;
1033
1034 // traverse around vertex in CW direction
1035 prevFid = afid;
1036 aeid = (aeid + 1) % nedges;
1037 afid = af.adjface(aeid);
1038 aeid = af.adjedge(aeid);
1039 }
1040
1041 if (afid < 0) {
1042 // hit boundary edge, check boundary mode
1044 isConst = false;
1045 }
1046
1047 // and traverse CCW neighbors too
1048 if (isConst) {
1049 aeid = (aeid - 1 + nedges) % nedges;
1050 afid = f.adjface(aeid);
1051 aeid = f.adjedge(aeid);
1052 count = 0;
1053 while (afid != faceid && afid >= 0 && ++count < maxcount) {
1054 // check if neighbor is constant, and has the same value as face
1055 FaceInfo& af = _faceinfo[afid];
1056 if (!af.isConstant() ||
1057 0 != memcmp(constdata, &_constdata[afid*_pixelSize], _pixelSize))
1058 { isConst = false; break; }
1059
1060 // traverse around vertex in CCW direction
1061 prevFid = afid;
1062 aeid = (aeid - 1 + nedges) % nedges;
1063 afid = af.adjface(aeid);
1064 aeid = af.adjedge(aeid);
1065
1066 // if traversing to a subface, switch to secondary subface (afid points to primary/CW subface)
1067 bool isSubface = af.isSubface();
1068 if (isSubface && !prevWasSubface) {
1069 aeid = (aeid + 3) % 4;
1070 afid = af.adjface(aeid);
1071 aeid = (af.adjedge(aeid) + 3) % 4;
1072 }
1073 prevWasSubface = isSubface;
1074 }
1075 }
1076 }
1077 }
1078 if (isConst) f.flags |= FaceInfo::flag_nbconstant;
1079 }
1080}
1081
1082
1084{
1085 // first generate "rfaceids", reduction faceids,
1086 // which are faceids reordered by decreasing smaller dimension
1087 int nfaces = _header.nfaces;
1088 _rfaceids.resize(nfaces);
1089 _faceids_r.resize(nfaces);
1091
1092 // determine how many faces in each level, and resize _levels
1093 // traverse in reverse rfaceid order to find number of faces
1094 // larger than cutoff size of each level
1095 for (int rfaceid = nfaces-1, cutoffres = MinReductionLog2; rfaceid >= 0; rfaceid--) {
1096 int faceid = _faceids_r[rfaceid];
1097 FaceInfo& face = _faceinfo[faceid];
1098 Res res = face.res;
1099 int min = face.isConstant() ? 1 : PtexUtils::min(res.ulog2, res.vlog2);
1100 while (min > cutoffres) {
1101 // i == last face for current level
1102 int size = rfaceid+1;
1103 _levels.push_back(LevelRec());
1104 LevelRec& level = _levels.back();
1105 level.pos.resize(size);
1106 level.fdh.resize(size);
1107 cutoffres++;
1108 }
1109 }
1110
1111 // generate and cache reductions (including const data)
1112 // first, find largest face and allocate tmp buffer
1113 int buffsize = 0;
1114 for (int i = 0; i < nfaces; i++)
1115 buffsize = PtexUtils::max(buffsize, _faceinfo[i].res.size());
1116 buffsize *= _pixelSize;
1117 char* buff = new char [buffsize];
1118
1119 int nlevels = int(_levels.size());
1120 for (int i = 1; i < nlevels; i++) {
1121 LevelRec& level = _levels[i];
1122 int nextsize = (i+1 < nlevels)? int(_levels[i+1].fdh.size()) : 0;
1123 for (int rfaceid = 0, size = int(level.fdh.size()); rfaceid < size; rfaceid++) {
1124 // output current reduction for face (previously generated)
1125 int faceid = _faceids_r[rfaceid];
1126 Res res = _faceinfo[faceid].res;
1127 res.ulog2 = (int8_t)(res.ulog2 - i);
1128 res.vlog2 = (int8_t)(res.vlog2 - i);
1129 int stride = res.u() * _pixelSize;
1130 int blocksize = res.size() * _pixelSize;
1131 fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
1132 readBlock(_tmpfp, buff, blocksize);
1133 fseeko(_tmpfp, 0, SEEK_END);
1134 level.pos[rfaceid] = ftello(_tmpfp);
1135 writeFaceData(_tmpfp, buff, stride, res, level.fdh[rfaceid]);
1136 if (!_ok) return;
1137
1138 // write a new reduction if needed for next level
1139 if (rfaceid < nextsize) {
1140 fseeko(_tmpfp, _rpos[faceid], SEEK_SET);
1141 writeReduction(_tmpfp, buff, stride, res);
1142 }
1143 else {
1144 // the last reduction for each face is its constant value
1145 storeConstValue(faceid, buff, stride, res);
1146 }
1147 }
1148 }
1149 fseeko(_tmpfp, 0, SEEK_END);
1150 delete [] buff;
1151}
1152
1153
1155{
1156 std::vector<MetaEntry*> lmdEntries; // large meta data items
1157
1158 // write small meta data items in a single zip block
1159 for (int i = 0, n = (int)_metadata.size(); i < n; i++) {
1160 MetaEntry& e = _metadata[i];
1161#ifndef PTEX_NO_LARGE_METADATA_BLOCKS
1162 if (int(e.data.size()) > MetaDataThreshold) {
1163 // skip large items, but record for later
1164 lmdEntries.push_back(&e);
1165 }
1166 else
1167#endif
1168 {
1169 // add small item to zip block
1171 }
1172 }
1174 // finish zip block
1175 _header.metadatazipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
1176 }
1177
1178 // write compatibility barrier
1179 writeBlank(fp, sizeof(uint64_t));
1180
1181 // write large items as separate blocks
1182 int nLmd = (int)lmdEntries.size();
1183 if (nLmd > 0) {
1184 // write data records to tmp file and accumulate zip sizes for lmd header
1185 std::vector<FilePos> lmdoffset(nLmd);
1186 std::vector<uint32_t> lmdzipsize(nLmd);
1187 for (int i = 0; i < nLmd; i++) {
1188 MetaEntry* e= lmdEntries[i];
1189 lmdoffset[i] = ftello(_tmpfp);
1190 lmdzipsize[i] = writeZipBlock(_tmpfp, &e->data[0], (int)e->data.size());
1191 }
1192
1193 // write lmd header records as single zip block
1194 for (int i = 0; i < nLmd; i++) {
1195 MetaEntry* e = lmdEntries[i];
1196 uint8_t keysize = uint8_t(e->key.size()+1);
1197 uint8_t datatype = e->datatype;
1198 uint32_t datasize = (uint32_t)e->data.size();
1199 uint32_t zipsize = lmdzipsize[i];
1200
1201 writeZipBlock(fp, &keysize, sizeof(keysize), false);
1202 writeZipBlock(fp, e->key.c_str(), keysize, false);
1203 writeZipBlock(fp, &datatype, sizeof(datatype), false);
1204 writeZipBlock(fp, &datasize, sizeof(datasize), false);
1205 writeZipBlock(fp, &zipsize, sizeof(zipsize), false);
1207 (uint32_t)(sizeof(keysize) + (size_t)keysize + sizeof(datatype) +
1208 sizeof(datasize) + sizeof(zipsize));
1209 }
1210 _extheader.lmdheaderzipsize = writeZipBlock(fp, 0, 0, /*finish*/ true);
1211
1212 // copy data records
1213 for (int i = 0; i < nLmd; i++) {
1215 copyBlock(fp, _tmpfp, lmdoffset[i], lmdzipsize[i]);
1216 }
1217 }
1218}
1219
1220
1221PtexIncrWriter::PtexIncrWriter(const char* path, FILE* fp,
1223 int nchannels, int alphachan, int nfaces)
1224 : PtexWriterBase(path, mt, dt, nchannels, alphachan, nfaces,
1225 /* compress */ false),
1226 _fp(fp)
1227{
1228 // note: incremental saves are not compressed (see compress flag above)
1229 // to improve save time in the case where in incremental save is followed by
1230 // a full save (which ultimately it always should be). With a compressed
1231 // incremental save, the data would be compressed twice and decompressed once
1232 // on every save vs. just compressing once.
1233
1234 // make sure existing header matches
1235 if (!fread(&_header, HeaderSize, 1, fp) || _header.magic != Magic) {
1236 std::stringstream str;
1237 str << "Not a ptex file: " << path;
1238 setError(str.str());
1239 return;
1240 }
1241
1242 bool headerMatch = (mt == _header.meshtype &&
1243 dt == datatype() &&
1244 nchannels == _header.nchannels &&
1245 alphachan == int(_header.alphachan) &&
1246 nfaces == int(_header.nfaces));
1247 if (!headerMatch) {
1248 std::stringstream str;
1249 str << "PtexWriter::edit error: header doesn't match existing file, "
1250 << "conversions not currently supported";
1251 setError(str.str());
1252 return;
1253 }
1254
1255 // read extended header
1256 memset(&_extheader, 0, sizeof(_extheader));
1257 if (!fread(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, fp)) {
1258 std::stringstream str;
1259 str << "Error reading extended header: " << path;
1260 setError(str.str());
1261 return;
1262 }
1263
1264 // seek to end of file to append
1265 fseeko(_fp, 0, SEEK_END);
1266}
1267
1268
1272
1273
1274bool PtexIncrWriter::writeFace(int faceid, const FaceInfo& f, const void* data, int stride)
1275{
1276 if (stride == 0) stride = f.res.u()*_pixelSize;
1277
1278 // handle constant case
1279 if (PtexUtils::isConstant(data, stride, f.res.u(), f.res.v(), _pixelSize))
1280 return writeConstantFace(faceid, f, data);
1281
1282 // init headers
1283 uint8_t edittype = et_editfacedata;
1284 uint32_t editsize;
1285 EditFaceDataHeader efdh;
1286 efdh.faceid = faceid;
1287
1288 // check and store face info
1289 if (!storeFaceInfo(faceid, efdh.faceinfo, f))
1290 return 0;
1291
1292 // record position and skip headers
1293 FilePos pos = ftello(_fp);
1294 writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(efdh));
1295
1296 // must compute constant (average) val first
1297 uint8_t* constval = new uint8_t [_pixelSize];
1298
1299 if (_header.hasAlpha()) {
1300 // must premult alpha before averaging
1301 // first copy to temp buffer
1302 int rowlen = f.res.u() * _pixelSize, nrows = f.res.v();
1303 uint8_t* temp = new uint8_t [rowlen * nrows];
1304 PtexUtils::copy(data, stride, temp, rowlen, nrows, rowlen);
1305
1306 // multiply alpha
1307 PtexUtils::multalpha(temp, f.res.size(), datatype(), _header.nchannels,
1309 // average
1310 PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
1312 // unmult alpha
1315 delete [] temp;
1316 }
1317 else {
1318 // average
1319 PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
1321 }
1322 // write const val
1323 writeBlock(_fp, constval, _pixelSize);
1324 delete [] constval;
1325
1326 // write face data
1327 writeFaceData(_fp, data, stride, f.res, efdh.fdh);
1328
1329 // update editsize in header
1330 editsize = (uint32_t)(sizeof(efdh) + (size_t)_pixelSize + efdh.fdh.blocksize());
1331
1332 // rewind and write headers
1333 fseeko(_fp, pos, SEEK_SET);
1334 writeBlock(_fp, &edittype, sizeof(edittype));
1335 writeBlock(_fp, &editsize, sizeof(editsize));
1336 writeBlock(_fp, &efdh, sizeof(efdh));
1337 fseeko(_fp, 0, SEEK_END);
1338 return 1;
1339}
1340
1341
1342bool PtexIncrWriter::writeConstantFace(int faceid, const FaceInfo& f, const void* data)
1343{
1344 // init headers
1345 uint8_t edittype = et_editfacedata;
1346 uint32_t editsize;
1347 EditFaceDataHeader efdh;
1348 efdh.faceid = faceid;
1349 efdh.fdh.set(0, enc_constant);
1350 editsize = (uint32_t)sizeof(efdh) + _pixelSize;
1351
1352 // check and store face info
1353 if (!storeFaceInfo(faceid, efdh.faceinfo, f, FaceInfo::flag_constant))
1354 return 0;
1355
1356 // write headers
1357 writeBlock(_fp, &edittype, sizeof(edittype));
1358 writeBlock(_fp, &editsize, sizeof(editsize));
1359 writeBlock(_fp, &efdh, sizeof(efdh));
1360 // write data
1361 writeBlock(_fp, data, _pixelSize);
1362 return 1;
1363}
1364
1365
1367{
1368 // init headers
1369 uint8_t edittype = et_editmetadata;
1370 uint32_t editsize;
1371 EditMetaDataHeader emdh;
1372 emdh.metadatazipsize = 0;
1373 emdh.metadatamemsize = 0;
1374
1375 // record position and skip headers
1376 FilePos pos = ftello(_fp);
1377 writeBlank(_fp, sizeof(edittype) + sizeof(editsize) + sizeof(emdh));
1378
1379 // write meta data
1380 for (size_t i = 0, n = _metadata.size(); i < n; i++) {
1381 MetaEntry& e = _metadata[i];
1383 }
1384 // finish zip block
1385 emdh.metadatazipsize = writeZipBlock(_fp, 0, 0, /*finish*/ true);
1386
1387 // update headers
1388 editsize = (uint32_t)(sizeof(emdh) + emdh.metadatazipsize);
1389
1390 // rewind and write headers
1391 fseeko(_fp, pos, SEEK_SET);
1392 writeBlock(_fp, &edittype, sizeof(edittype));
1393 writeBlock(_fp, &editsize, sizeof(editsize));
1394 writeBlock(_fp, &emdh, sizeof(emdh));
1395 fseeko(_fp, 0, SEEK_END);
1396}
1397
1398
1400{
1401 // closing base writer will write all pending data via finish() method
1402 bool result = PtexWriterBase::close(error);
1403 if (_fp) {
1404 fclose(_fp);
1405 _fp = 0;
1406 }
1407 return result;
1408}
1409
1410
1412{
1413 // write meta data edit block (if any)
1414 if (!_metadata.empty()) writeMetaDataEdit();
1415
1416 // rewrite extheader for updated editdatasize
1417 if (_extheader.editdatapos) {
1418 _extheader.editdatasize = uint64_t(ftello(_fp)) - _extheader.editdatapos;
1419 fseeko(_fp, HeaderSize, SEEK_SET);
1421 }
1422}
1423
const uint32_t Magic
Definition PtexIO.h:104
const int ExtHeaderSize
Definition PtexIO.h:106
const int AllocaMax
Definition PtexIO.h:116
const int HeaderSize
Definition PtexIO.h:105
const int LevelInfoSize
Definition PtexIO.h:107
const int BlockSize
Definition PtexIO.h:114
bool LittleEndian()
Definition PtexIO.h:119
@ et_editfacedata
Definition PtexIO.h:92
@ et_editmetadata
Definition PtexIO.h:92
const int TileSize
Definition PtexIO.h:115
@ 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:117
Platform-specific classes, functions, and includes.
off_t FilePos
#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
virtual bool close(Ptex::String &error)
Close the file.
PtexIncrWriter(const char *path, FILE *fp, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces)
virtual void finish()
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
virtual ~PtexIncrWriter()
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
void writeMetaDataEdit()
void generateReductions()
std::string _tmppath
Definition PtexWriter.h:155
std::vector< uint8_t > _constdata
Definition PtexWriter.h:160
std::string _newpath
Definition PtexWriter.h:154
std::vector< uint32_t > _rfaceids
Definition PtexWriter.h:161
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:177
virtual void finish()
std::vector< uint32_t > _faceids_r
Definition PtexWriter.h:162
void storeConstValue(int faceid, const void *data, int stride, Res res)
std::vector< FaceInfo > _faceinfo
Definition PtexWriter.h:159
void writeMetaData(FILE *fp)
virtual ~PtexMainWriter()
static const int MinReductionLog2
Definition PtexWriter.h:164
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
std::vector< FilePos > _rpos
Definition PtexWriter.h:175
std::vector< LevelRec > _levels
Definition PtexWriter.h:174
void flagConstantNeighorhoods()
Meta data accessor.
Definition Ptexture.h:328
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:1032
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition PtexReader.h:56
virtual PtexMetaData * getMetaData()
Access meta data.
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
virtual bool hasEdits()
True if the file has edit blocks.
Definition PtexReader.h:98
Interface for reading data from a ptex file.
Definition Ptexture.h:457
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 bool hasMipMaps()=0
True if the file has mipmaps.
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 bool hasEdits()=0
True if the file has edit blocks.
virtual Ptex::BorderMode vBorderMode()=0
Mode for filtering texture access beyond mesh border.
virtual int numChannels()=0
Number of channels stored in file.
std::map< std::string, int > _metamap
Definition PtexWriter.h:122
DataType datatype() const
Definition PtexWriter.h:80
int writeBlank(FILE *fp, int size)
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
Definition PtexWriter.h:57
std::string _path
Definition PtexWriter.h:115
int copyBlock(FILE *dst, FILE *src, FilePos pos, int size)
void setError(const std::string &error)
Definition PtexWriter.h:110
int writeZipBlock(FILE *fp, const void *data, int size, bool finish=true)
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
z_stream_s _zstream
Definition PtexWriter.h:123
virtual void finish()=0
virtual void addMetaData(const char *key, MetaDataType t, const void *value, int size)
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
int writeBlock(FILE *fp, const void *data, int size)
std::string _tilepath
Definition PtexWriter.h:116
virtual bool close(Ptex::String &error)
Close the file.
void writeFaceData(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
int readBlock(FILE *fp, void *data, int size)
std::vector< MetaEntry > _metadata
Definition PtexWriter.h:121
void writeConstFaceBlock(FILE *fp, const void *data, FaceDataHeader &fdh)
int writeMetaDataBlock(FILE *fp, MetaEntry &val)
ExtHeader _extheader
Definition PtexWriter.h:119
PtexUtils::ReduceFn * _reduceFn
Definition PtexWriter.h:125
void writeReduction(FILE *fp, const void *data, int stride, Res res)
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
Definition PtexWriter.h:52
virtual ~PtexWriterBase()
void getError(Ptex::String &error)
Definition PtexWriter.h:75
PtexWriterBase(const char *path, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool compress)
bool ok(Ptex::String &error)
Definition PtexWriter.h:71
void writeFaceBlock(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Res calcTileRes(Res faceres)
Interface for writing data to a ptex file.
Definition Ptexture.h:810
static PtexWriter * edit(const char *path, bool incremental, 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 bool applyEdits(const char *path, Ptex::String &error)
Apply edits to a file.
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:296
const char * c_str() const
Definition Ptexture.h:304
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
FILE * OpenTempFile(std::string &tmppath)
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)
T min(T a, T b)
Definition PtexUtils.h:148
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
void encodeDifference(void *data, int size, DataType dt)
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)
T max(T a, T b)
Definition PtexUtils.h:151
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:69
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
uint32_t faceid
Definition PtexIO.h:94
FaceDataHeader fdh
Definition PtexIO.h:96
FaceInfo faceinfo
Definition PtexIO.h:95
uint32_t metadatazipsize
Definition PtexIO.h:99
uint32_t metadatamemsize
Definition PtexIO.h:100
uint16_t vbordermode
Definition PtexIO.h:67
uint16_t ubordermode
Definition PtexIO.h:65
uint64_t lmddatasize
Definition PtexIO.h:71
uint64_t editdatapos
Definition PtexIO.h:73
uint32_t lmdheadermemsize
Definition PtexIO.h:70
uint32_t lmdheaderzipsize
Definition PtexIO.h:69
uint64_t editdatasize
Definition PtexIO.h:72
void set(uint32_t blocksizeArg, Encoding encodingArg)
Definition PtexIO.h:88
uint32_t blocksize() const
Definition PtexIO.h:84
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
uint32_t levelheadersize
Definition PtexIO.h:77
uint64_t leveldatasize
Definition PtexIO.h:76
uint32_t nfaces
Definition PtexIO.h:78
std::vector< FilePos > pos
Definition PtexWriter.h:171
std::vector< FaceDataHeader > fdh
Definition PtexWriter.h:172
std::vector< uint8_t > data
Definition PtexWriter.h:85
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
Res res
Resolution of face.
Definition Ptexture.h:230
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition Ptexture.h:262
Pixel resolution of a given texture.
Definition Ptexture.h:159
int size() const
Total size of specified texture in texels (u * v).
Definition Ptexture.h:182
int u() const
U resolution in texels.
Definition Ptexture.h:173