Ptex: PtexWriter.cpp Source File

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
78 PTEX_NAMESPACE_BEGIN
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
171 PtexWriter* PtexWriter::open(const char* path,
172 Ptex::MeshType mt, Ptex::DataType dt,
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
190 PtexWriter* PtexWriter::edit(const char* path, bool incremental,
191 Ptex::MeshType mt, Ptex::DataType dt,
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
246 bool 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
265PtexWriterBase::PtexWriterBase(const char* path,
266 Ptex::MeshType mt, Ptex::DataType dt,
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));
274 _header.magic = Magic;
275 _header.version = PtexFileMajorVersion;
276 _header.minorversion = PtexFileMinorVersion;
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;
283 _header.extheadersize = sizeof(_extheader);
284 _pixelSize = _header.pixelSize();
285
286 memset(&_extheader, 0, sizeof(_extheader));
287
288 if (mt == mt_triangle)
289 _reduceFn = &PtexUtils::reduceTri;
290 else
291 _reduceFn = &PtexUtils::reduce;
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
305 void PtexWriterBase::release()
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
314PtexWriterBase::~PtexWriterBase()
315{
316 deflateEnd(&_zstream);
317}
318
319
320 bool PtexWriterBase::close(Ptex::String& error)
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
333 bool 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
365 void PtexWriterBase::writeMeta(const char* key, const char* value)
366{
367 addMetaData(key, mdt_string, value, int(strlen(value)+1));
368}
369
370
371 void PtexWriterBase::writeMeta(const char* key, const int8_t* value, int count)
372{
373 addMetaData(key, mdt_int8, value, count);
374}
375
376
377 void 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
383 void 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
389 void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
390{
391 addMetaData(key, mdt_float, value, count*(int)sizeof(float));
392}
393
394
395 void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
396{
397 addMetaData(key, mdt_double, value, count*(int)sizeof(double));
398}
399
400
401 void PtexWriterBase::writeMeta(PtexMetaData* data)
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
457 void 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
491 int 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
503 int 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
514 int 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
545 int 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
555 int 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
574 Ptex::Res PtexWriterBase::calcTileRes(Res faceres)
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
596 void 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
601 fdh.set(_pixelSize, enc_constant);
602 writeBlock(fp, data, _pixelSize);
603}
604
605
606 void 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()),
617 datatype(), _header.nchannels);
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
633 void 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))
666 writeConstFaceBlock(_tilefp, p, *tdh);
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
694 void 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
711 int PtexWriterBase::writeMetaDataBlock(FILE* fp, MetaEntry& val)
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
727PtexMainWriter::PtexMainWriter(const char* path, PtexTexture* tex,
728 Ptex::MeshType mt, Ptex::DataType dt,
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
766 setEdgeFilterMode(tex->edgeFilterMode());
767
768 // copy meta data from existing file
769 PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
770 writeMeta(meta);
771
772 // see if we have any edits
773 _hasNewData = _reader->hasEdits();
774 }
775}
776
777
778PtexMainWriter::~PtexMainWriter()
779{
780 if (_reader) _reader->release();
781}
782
783
784 bool PtexMainWriter::close(Ptex::String& error)
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
810 bool 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,
843 _header.alphachan);
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
867 bool 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
882 void 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,
887 datatype(), _header.nchannels);
888 if (_header.hasAlpha())
889 PtexUtils::divalpha(constdata, 1, datatype(), _header.nchannels, _header.alphachan);
890}
891
892
893
894 void PtexMainWriter::finish()
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()) {
907 PtexPtr<PtexFaceData> data ( _reader->getData(i) );
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)
930 generateReductions();
931
932 // flag faces w/ constant neighborhoods
933 flagConstantNeighorhoods();
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);
948 writeBlank(newfp, ExtHeaderSize);
949
950 // write compressed face info block
951 _header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0],
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);
959 writeBlank(newfp, LevelInfoSize * _header.nlevels);
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());
977 _header.leveldatasize += info.leveldatasize;
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);
990 _header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels);
991
992 // rewrite header
993 fseeko(newfp, 0, SEEK_SET);
994 writeBlock(newfp, &_header, HeaderSize);
995 writeBlock(newfp, &_extheader, ExtHeaderSize);
996 fclose(newfp);
997}
998
999
1000 void PtexMainWriter::flagConstantNeighorhoods()
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
1043 if (_extheader.ubordermode != Ptex::m_clamp || _extheader.vbordermode != Ptex::m_clamp) {
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
1083 void PtexMainWriter::generateReductions()
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);
1090 PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
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
1154 void PtexMainWriter::writeMetaData(FILE* fp)
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
1170 _header.metadatamemsize += writeMetaDataBlock(fp, e);
1171 }
1172 }
1173 if (_header.metadatamemsize) {
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);
1206 _extheader.lmdheadermemsize +=
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++) {
1214 _extheader.lmddatasize +=
1215 copyBlock(fp, _tmpfp, lmdoffset[i], lmdzipsize[i]);
1216 }
1217 }
1218}
1219
1220
1221PtexIncrWriter::PtexIncrWriter(const char* path, FILE* fp,
1222 Ptex::MeshType mt, Ptex::DataType dt,
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
1269PtexIncrWriter::~PtexIncrWriter()
1270{
1271}
1272
1273
1274 bool 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,
1308 _header.alphachan);
1309 // average
1310 PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
1311 datatype(), _header.nchannels);
1312 // unmult alpha
1313 PtexUtils::divalpha(constval, 1, datatype(), _header.nchannels,
1314 _header.alphachan);
1315 delete [] temp;
1316 }
1317 else {
1318 // average
1319 PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
1320 datatype(), _header.nchannels);
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
1342 bool 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
1366 void PtexIncrWriter::writeMetaDataEdit()
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];
1382 emdh.metadatamemsize += writeMetaDataBlock(_fp, e);
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
1399 bool PtexIncrWriter::close(Ptex::String& error)
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
1411 void PtexIncrWriter::finish()
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);
1420 fwrite(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, _fp);
1421 }
1422}
1423
1424 PTEX_NAMESPACE_END
Magic
const uint32_t Magic
Definition PtexIO.h:104
ExtHeaderSize
const int ExtHeaderSize
Definition PtexIO.h:106
AllocaMax
const int AllocaMax
Definition PtexIO.h:116
HeaderSize
const int HeaderSize
Definition PtexIO.h:105
LevelInfoSize
const int LevelInfoSize
Definition PtexIO.h:107
BlockSize
const int BlockSize
Definition PtexIO.h:114
LittleEndian
bool LittleEndian()
Definition PtexIO.h:119
et_editfacedata
@ et_editfacedata
Definition PtexIO.h:92
et_editmetadata
@ et_editmetadata
Definition PtexIO.h:92
TileSize
const int TileSize
Definition PtexIO.h:115
enc_diffzipped
@ enc_diffzipped
Definition PtexIO.h:81
enc_zipped
@ enc_zipped
Definition PtexIO.h:81
enc_constant
@ enc_constant
Definition PtexIO.h:81
enc_tiled
@ enc_tiled
Definition PtexIO.h:81
MetaDataThreshold
const int MetaDataThreshold
Definition PtexIO.h:117
PtexPlatform.h
Platform-specific classes, functions, and includes.
FilePos
off_t FilePos
Definition PtexPlatform.h:101
PtexUtils.h
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
PtexFileMinorVersion
#define PtexFileMinorVersion
Definition PtexVersion.h:41
PtexFileMajorVersion
#define PtexFileMajorVersion
Definition PtexVersion.h:40
PtexWriter.h
Ptexture.h
Public API classes for reading, writing, caching, and filtering Ptex files.
AutoLock
Automatically acquire and release lock within enclosing scope.
Definition PtexMutex.h:43
Mutex
Definition PtexPlatform.h:139
PtexIncrWriter
Definition PtexWriter.h:181
PtexIncrWriter::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:1399
PtexIncrWriter::PtexIncrWriter
PtexIncrWriter(const char *path, FILE *fp, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces)
Definition PtexWriter.cpp:1221
PtexIncrWriter::finish
virtual void finish()
Definition PtexWriter.cpp:1411
PtexIncrWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition PtexWriter.cpp:1342
PtexIncrWriter::_fp
FILE * _fp
Definition PtexWriter.h:197
PtexIncrWriter::~PtexIncrWriter
virtual ~PtexIncrWriter()
Definition PtexWriter.cpp:1269
PtexIncrWriter::writeFace
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition PtexWriter.cpp:1274
PtexIncrWriter::writeMetaDataEdit
void writeMetaDataEdit()
Definition PtexWriter.cpp:1366
PtexMainWriter
Definition PtexWriter.h:129
PtexMainWriter::generateReductions
void generateReductions()
Definition PtexWriter.cpp:1083
PtexMainWriter::_tmppath
std::string _tmppath
Definition PtexWriter.h:155
PtexMainWriter::_constdata
std::vector< uint8_t > _constdata
Definition PtexWriter.h:160
PtexMainWriter::_newpath
std::string _newpath
Definition PtexWriter.h:154
PtexMainWriter::_rfaceids
std::vector< uint32_t > _rfaceids
Definition PtexWriter.h:161
PtexMainWriter::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:784
PtexMainWriter::_genmipmaps
bool _genmipmaps
Definition PtexWriter.h:158
PtexMainWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition PtexWriter.cpp:867
PtexMainWriter::_reader
PtexReader * _reader
Definition PtexWriter.h:177
PtexMainWriter::_tmpfp
FILE * _tmpfp
Definition PtexWriter.h:156
PtexMainWriter::finish
virtual void finish()
Definition PtexWriter.cpp:894
PtexMainWriter::_faceids_r
std::vector< uint32_t > _faceids_r
Definition PtexWriter.h:162
PtexMainWriter::storeConstValue
void storeConstValue(int faceid, const void *data, int stride, Res res)
Definition PtexWriter.cpp:882
PtexMainWriter::_faceinfo
std::vector< FaceInfo > _faceinfo
Definition PtexWriter.h:159
PtexMainWriter::writeMetaData
void writeMetaData(FILE *fp)
Definition PtexWriter.cpp:1154
PtexMainWriter::~PtexMainWriter
virtual ~PtexMainWriter()
Definition PtexWriter.cpp:778
PtexMainWriter::_hasNewData
bool _hasNewData
Definition PtexWriter.h:157
PtexMainWriter::MinReductionLog2
static const int MinReductionLog2
Definition PtexWriter.h:164
PtexMainWriter::writeFace
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition PtexWriter.cpp:810
PtexMainWriter::PtexMainWriter
PtexMainWriter(const char *path, PtexTexture *tex, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool genmipmaps)
Definition PtexWriter.cpp:727
PtexMainWriter::_rpos
std::vector< FilePos > _rpos
Definition PtexWriter.h:175
PtexMainWriter::_levels
std::vector< LevelRec > _levels
Definition PtexWriter.h:174
PtexMainWriter::flagConstantNeighorhoods
void flagConstantNeighorhoods()
Definition PtexWriter.cpp:1000
PtexMetaData
Meta data accessor.
Definition Ptexture.h:328
PtexMetaData::numKeys
virtual int numKeys()=0
Query number of meta data entries stored in file.
PtexMetaData::getValue
virtual void getValue(const char *key, const char *&value)=0
Query the value of a given meta data entry.
PtexMetaData::getKey
virtual void getKey(int index, const char *&key, Ptex::MetaDataType &type)=0
Query the name and type of a meta data entry.
PtexPtr
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1032
PtexReader
Definition PtexReader.h:52
PtexReader::release
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition PtexReader.h:56
PtexReader::getMetaData
virtual PtexMetaData * getMetaData()
Access meta data.
Definition PtexReader.cpp:343
PtexReader::getData
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
Definition PtexReader.cpp:719
PtexReader::getFaceInfo
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
Definition PtexReader.cpp:275
PtexReader::hasEdits
virtual bool hasEdits()
True if the file has edit blocks.
Definition PtexReader.h:98
PtexTexture
Interface for reading data from a ptex file.
Definition Ptexture.h:457
PtexTexture::meshType
virtual Ptex::MeshType meshType()=0
Type of mesh for which texture data is defined.
PtexTexture::uBorderMode
virtual Ptex::BorderMode uBorderMode()=0
Mode for filtering texture access beyond mesh border.
PtexTexture::hasMipMaps
virtual bool hasMipMaps()=0
True if the file has mipmaps.
PtexTexture::numFaces
virtual int numFaces()=0
Number of faces stored in file.
PtexTexture::dataType
virtual Ptex::DataType dataType()=0
Type of data stored in file.
PtexTexture::alphaChannel
virtual int alphaChannel()=0
Index of alpha channel (if any).
PtexTexture::edgeFilterMode
virtual Ptex::EdgeFilterMode edgeFilterMode()=0
Mode for filtering textures across edges.
PtexTexture::open
static PtexTexture * open(const char *path, Ptex::String &error, bool premultiply=0)
Open a ptex file for reading.
Definition PtexReader.cpp:59
PtexTexture::hasEdits
virtual bool hasEdits()=0
True if the file has edit blocks.
PtexTexture::vBorderMode
virtual Ptex::BorderMode vBorderMode()=0
Mode for filtering texture access beyond mesh border.
PtexTexture::numChannels
virtual int numChannels()=0
Number of channels stored in file.
PtexWriterBase
Definition PtexWriter.h:50
PtexWriterBase::_metamap
std::map< std::string, int > _metamap
Definition PtexWriter.h:122
PtexWriterBase::datatype
DataType datatype() const
Definition PtexWriter.h:80
PtexWriterBase::writeBlank
int writeBlank(FILE *fp, int size)
Definition PtexWriter.cpp:491
PtexWriterBase::setEdgeFilterMode
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
Set edge filter mode.
Definition PtexWriter.h:57
PtexWriterBase::_path
std::string _path
Definition PtexWriter.h:115
PtexWriterBase::copyBlock
int copyBlock(FILE *dst, FILE *src, FilePos pos, int size)
Definition PtexWriter.cpp:555
PtexWriterBase::setError
void setError(const std::string &error)
Definition PtexWriter.h:110
PtexWriterBase::writeZipBlock
int writeZipBlock(FILE *fp, const void *data, int size, bool finish=true)
Definition PtexWriter.cpp:514
PtexWriterBase::writeMeta
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
Definition PtexWriter.cpp:365
PtexWriterBase::_zstream
z_stream_s _zstream
Definition PtexWriter.h:123
PtexWriterBase::_ok
bool _ok
Definition PtexWriter.h:113
PtexWriterBase::finish
virtual void finish()=0
PtexWriterBase::addMetaData
virtual void addMetaData(const char *key, MetaDataType t, const void *value, int size)
Definition PtexWriter.cpp:457
PtexWriterBase::release
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition PtexWriter.cpp:305
PtexWriterBase::writeBlock
int writeBlock(FILE *fp, const void *data, int size)
Definition PtexWriter.cpp:503
PtexWriterBase::_tilefp
FILE * _tilefp
Definition PtexWriter.h:117
PtexWriterBase::_tilepath
std::string _tilepath
Definition PtexWriter.h:116
PtexWriterBase::close
virtual bool close(Ptex::String &error)
Close the file.
Definition PtexWriter.cpp:320
PtexWriterBase::writeFaceData
void writeFaceData(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Definition PtexWriter.cpp:633
PtexWriterBase::readBlock
int readBlock(FILE *fp, void *data, int size)
Definition PtexWriter.cpp:545
PtexWriterBase::_metadata
std::vector< MetaEntry > _metadata
Definition PtexWriter.h:121
PtexWriterBase::writeConstFaceBlock
void writeConstFaceBlock(FILE *fp, const void *data, FaceDataHeader &fdh)
Definition PtexWriter.cpp:596
PtexWriterBase::writeMetaDataBlock
int writeMetaDataBlock(FILE *fp, MetaEntry &val)
Definition PtexWriter.cpp:711
PtexWriterBase::_extheader
ExtHeader _extheader
Definition PtexWriter.h:119
PtexWriterBase::_reduceFn
PtexUtils::ReduceFn * _reduceFn
Definition PtexWriter.h:125
PtexWriterBase::writeReduction
void writeReduction(FILE *fp, const void *data, int stride, Res res)
Definition PtexWriter.cpp:694
PtexWriterBase::storeFaceInfo
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
Definition PtexWriter.cpp:333
PtexWriterBase::setBorderModes
virtual void setBorderModes(Ptex::BorderMode uBorderMode, Ptex::BorderMode vBorderMode)
Set border modes.
Definition PtexWriter.h:52
PtexWriterBase::~PtexWriterBase
virtual ~PtexWriterBase()
Definition PtexWriter.cpp:314
PtexWriterBase::getError
void getError(Ptex::String &error)
Definition PtexWriter.h:75
PtexWriterBase::PtexWriterBase
PtexWriterBase(const char *path, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, bool compress)
Definition PtexWriter.cpp:265
PtexWriterBase::_header
Header _header
Definition PtexWriter.h:118
PtexWriterBase::_pixelSize
int _pixelSize
Definition PtexWriter.h:120
PtexWriterBase::ok
bool ok(Ptex::String &error)
Definition PtexWriter.h:71
PtexWriterBase::writeFaceBlock
void writeFaceBlock(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Definition PtexWriter.cpp:606
PtexWriterBase::calcTileRes
Res calcTileRes(Res faceres)
Definition PtexWriter.cpp:574
PtexWriter
Interface for writing data to a ptex file.
Definition Ptexture.h:810
PtexWriter::edit
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.
Definition PtexWriter.cpp:190
PtexWriter::applyEdits
static bool applyEdits(const char *path, Ptex::String &error)
Apply edits to a file.
Definition PtexWriter.cpp:246
PtexWriter::open
static PtexWriter * open(const char *path, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces, Ptex::String &error, bool genmipmaps=true)
Open a new texture file for writing.
Definition PtexWriter.cpp:171
Ptex::String
Memory-managed string.
Definition Ptexture.h:296
Ptex::String::c_str
const char * c_str() const
Definition Ptexture.h:304
PTEX_NAMESPACE_BEGIN
Definition PtexSeparableKernel.cpp:42
PTEX_NAMESPACE_BEGIN::checkFormat
bool checkFormat(Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, Ptex::String &error)
Definition PtexWriter.cpp:137
PTEX_NAMESPACE_BEGIN::OpenTempFile
FILE * OpenTempFile(std::string &tmppath)
Definition PtexWriter.cpp:82
PTEX_NAMESPACE_BEGIN::fileError
std::string fileError(const char *message, const char *path)
Definition PtexWriter.cpp:130
PtexUtils::genRfaceids
void genRfaceids(const FaceInfo *faces, int nfaces, uint32_t *rfaceids, uint32_t *faceids)
Definition PtexUtils.cpp:630
PtexUtils::isConstant
bool isConstant(const void *data, int stride, int ures, int vres, int pixelSize)
Definition PtexUtils.cpp:147
PtexUtils::min
T min(T a, T b)
Definition PtexUtils.h:148
PtexUtils::divalpha
void divalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition PtexUtils.cpp:618
PtexUtils::encodeDifference
void encodeDifference(void *data, int size, DataType dt)
Definition PtexUtils.cpp:251
PtexUtils::reduce
void reduce(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:299
PtexUtils::deinterleave
void deinterleave(const void *src, int sstride, int uw, int vw, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:226
PtexUtils::max
T max(T a, T b)
Definition PtexUtils.h:151
PtexUtils::reduceTri
void reduceTri(const void *src, int sstride, int w, int, void *dst, int dstride, DataType dt, int nchan)
Definition PtexUtils.cpp:406
PtexUtils::copy
void copy(const void *src, int sstride, void *dst, int dstride, int vres, int rowlen)
Definition PtexUtils.cpp:438
PtexUtils::floor_log2
uint32_t floor_log2(uint32_t x)
Definition PtexUtils.h:69
PtexUtils::multalpha
void multalpha(void *data, int npixels, DataType dt, int nchannels, int alphachan)
Definition PtexUtils.cpp:579
PtexUtils::average
void average(const void *src, int sstride, int uw, int vw, void *dst, DataType dt, int nchan)
Definition PtexUtils.cpp:522
Ptex::DataType
DataType
Type of data stored in texture file.
Definition Ptexture.h:72
Ptex::dt_float
@ dt_float
Single-precision (32-bit) floating point.
Definition Ptexture.h:76
Ptex::MeshType
MeshType
Type of base mesh for which the textures are defined.
Definition Ptexture.h:66
Ptex::mt_quad
@ mt_quad
Mesh is quad-based.
Definition Ptexture.h:68
Ptex::m_clamp
@ m_clamp
texel access is clamped to border
Definition Ptexture.h:87
EditFaceDataHeader
Definition PtexIO.h:93
EditFaceDataHeader::faceid
uint32_t faceid
Definition PtexIO.h:94
EditFaceDataHeader::fdh
FaceDataHeader fdh
Definition PtexIO.h:96
EditFaceDataHeader::faceinfo
FaceInfo faceinfo
Definition PtexIO.h:95
EditMetaDataHeader
Definition PtexIO.h:98
EditMetaDataHeader::metadatazipsize
uint32_t metadatazipsize
Definition PtexIO.h:99
EditMetaDataHeader::metadatamemsize
uint32_t metadatamemsize
Definition PtexIO.h:100
ExtHeader::vbordermode
uint16_t vbordermode
Definition PtexIO.h:67
ExtHeader::ubordermode
uint16_t ubordermode
Definition PtexIO.h:65
ExtHeader::lmddatasize
uint64_t lmddatasize
Definition PtexIO.h:71
ExtHeader::editdatapos
uint64_t editdatapos
Definition PtexIO.h:73
ExtHeader::lmdheadermemsize
uint32_t lmdheadermemsize
Definition PtexIO.h:70
ExtHeader::lmdheaderzipsize
uint32_t lmdheaderzipsize
Definition PtexIO.h:69
ExtHeader::editdatasize
uint64_t editdatasize
Definition PtexIO.h:72
FaceDataHeader
Definition PtexIO.h:82
FaceDataHeader::set
void set(uint32_t blocksizeArg, Encoding encodingArg)
Definition PtexIO.h:88
FaceDataHeader::blocksize
uint32_t blocksize() const
Definition PtexIO.h:84
Header::metadatamemsize
uint32_t metadatamemsize
Definition PtexIO.h:60
Header::faceinfosize
uint32_t faceinfosize
Definition PtexIO.h:54
Header::nlevels
uint16_t nlevels
Definition PtexIO.h:51
Header::nchannels
uint16_t nchannels
Definition PtexIO.h:50
Header::meshtype
uint32_t meshtype
Definition PtexIO.h:47
Header::constdatasize
uint32_t constdatasize
Definition PtexIO.h:55
Header::levelinfosize
uint32_t levelinfosize
Definition PtexIO.h:56
Header::extheadersize
uint32_t extheadersize
Definition PtexIO.h:53
Header::minorversion
uint32_t minorversion
Definition PtexIO.h:57
Header::metadatazipsize
uint32_t metadatazipsize
Definition PtexIO.h:59
Header::datatype
uint32_t datatype
Definition PtexIO.h:48
Header::pixelSize
int pixelSize() const
Definition PtexIO.h:61
Header::alphachan
int32_t alphachan
Definition PtexIO.h:49
Header::magic
uint32_t magic
Definition PtexIO.h:45
Header::nfaces
uint32_t nfaces
Definition PtexIO.h:52
Header::leveldatasize
uint64_t leveldatasize
Definition PtexIO.h:58
Header::version
uint32_t version
Definition PtexIO.h:46
Header::hasAlpha
bool hasAlpha() const
Definition PtexIO.h:62
LevelInfo
Definition PtexIO.h:75
LevelInfo::levelheadersize
uint32_t levelheadersize
Definition PtexIO.h:77
LevelInfo::leveldatasize
uint64_t leveldatasize
Definition PtexIO.h:76
LevelInfo::nfaces
uint32_t nfaces
Definition PtexIO.h:78
PtexMainWriter::LevelRec
Definition PtexWriter.h:165
PtexMainWriter::LevelRec::pos
std::vector< FilePos > pos
Definition PtexWriter.h:171
PtexMainWriter::LevelRec::fdh
std::vector< FaceDataHeader > fdh
Definition PtexWriter.h:172
PtexWriterBase::MetaEntry
Definition PtexWriter.h:82
PtexWriterBase::MetaEntry::key
std::string key
Definition PtexWriter.h:83
PtexWriterBase::MetaEntry::datatype
MetaDataType datatype
Definition PtexWriter.h:84
PtexWriterBase::MetaEntry::data
std::vector< uint8_t > data
Definition PtexWriter.h:85
Ptex::FaceInfo
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
Ptex::FaceInfo::res
Res res
Resolution of face.
Definition Ptexture.h:230
Ptex::FaceInfo::isConstant
bool isConstant() const
Determine if face is constant (by checking a flag).
Definition Ptexture.h:262
Ptex::Res
Pixel resolution of a given texture.
Definition Ptexture.h:159
Ptex::Res::size
int size() const
Total size of specified texture in texels (u * v).
Definition Ptexture.h:182
Ptex::Res::u
int u() const
U resolution in texels.
Definition Ptexture.h:173