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