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
70#include "Ptexture.h"
71#include "PtexUtils.h"
72#include "PtexWriter.h"
73
74 PTEX_NAMESPACE_BEGIN
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
167 PtexWriter* PtexWriter::open(const char* path,
168 Ptex::MeshType mt, Ptex::DataType dt,
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
186 PtexWriter* PtexWriter::edit(const char* path, bool incremental,
187 Ptex::MeshType mt, Ptex::DataType dt,
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
242 bool 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
261PtexWriterBase::PtexWriterBase(const char* path,
262 Ptex::MeshType mt, Ptex::DataType dt,
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));
270 _header.magic = Magic;
271 _header.version = PtexFileMajorVersion;
272 _header.minorversion = PtexFileMinorVersion;
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;
279 _header.extheadersize = sizeof(_extheader);
280 _pixelSize = _header.pixelSize();
281
282 memset(&_extheader, 0, sizeof(_extheader));
283
284 if (mt == mt_triangle)
285 _reduceFn = &PtexUtils::reduceTri;
286 else
287 _reduceFn = &PtexUtils::reduce;
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
301 void PtexWriterBase::release()
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
310PtexWriterBase::~PtexWriterBase()
311{
312 deflateEnd(&_zstream);
313}
314
315
316 bool PtexWriterBase::close(Ptex::String& error)
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
329 bool 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
361 void PtexWriterBase::writeMeta(const char* key, const char* value)
362{
363 addMetaData(key, mdt_string, value, int(strlen(value)+1));
364}
365
366
367 void PtexWriterBase::writeMeta(const char* key, const int8_t* value, int count)
368{
369 addMetaData(key, mdt_int8, value, count);
370}
371
372
373 void 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
379 void 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
385 void PtexWriterBase::writeMeta(const char* key, const float* value, int count)
386{
387 addMetaData(key, mdt_float, value, count*(int)sizeof(float));
388}
389
390
391 void PtexWriterBase::writeMeta(const char* key, const double* value, int count)
392{
393 addMetaData(key, mdt_double, value, count*(int)sizeof(double));
394}
395
396
397 void PtexWriterBase::writeMeta(PtexMetaData* data)
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
453 void 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
487 int 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
499 int 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
510 int 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
541 int 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
551 int 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
570 Ptex::Res PtexWriterBase::calcTileRes(Res faceres)
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
592 void 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
597 fdh.set(_pixelSize, enc_constant);
598 writeBlock(fp, data, _pixelSize);
599}
600
601
602 void 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()),
613 datatype(), _header.nchannels);
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
629 void 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))
662 writeConstFaceBlock(_tilefp, p, *tdh);
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
690 void 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
707 int PtexWriterBase::writeMetaDataBlock(FILE* fp, MetaEntry& val)
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
723PtexMainWriter::PtexMainWriter(const char* path, PtexTexture* tex,
724 Ptex::MeshType mt, Ptex::DataType dt,
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
762 setEdgeFilterMode(tex->edgeFilterMode());
763
764 // copy meta data from existing file
765 PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
766 writeMeta(meta);
767
768 // see if we have any edits
769 _hasNewData = _reader->hasEdits();
770 }
771}
772
773
774PtexMainWriter::~PtexMainWriter()
775{
776 if (_reader) _reader->release();
777}
778
779
780 bool PtexMainWriter::close(Ptex::String& error)
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
806 bool 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,
839 _header.alphachan);
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
863 bool 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
878 void 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,
883 datatype(), _header.nchannels);
884 if (_header.hasAlpha())
885 PtexUtils::divalpha(constdata, 1, datatype(), _header.nchannels, _header.alphachan);
886}
887
888
889
890 void PtexMainWriter::finish()
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()) {
903 PtexPtr<PtexFaceData> data ( _reader->getData(i) );
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)
926 generateReductions();
927
928 // flag faces w/ constant neighborhoods
929 flagConstantNeighorhoods();
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);
944 writeBlank(newfp, ExtHeaderSize);
945
946 // write compressed face info block
947 _header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0],
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);
955 writeBlank(newfp, LevelInfoSize * _header.nlevels);
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());
973 _header.leveldatasize += info.leveldatasize;
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);
986 _header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels);
987
988 // rewrite header
989 fseeko(newfp, 0, SEEK_SET);
990 writeBlock(newfp, &_header, HeaderSize);
991 writeBlock(newfp, &_extheader, ExtHeaderSize);
992 fclose(newfp);
993}
994
995
996 void PtexMainWriter::flagConstantNeighorhoods()
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
1039 if (_extheader.ubordermode != Ptex::m_clamp || _extheader.vbordermode != Ptex::m_clamp) {
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
1079 void PtexMainWriter::generateReductions()
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);
1086 PtexUtils::genRfaceids(&_faceinfo[0], nfaces, &_rfaceids[0], &_faceids_r[0]);
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
1150 void PtexMainWriter::writeMetaData(FILE* fp)
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
1166 _header.metadatamemsize += writeMetaDataBlock(fp, e);
1167 }
1168 }
1169 if (_header.metadatamemsize) {
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);
1202 _extheader.lmdheadermemsize +=
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++) {
1210 _extheader.lmddatasize +=
1211 copyBlock(fp, _tmpfp, lmdoffset[i], lmdzipsize[i]);
1212 }
1213 }
1214}
1215
1216
1217PtexIncrWriter::PtexIncrWriter(const char* path, FILE* fp,
1218 Ptex::MeshType mt, Ptex::DataType dt,
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
1265PtexIncrWriter::~PtexIncrWriter()
1266{
1267}
1268
1269
1270 bool 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,
1304 _header.alphachan);
1305 // average
1306 PtexUtils::average(temp, rowlen, f.res.u(), f.res.v(), constval,
1307 datatype(), _header.nchannels);
1308 // unmult alpha
1309 PtexUtils::divalpha(constval, 1, datatype(), _header.nchannels,
1310 _header.alphachan);
1311 delete [] temp;
1312 }
1313 else {
1314 // average
1315 PtexUtils::average(data, stride, f.res.u(), f.res.v(), constval,
1316 datatype(), _header.nchannels);
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
1338 bool 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
1362 void PtexIncrWriter::writeMetaDataEdit()
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];
1378 emdh.metadatamemsize += writeMetaDataBlock(_fp, e);
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
1395 bool PtexIncrWriter::close(Ptex::String& error)
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
1407 void PtexIncrWriter::finish()
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);
1416 fwrite(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize), 1, _fp);
1417 }
1418}
1419
1420 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:99
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:137
PtexIncrWriter
Definition: PtexWriter.h:181
PtexIncrWriter::close
virtual bool close(Ptex::String &error)
Close the file.
Definition: PtexWriter.cpp:1395
PtexIncrWriter::PtexIncrWriter
PtexIncrWriter(const char *path, FILE *fp, Ptex::MeshType mt, Ptex::DataType dt, int nchannels, int alphachan, int nfaces)
Definition: PtexWriter.cpp:1217
PtexIncrWriter::finish
virtual void finish()
Definition: PtexWriter.cpp:1407
PtexIncrWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition: PtexWriter.cpp:1338
PtexIncrWriter::_fp
FILE * _fp
Definition: PtexWriter.h:197
PtexIncrWriter::~PtexIncrWriter
virtual ~PtexIncrWriter()
Definition: PtexWriter.cpp:1265
PtexIncrWriter::writeFace
virtual bool writeFace(int faceid, const FaceInfo &f, const void *data, int stride)
Definition: PtexWriter.cpp:1270
PtexIncrWriter::writeMetaDataEdit
void writeMetaDataEdit()
Definition: PtexWriter.cpp:1362
PtexMainWriter
Definition: PtexWriter.h:129
PtexMainWriter::generateReductions
void generateReductions()
Definition: PtexWriter.cpp:1079
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:780
PtexMainWriter::_genmipmaps
bool _genmipmaps
Definition: PtexWriter.h:158
PtexMainWriter::writeConstantFace
virtual bool writeConstantFace(int faceid, const FaceInfo &f, const void *data)
Definition: PtexWriter.cpp:863
PtexMainWriter::_reader
PtexReader * _reader
Definition: PtexWriter.h:177
PtexMainWriter::_tmpfp
FILE * _tmpfp
Definition: PtexWriter.h:156
PtexMainWriter::finish
virtual void finish()
Definition: PtexWriter.cpp:890
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:878
PtexMainWriter::_faceinfo
std::vector< FaceInfo > _faceinfo
Definition: PtexWriter.h:159
PtexMainWriter::writeMetaData
void writeMetaData(FILE *fp)
Definition: PtexWriter.cpp:1150
PtexMainWriter::~PtexMainWriter
virtual ~PtexMainWriter()
Definition: PtexWriter.cpp:774
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:806
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:723
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:996
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:335
PtexReader::getData
virtual void getData(int faceid, void *buffer, int stride)
Access texture data for a face at highest-resolution.
Definition: PtexReader.cpp:711
PtexReader::getFaceInfo
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)
Access resolution and adjacency information about a face.
Definition: PtexReader.cpp:267
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:487
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:551
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:510
PtexWriterBase::writeMeta
virtual void writeMeta(const char *key, const char *value)
Write a string as meta data.
Definition: PtexWriter.cpp:361
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:453
PtexWriterBase::release
virtual void release()
Release resources held by this pointer (pointer becomes invalid).
Definition: PtexWriter.cpp:301
PtexWriterBase::writeBlock
int writeBlock(FILE *fp, const void *data, int size)
Definition: PtexWriter.cpp:499
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:316
PtexWriterBase::writeFaceData
void writeFaceData(FILE *fp, const void *data, int stride, Res res, FaceDataHeader &fdh)
Definition: PtexWriter.cpp:629
PtexWriterBase::readBlock
int readBlock(FILE *fp, void *data, int size)
Definition: PtexWriter.cpp:541
PtexWriterBase::_metadata
std::vector< MetaEntry > _metadata
Definition: PtexWriter.h:121
PtexWriterBase::writeConstFaceBlock
void writeConstFaceBlock(FILE *fp, const void *data, FaceDataHeader &fdh)
Definition: PtexWriter.cpp:592
PtexWriterBase::writeMetaDataBlock
int writeMetaDataBlock(FILE *fp, MetaEntry &val)
Definition: PtexWriter.cpp:707
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:690
PtexWriterBase::storeFaceInfo
bool storeFaceInfo(int faceid, FaceInfo &dest, const FaceInfo &src, int flags=0)
Definition: PtexWriter.cpp:329
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:310
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:261
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:602
PtexWriterBase::calcTileRes
Res calcTileRes(Res faceres)
Definition: PtexWriter.cpp:570
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:186
PtexWriter::applyEdits
static bool applyEdits(const char *path, Ptex::String &error)
Apply edits to a file.
Definition: PtexWriter.cpp:242
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:167
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:133
PTEX_NAMESPACE_BEGIN::OpenTempFile
FILE * OpenTempFile(std::string &tmppath)
Definition: PtexWriter.cpp:78
PTEX_NAMESPACE_BEGIN::fileError
std::string fileError(const char *message, const char *path)
Definition: PtexWriter.cpp:126
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