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