1 /**
2 * @brief PFS library - core API interfaces
3 *
4 * Classes for reading and writing a stream of PFS frames.
5 *
6 * This file is a part of PFSTOOLS package.
7 * ----------------------------------------------------------------------
8 * Copyright (C) 2003,2004 Rafal Mantiuk and Grzegorz Krawczyk
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * ----------------------------------------------------------------------
24 *
25 * @author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de>
26 *
27 * $Id: pfs.cpp,v 1.13 2014/09/17 15:07:18 rafm Exp $
28 */
29
30 #if !defined(_MSC_VER) && !defined(_MATLAB_VER)
31 #include <config.h>
32 #endif
33
34 // This does not seem to be needed under Cygwin
35 //#if defined(_MSC_VER) || defined(__CYGWIN__)
36
37 #if defined(_MSC_VER)
38 #include <io.h>
39 #define HAVE_SETMODE
40 #endif
41
42 #include <cstdlib>
43
44 #include <fcntl.h>
45
46 #include <string.h>
47 #include <assert.h>
48 #include <string>
49 #include <list>
50
51 #include <map>
52
53 #include "pfs.h"
54
55 #define PFSEOL "\x0a"
56 #define PFSEOLCH '\x0a'
57
58 #define MAX_RES 65535
59
60 #define MAX_CHANNEL_NAME 32
61 #define MAX_TAG_STRING 1024
62 #define MAX_CHANNEL_COUNT 1024
63
64
65 using namespace std;
66
67 namespace pfs
68 {
69
70 const char *PFSFILEID="PFS1\x0a";
71
72
73 //------------------------------------------------------------------------------
74 // TagContainer implementation
75 //------------------------------------------------------------------------------
76
77 typedef list<string> TagList;
78
79 class TagIteratorImpl: public TagIterator
80 {
81 TagList::const_iterator it;
82 const TagList &tagList;
83 string tagName;
84
85 public:
TagIteratorImpl(const TagList & tagList)86 TagIteratorImpl( const TagList &tagList ) : tagList( tagList )
87 {
88 it = tagList.begin();
89 }
90
91 /**
92 * Get next item on the list.
93 */
getNext()94 const char *getNext()
95 {
96 const string &tag = *(it++);
97 size_t equalSign = tag.find( '=' );
98 assert( equalSign != -1 );
99 tagName = string( tag, 0, equalSign );
100 return tagName.c_str();
101 }
102
103 /**
104 * Returns true if there is still an item left on the list.
105 */
hasNext() const106 bool hasNext() const
107 {
108 return it != tagList.end();
109 }
110
111 };
112
113 class TagContainerImpl: public TagContainer
114 {
115 public:
116 private:
117
118 TagList tagList;
119
120 public:
121
122 // ~TagContainerImpl()
123 // {
124 // tagList.clear();
125 // }
126
tagsBegin() const127 TagList::const_iterator tagsBegin() const
128 {
129 return tagList.begin();
130 }
131
tagsEnd() const132 TagList::const_iterator tagsEnd() const
133 {
134 return tagList.end();
135 }
136
getSize() const137 int getSize() const
138 {
139 return (int)tagList.size();
140 }
141
appendTagEOL(const char * tagValue)142 void appendTagEOL( const char *tagValue )
143 {
144 assert( tagValue[strlen( tagValue ) -1] == PFSEOLCH );
145 tagList.push_back( string( tagValue, strlen( tagValue ) -1 ) );
146 }
147
appendTag(const string & tagValue)148 void appendTag( const string &tagValue )
149 {
150 tagList.push_back( tagValue );
151 }
152
findTag(const char * tagName)153 TagList::iterator findTag( const char *tagName )
154 {
155 size_t tagNameLen = strlen( tagName );
156 TagList::iterator it;
157 for( it = tagList.begin(); it != tagList.end(); it++ ) {
158 if( !memcmp( tagName, it->c_str(), tagNameLen ) ) break; // Found
159 }
160 return it;
161 }
162
setTag(const char * tagName,const char * tagValue)163 void setTag( const char *tagName, const char *tagValue )
164 {
165 string tagVal( tagName );
166 tagVal += "=";
167 tagVal += tagValue;
168
169 TagList::iterator element = findTag( tagName );
170 if( element == tagList.end() ) { // Does not exist
171 tagList.push_back( tagVal );
172 } else { // Already exist
173 *element = tagVal;
174 }
175 }
176
getTag(const char * tagName)177 const char *getTag( const char *tagName )
178 {
179 TagList::iterator element = findTag( tagName );
180 if( element == tagList.end() ) return NULL;
181
182 string::size_type equalSign = element->find( '=' );
183 assert( equalSign != string::npos );
184
185 return element->c_str() + equalSign + 1;
186 }
187
188
189 //Implementation of TagContainer
getString(const char * tagName)190 const char* getString( const char *tagName )
191 {
192 return getTag( tagName );
193 }
194
setString(const char * tagName,const char * tagValue)195 void setString( const char *tagName, const char *tagValue )
196 {
197 setTag( tagName, tagValue );
198 }
199
removeTag(const char * tagName)200 void removeTag( const char *tagName )
201 {
202 TagList::iterator element = findTag( tagName );
203 if( element != tagList.end() ) tagList.erase( element );
204 }
205
getIterator() const206 TagIteratorPtr getIterator() const
207 {
208 return TagIteratorPtr( new TagIteratorImpl( tagList ) );
209 }
210
removeAllTags()211 void removeAllTags()
212 {
213 tagList.clear();
214 }
215
216
217 };
218
copyTags(const TagContainer * from,TagContainer * to)219 void copyTags( const TagContainer *from, TagContainer *to )
220 {
221 TagContainerImpl *f = (TagContainerImpl*)from;
222 TagContainerImpl *t = (TagContainerImpl*)to;
223
224 t->removeAllTags();
225
226 TagList::const_iterator it;
227 for( it = f->tagsBegin(); it != f->tagsEnd(); it++ ) {
228 t->appendTag( *it );
229 }
230 }
231
copyTags(Frame * from,Frame * to)232 void copyTags( Frame *from, Frame *to )
233 {
234 copyTags( from->getTags(), to->getTags() );
235 pfs::ChannelIterator *it = from->getChannels();
236 while( it->hasNext() ) {
237 pfs::Channel *fromCh = it->getNext();
238 pfs::Channel *toCh = to->getChannel( fromCh->getName() );
239 if( toCh == NULL ) // Skip if there is no corresponding channel
240 continue;
241 copyTags( fromCh->getTags(), toCh->getTags() );
242 }
243
244 }
245
246
247 //------------------------------------------------------------------------------
248 // Channel implementation
249 //------------------------------------------------------------------------------
250
251 class DOMIOImpl;
252
253 class ChannelImpl: public Channel {
254 int width, height;
255 float *data;
256 const char *name;
257
258 protected:
259 friend class DOMIOImpl;
260
261 TagContainerImpl *tags;
262
263 public:
ChannelImpl(int width,int height,const char * n_name)264 ChannelImpl( int width, int height, const char *n_name ) : width( width ), height( height )
265 {
266 data = new float[width*height];
267 tags = new TagContainerImpl();
268 name = strdup( n_name );
269 }
270
~ChannelImpl()271 virtual ~ChannelImpl()
272 {
273 delete tags;
274 delete[] data;
275 free( (void*)name );
276 }
277
278 // Channel implementation
getTags()279 TagContainer *getTags()
280 {
281 return tags;
282 }
283
getRawData()284 float *getRawData()
285 {
286 return data;
287 }
288
289 //Array2D implementation
290
getCols() const291 virtual int getCols() const {
292 return width;
293 }
294
getRows() const295 virtual int getRows() const {
296 return height;
297 }
298
getName() const299 virtual const char *getName() const
300 {
301 return name;
302 }
303
operator ()(int x,int y)304 inline float& operator()( int x, int y ) {
305 assert( x >= 0 && x < width );
306 assert( y >= 0 && y < height );
307 return data[ x+y*width ];
308 }
309
operator ()(int x,int y) const310 inline const float& operator()( int x, int y ) const
311 {
312 assert( x >= 0 && x < width );
313 assert( y >= 0 && y < height );
314 return data[ x+y*width ];
315 }
316
operator ()(int rowMajorIndex)317 inline float& operator()( int rowMajorIndex )
318 {
319 assert( rowMajorIndex < width*height );
320 assert( rowMajorIndex >= 0 );
321 return data[ rowMajorIndex ];
322 }
323
operator ()(int rowMajorIndex) const324 inline const float& operator()( int rowMajorIndex ) const
325 {
326 assert( rowMajorIndex < width*height );
327 assert( rowMajorIndex >= 0 );
328 return data[ rowMajorIndex ];
329 }
330
331 };
332
333
334 //------------------------------------------------------------------------------
335 // Map of channels
336 //------------------------------------------------------------------------------
337
338 struct str_cmp: public std::binary_function<const char*,const char*,bool>
339 {
operator ()pfs::str_cmp340 bool operator()(const char* s1, const char* s2) const
341 {
342 return strcmp(s1, s2) < 0;
343 }
344 };
345 typedef std::map<const char*, ChannelImpl*, str_cmp> ChannelMap;
346
347 //------------------------------------------------------------------------------
348 // Channel Iterator implementation
349 //-----------------------------------------------------------------------------
350
351 class ChannelIteratorImpl: public ChannelIterator
352 {
353 ChannelMap::iterator it;
354 ChannelMap *cm;
355 public:
ChannelIteratorImpl(ChannelMap * cm)356 ChannelIteratorImpl( ChannelMap *cm ) : cm(cm)
357 {
358 reset();
359 }
360
reset()361 void reset()
362 {
363 it = cm->begin();
364 }
365
getNext()366 Channel *getNext()
367 {
368 if( !hasNext() ) return NULL;
369 return (it++)->second;
370 }
371
hasNext() const372 bool hasNext() const
373 {
374 return it != cm->end();
375 }
376
377 };
378
379 //------------------------------------------------------------------------------
380 // Frame implementation
381 //------------------------------------------------------------------------------
382
383 //A pure virtual destructor
~Frame()384 Frame::~Frame()
385 {}
386
387 class FrameImpl: public Frame {
388 int width, height;
389
390 protected:
391 friend class DOMIOImpl;
392
393 TagContainerImpl *tags;
394
395 // enum ChannelID { CH_X = 0, CH_Y, CH_Z, CH_ALPHA, CH_DEPTH, CH_AUXLUM, CH_COUNT };
396 // static const char* const channelStrID[CH_COUNT];
397
398 ChannelMap channel;
399
400 ChannelIteratorImpl channelIterator;
401
402 public:
403
FrameImpl(int width,int height)404 FrameImpl( int width, int height ): width( width ), height( height ),
405 channelIterator( &channel )
406 {
407 tags = new TagContainerImpl();
408 }
409
~FrameImpl()410 ~FrameImpl()
411 {
412 delete tags;
413 ChannelMap::iterator it;
414 for( it = channel.begin(); it != channel.end(); ) {
415 Channel *ch = it->second;
416 ChannelMap::iterator itToDelete = it; // Nasty trick because hashmap
417 // elements point to string that is
418 // freed by the channel
419
420 it++;
421 channel.erase( itToDelete );
422 delete ch;
423 }
424
425 }
426
getWidth() const427 virtual int getWidth() const
428 {
429 return width;
430 }
431
getHeight() const432 virtual int getHeight() const
433 {
434 return height;
435 }
436
getXYZChannels(Channel * & X,Channel * & Y,Channel * & Z)437 virtual void getXYZChannels( Channel* &X, Channel* &Y, Channel* &Z ) {
438
439 if( channel.find("X") == channel.end() ||
440 channel.find("Y") == channel.end() ||
441 channel.find("Z") == channel.end() ) {
442 X = Y = Z = NULL;
443 } else {
444 X = channel["X"];
445 Y = channel["Y"];
446 Z = channel["Z"];
447 }
448 }
449
createXYZChannels(Channel * & X,Channel * & Y,Channel * & Z)450 virtual void createXYZChannels( Channel* &X, Channel* &Y, Channel* &Z )
451 {
452 X = createChannel("X");
453 Y = createChannel("Y");
454 Z = createChannel("Z");
455 }
456
getChannel(const char * name)457 Channel* getChannel( const char *name )
458 {
459 ChannelMap::iterator it = channel.find(name);
460 if( it == channel.end() )
461 return NULL;
462 else
463 return it->second;
464
465 }
466
createChannel(const char * name)467 Channel *createChannel( const char *name )
468 {
469 ChannelImpl *ch;
470 if( channel.find(name) == channel.end() ) {
471 ch = new ChannelImpl( width, height, name );
472 channel.insert( pair<const char*, ChannelImpl*>(ch->getName(), ch) );
473 } else
474 ch = channel[name];
475
476 return ch;
477 }
478
removeChannel(Channel * ch)479 void removeChannel( Channel *ch )
480 {
481 assert( ch != NULL );
482 ChannelMap::iterator it = channel.find( ch->getName() );
483 assert( it != channel.end() && it->second == ch );
484
485 channel.erase( it );
486 delete ch;
487 }
488
getChannels()489 ChannelIterator *getChannels()
490 {
491 channelIterator.reset();
492 return &channelIterator;
493 }
494
getChannelIterator()495 ChannelIteratorPtr getChannelIterator()
496 {
497 return ChannelIteratorPtr( new ChannelIteratorImpl( &channel ) );
498 }
499
getTags()500 TagContainer *getTags()
501 {
502 return tags;
503 }
504
505
506 };
507
readTags(TagContainerImpl * tags,FILE * in)508 static void readTags( TagContainerImpl *tags, FILE *in )
509 {
510 int readItems;
511 int tagCount;
512 readItems = fscanf( in, "%d" PFSEOL, &tagCount );
513 if( readItems != 1 || tagCount < 0 || tagCount > 1024 )
514 throw Exception( "Corrupted PFS tag section: missing or wrong number of tags" );
515
516 char buf[MAX_TAG_STRING];
517 for( int i = 0; i < tagCount; i++ ) {
518 char *read = fgets( buf, MAX_TAG_STRING, in );
519 if( read == NULL ) throw Exception( "Corrupted PFS tag section: missing tag" );
520 if( strlen(buf)==(MAX_TAG_STRING-1) ) { // Crop the string if needed
521 buf[MAX_TAG_STRING-2] = PFSEOLCH;
522 // now we need to skip all characters in the file stream up to EOL
523 const int SKIP_BUF_LEN = 256;
524 char skip_buf[SKIP_BUF_LEN];
525 do {
526 read = fgets( skip_buf, SKIP_BUF_LEN, in );
527 } while( read != NULL && strlen(skip_buf)==(SKIP_BUF_LEN-1) );
528
529 }
530 char *equalSign = strstr( buf, "=" );
531 if( equalSign == NULL ) throw Exception( "Corrupted PFS tag section ('=' sign missing)" );
532 tags->appendTagEOL( buf );
533 }
534 }
535
writeTags(const TagContainerImpl * tags,FILE * out)536 static void writeTags( const TagContainerImpl *tags, FILE *out )
537 {
538 TagList::const_iterator it;
539 fprintf( out, "%d" PFSEOL, tags->getSize() );
540 for( it = tags->tagsBegin(); it != tags->tagsEnd(); it++ ) {
541 fputs( (const char*)(it->c_str()), out );
542 fprintf( out, PFSEOL );
543 }
544 }
545
546
547
548 //------------------------------------------------------------------------------
549 // pfs IO
550 //------------------------------------------------------------------------------
551
552 class DOMIOImpl {
553 public:
554
readFrame(FILE * inputStream)555 Frame *readFrame( FILE *inputStream )
556 {
557 assert( inputStream != NULL );
558
559 #ifdef HAVE_SETMODE
560 // Needed under MS windows (text translation IO for stdin/out)
561 int old_mode = setmode( fileno( inputStream ), _O_BINARY );
562 #endif
563
564 size_t read;
565
566 char buf[5];
567 read = fread( buf, 1, 5, inputStream );
568 if( read == 0 ) return NULL; // EOF
569
570 if( memcmp( buf, PFSFILEID, 5 ) ) throw Exception( "Incorrect PFS file header" );
571
572 int width, height, channelCount;
573 read = fscanf( inputStream, "%d %d" PFSEOL, &width, &height );
574 if( read != 2 || width <= 0 || width > MAX_RES || height <= 0 || height > MAX_RES )
575 throw Exception( "Corrupted PFS file: missing or wrong 'width', 'height' tags" );
576 read = fscanf( inputStream, "%d" PFSEOL, &channelCount );
577 if( read != 1 || channelCount < 0 || channelCount > MAX_CHANNEL_COUNT )
578 throw Exception( "Corrupted PFS file: missing or wrong 'channelCount' tag" );
579
580 FrameImpl *frame = (FrameImpl*)createFrame( width, height );
581
582 readTags( frame->tags, inputStream );
583
584 //Read channel IDs and tags
585 // FrameImpl::ChannelID *channelID = new FrameImpl::ChannelID[channelCount];
586 list<ChannelImpl*> orderedChannel;
587 for( int i = 0; i < channelCount; i++ ) {
588 char channelName[MAX_CHANNEL_NAME+1], *rs;
589 rs = fgets( channelName, MAX_CHANNEL_NAME, inputStream );
590 if( rs == NULL )
591 throw Exception( "Corrupted PFS file: missing channel name" );
592 size_t len = strlen( channelName );
593 // fprintf( stderr, "s = '%s' len = %d\n", channelName, len );
594 if( len < 1 || channelName[len-1] != PFSEOLCH )
595 throw Exception( "Corrupted PFS file: bad channel name" );
596 channelName[len-1] = 0;
597 ChannelImpl *ch = (ChannelImpl*)frame->createChannel( channelName );
598 readTags( ch->tags, inputStream );
599 orderedChannel.push_back( ch );
600 }
601
602 read = fread( buf, 1, 4, inputStream );
603 if( read == 0 || memcmp( buf, "ENDH", 4 ) )
604 throw Exception( "Corrupted PFS file: missing end of header (ENDH) token" );
605
606
607 //Read channels
608 list<ChannelImpl*>::iterator it;
609 for( it = orderedChannel.begin(); it != orderedChannel.end(); it++ ) {
610 ChannelImpl *ch = *it;
611 int size = frame->getWidth()*frame->getHeight();
612 read = fread( ch->getRawData(), sizeof( float ), size, inputStream );
613 if( read != size )
614 throw Exception( "Corrupted PFS file: missing channel data" );
615 }
616 #ifdef HAVE_SETMODE
617 setmode( fileno( inputStream ), old_mode );
618 #endif
619 return frame;
620 }
621
622
createFrame(int width,int height)623 Frame *createFrame( int width, int height )
624 {
625 /* if( lastFrame != NULL && lastFrame->width() == width && lastFrame->height() == height ) {
626 // Reuse last frame
627 return lastFrame;
628 } else
629 delete lastFrame;*/
630
631 Frame *frame = new FrameImpl( width, height );
632 if( frame == NULL ) throw Exception( "Out of memory" );
633 return frame;
634 }
635
636
writeFrame(Frame * frame,FILE * outputStream)637 void writeFrame( Frame *frame, FILE *outputStream )
638 {
639 assert( outputStream != NULL );
640 assert( frame != NULL );
641 #ifdef HAVE_SETMODE
642 // Needed under MS windows (text translation IO for stdin/out)
643 int old_mode = setmode( fileno( outputStream ), _O_BINARY );
644 #endif
645
646 FrameImpl *frameImpl = (FrameImpl*)frame;
647
648 fwrite( PFSFILEID, 1, 5, outputStream ); // Write header ID
649
650 fprintf( outputStream, "%d %d" PFSEOL, (int)frame->getWidth(), (int)frame->getHeight() );
651 fprintf( outputStream, "%d" PFSEOL, (int)frameImpl->channel.size() );
652
653 writeTags( frameImpl->tags, outputStream );
654
655 //Write channel IDs and tags
656 for( ChannelMap::iterator it = frameImpl->channel.begin(); it != frameImpl->channel.end(); it++ ) {
657 fprintf( outputStream, "%s" PFSEOL, it->second->getName() );
658 writeTags( it->second->tags, outputStream );
659 }
660
661 fprintf( outputStream, "ENDH");
662
663 //Write channels
664 {
665 for( ChannelMap::iterator it = frameImpl->channel.begin(); it != frameImpl->channel.end(); it++ ) {
666 int size = frame->getWidth()*frame->getHeight();
667 fwrite( it->second->getRawData(), sizeof( float ), size, outputStream );
668 }
669 }
670
671 //Very important for pfsoutavi !!!
672 fflush(outputStream);
673 #ifdef HAVE_SETMODE
674 setmode( fileno( outputStream ), old_mode );
675 #endif
676 }
677
freeFrame(Frame * frame)678 void freeFrame( Frame *frame )
679 {
680 delete frame;
681 }
682
683 };
684
685
DOMIO()686 DOMIO::DOMIO()
687 {
688 impl = new DOMIOImpl();
689 }
690
~DOMIO()691 DOMIO::~DOMIO()
692 {
693 delete impl;
694 }
695
createFrame(int width,int height)696 Frame *DOMIO::createFrame( int width, int height )
697 {
698 return impl->createFrame( width, height );
699 }
700
701
readFrame(FILE * inputStream)702 Frame *DOMIO::readFrame( FILE *inputStream )
703 {
704 return impl->readFrame( inputStream );
705 }
706
707
writeFrame(Frame * frame,FILE * outputStream)708 void DOMIO::writeFrame( Frame *frame, FILE *outputStream )
709 {
710 impl->writeFrame( frame, outputStream );
711 }
712
713
freeFrame(Frame * frame)714 void DOMIO::freeFrame( Frame *frame )
715 {
716 impl->freeFrame( frame );
717 }
718
719 };
720