1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 
19 #include "itkImageIOBase.h"
20 #include "itkImageRegionSplitterSlowDimension.h"
21 #include <mutex>
22 #include "itksys/SystemTools.hxx"
23 #include "itkPrintHelper.h"
24 
25 
26 namespace itk
27 {
ImageIOBase()28 ImageIOBase::ImageIOBase()
29 
30 {
31   Reset(false);
32 }
33 
Reset(const bool)34 void ImageIOBase::Reset(const bool)
35 {
36   m_Initialized = false;
37   m_FileName = "";
38   m_NumberOfComponents = 1;
39   for ( unsigned int i = 0; i < m_NumberOfDimensions; i++ )
40     {
41     m_Dimensions[i] = 0;
42     m_Strides[i] = 0;
43     }
44   m_NumberOfDimensions = 0;
45 
46   m_UseStreamedReading = false;
47   m_UseStreamedWriting = false;
48   m_ExpandRGBPalette   = true;
49   m_IsReadAsScalarPlusPalette  = false;
50 
51 }
52 
53 ImageIOBase::~ImageIOBase() = default;
54 
55 const ImageIOBase::ArrayOfExtensionsType &
GetSupportedWriteExtensions() const56 ImageIOBase::GetSupportedWriteExtensions() const
57 {
58   return this->m_SupportedWriteExtensions;
59 }
60 
61 const ImageIOBase::ArrayOfExtensionsType &
GetSupportedReadExtensions() const62 ImageIOBase::GetSupportedReadExtensions() const
63 {
64   return this->m_SupportedReadExtensions;
65 }
66 
AddSupportedReadExtension(const char * extension)67 void ImageIOBase::AddSupportedReadExtension(const char *extension)
68 {
69   this->m_SupportedReadExtensions.push_back(extension);
70 }
71 
AddSupportedWriteExtension(const char * extension)72 void ImageIOBase::AddSupportedWriteExtension(const char *extension)
73 {
74   this->m_SupportedWriteExtensions.push_back(extension);
75 }
76 
Resize(const unsigned int numDimensions,const unsigned int * dimensions)77 void ImageIOBase::Resize(const unsigned int numDimensions,
78                          const unsigned int *dimensions)
79 {
80   m_NumberOfDimensions = numDimensions;
81   if ( dimensions != nullptr )
82     {
83     for ( unsigned int i = 0; i < m_NumberOfDimensions; i++ )
84       {
85       m_Dimensions[i] = dimensions[i];
86       }
87     ComputeStrides();
88     }
89 }
90 
SetDimensions(unsigned int i,SizeValueType dim)91 void ImageIOBase::SetDimensions(unsigned int i, SizeValueType dim)
92 {
93   if ( i >= m_Dimensions.size() )
94     {
95     itkWarningMacro( "Index: " << i
96                                << " is out of bounds, expected maximum is "
97                                << m_Dimensions.size() );
98     itkExceptionMacro( "Index: " << i
99                                  << " is out of bounds, expected maximum is "
100                                  << m_Dimensions.size() );
101     }
102   this->Modified();
103   m_Dimensions[i] = dim;
104 }
105 
SetOrigin(unsigned int i,double origin)106 void ImageIOBase::SetOrigin(unsigned int i, double origin)
107 {
108   if ( i >= m_Origin.size() )
109     {
110     itkWarningMacro( "Index: " << i
111                                << " is out of bounds, expected maximum is "
112                                << m_Origin.size() );
113     itkExceptionMacro( "Index: " << i
114                                  << " is out of bounds, expected maximum is "
115                                  << m_Origin.size() );
116     }
117   this->Modified();
118   m_Origin[i] = origin;
119 }
120 
SetSpacing(unsigned int i,double spacing)121 void ImageIOBase::SetSpacing(unsigned int i, double spacing)
122 {
123   if ( i >= m_Spacing.size() )
124     {
125     itkWarningMacro( "Index: " << i
126                                << " is out of bounds, expected maximum is "
127                                << m_Spacing.size() );
128     itkExceptionMacro( "Index: " << i
129                                  << " is out of bounds, expected maximum is "
130                                  << m_Spacing.size() );
131     }
132   this->Modified();
133   m_Spacing[i] = spacing;
134 }
135 
SetDirection(unsigned int i,const std::vector<double> & direction)136 void ImageIOBase::SetDirection(unsigned int i, const std::vector< double > & direction)
137 {
138   if ( i >= m_Direction.size() )
139     {
140     itkWarningMacro( "Index: " << i
141                                << " is out of bounds, expected maximum is "
142                                << m_Direction.size() );
143     itkExceptionMacro( "Index: " << i
144                                  << " is out of bounds, expected maximum is "
145                                  << m_Direction.size() );
146     }
147   this->Modified();
148   m_Direction[i] = direction;
149 }
150 
SetDirection(unsigned int i,const vnl_vector<double> & direction)151 void ImageIOBase::SetDirection(unsigned int i, const vnl_vector< double > & direction)
152 {
153   if ( i >= m_Direction.size() )
154     {
155     itkWarningMacro( "Index: " << i
156                                << " is out of bounds, expected maximum is "
157                                << m_Direction.size() );
158     itkExceptionMacro( "Index: " << i
159                                  << " is out of bounds, expected maximum is "
160                                  << m_Direction.size() );
161     }
162   this->Modified();
163   std::vector< double > v;
164   v.resize( m_Direction.size() );
165   // Note: direction.size() <= v.size().
166   for ( unsigned int j = 0; j < direction.size(); j++ )
167     {
168     v[j] = direction[j];
169     }
170   m_Direction[i] = v;
171 }
172 
GetComponentTypeInfo() const173 const std::type_info & ImageIOBase::GetComponentTypeInfo() const
174 {
175   switch ( m_ComponentType )
176     {
177     case UCHAR:
178       return typeid( unsigned char );
179     case CHAR:
180       return typeid( char );
181     case USHORT:
182       return typeid( unsigned short );
183     case SHORT:
184       return typeid( short );
185     case UINT:
186       return typeid( unsigned int );
187     case INT:
188       return typeid( int );
189     case ULONG:
190       return typeid( unsigned long );
191     case LONG:
192       return typeid( long );
193     case ULONGLONG:
194       return typeid( unsigned long long );
195     case LONGLONG:
196       return typeid( long long);
197     case FLOAT:
198       return typeid( float );
199     case DOUBLE:
200       return typeid( double );
201     case UNKNOWNCOMPONENTTYPE:
202     default:
203       itkExceptionMacro ("Unknown component type: " << m_ComponentType);
204     }
205 }
206 
ComputeStrides()207 void ImageIOBase::ComputeStrides()
208 {
209   m_Strides[0] = this->GetComponentSize();
210   m_Strides[1] = m_NumberOfComponents * m_Strides[0];
211   for ( unsigned int i = 2; i <= ( m_NumberOfDimensions + 1 ); i++ )
212     {
213     m_Strides[i] = static_cast<SizeType>(m_Dimensions[i - 2]) * m_Strides[i - 1];
214     }
215 }
216 
217 // Calculates the image size in PIXELS
218 ImageIOBase::SizeType
219 ImageIOBase
GetImageSizeInPixels() const220 ::GetImageSizeInPixels() const
221 {
222   unsigned int i;
223   SizeType     numPixels = 1;
224 
225   for ( i = 0; i < m_NumberOfDimensions; i++ )
226     {
227     numPixels *= m_Dimensions[i];
228     }
229 
230   return numPixels;
231 }
232 
233 ImageIOBase::SizeType
234 ImageIOBase
GetImageSizeInComponents() const235 ::GetImageSizeInComponents() const
236 {
237   return ( this->GetImageSizeInPixels() * m_NumberOfComponents );
238 }
239 
240 ImageIOBase::SizeType
241 ImageIOBase
GetImageSizeInBytes() const242 ::GetImageSizeInBytes() const
243 {
244   return ( this->GetImageSizeInComponents() * this->GetComponentSize() );
245 }
246 
247 ImageIOBase::SizeType
248 ImageIOBase
GetComponentStride() const249 ::GetComponentStride() const
250 {
251   return m_Strides[0];
252 }
253 
254 ImageIOBase::SizeType
255 ImageIOBase
GetPixelStride() const256 ::GetPixelStride() const
257 {
258   return m_Strides[1];
259 }
260 
261 ImageIOBase::SizeType
262 ImageIOBase
GetRowStride() const263 ::GetRowStride() const
264 {
265   return m_Strides[2];
266 }
267 
268 ImageIOBase::SizeType
269 ImageIOBase
GetSliceStride() const270 ::GetSliceStride() const
271 {
272   return m_Strides[3];
273 }
274 
SetNumberOfDimensions(unsigned int dim)275 void ImageIOBase::SetNumberOfDimensions(unsigned int dim)
276 {
277   if ( dim != m_NumberOfDimensions )
278     {
279     m_Origin.resize(dim);
280     m_Spacing.resize(dim);
281     m_Direction.resize(dim);
282     m_Strides.resize(dim + 2);
283     m_NumberOfDimensions = dim;
284     m_Dimensions.resize(dim);
285     m_Direction.resize(dim);
286     std::vector< double > axis(dim);
287     for ( unsigned int i = 0; i < dim; i++ )
288       {
289       for ( unsigned int j = 0; j < dim; j++ )
290         {
291         if ( i == j )
292           {
293           axis[j] = 1.0;
294           }
295         else
296           {
297           axis[j] = 0.0;
298           }
299         }
300       this->SetDirection(i, axis);
301       this->SetOrigin(i, 0.0);
302       this->SetSpacing(i, 1.0);
303       }
304     this->Modified();
305     }
306 }
307 
308 bool
309 ImageIOBase
ReadBufferAsBinary(std::istream & is,void * buffer,ImageIOBase::SizeType num)310 ::ReadBufferAsBinary(std::istream & is, void *buffer, ImageIOBase::SizeType num)
311 {
312   const auto numberOfBytesToBeRead = Math::CastWithRangeCheck< std::streamsize >(num);
313 
314   is.read(static_cast< char * >( buffer ), numberOfBytesToBeRead);
315 
316   const std::streamsize numberOfBytesRead = is.gcount();
317 
318 #ifdef __APPLE_CC__
319   // fail() is broken in the Mac. It returns true when reaches eof().
320   if ( numberOfBytesRead != numberOfBytesToBeRead )
321 #else
322   if ( ( numberOfBytesRead != numberOfBytesToBeRead )  || is.fail() )
323 #endif
324     {
325     return false; // read failed
326     }
327 
328   return true;
329 }
330 
GetPixelSize() const331 unsigned int ImageIOBase::GetPixelSize() const
332 {
333   if ( m_ComponentType == UNKNOWNCOMPONENTTYPE
334        || m_PixelType == UNKNOWNPIXELTYPE )
335     {
336     itkExceptionMacro ("Unknown pixel or component type: ("
337                        << m_PixelType << ", " << m_ComponentType << ")");
338     }
339 
340   return this->GetComponentSize() * this->GetNumberOfComponents();
341 }
342 
343 
SetCompressor(std::string _c)344 void ImageIOBase::SetCompressor( std::string _c )
345 {
346   if ( this->m_Compressor != _c )
347     {
348     this->m_Compressor = _c;
349     this->Modified();
350 
351     std::transform(_c.begin(), _c.end(), _c.begin(), ::toupper);
352     this->InternalSetCompressor(_c);
353     }
354 }
355 
SetMaximumCompressionLevel(int _MaximumCompressionLevel)356 void ImageIOBase::SetMaximumCompressionLevel( int _MaximumCompressionLevel )
357 {
358   this->m_MaximumCompressionLevel = _MaximumCompressionLevel;
359   this->SetCompressionLevel( this->GetCompressionLevel() );
360 }
361 
InternalSetCompressor(const std::string & _compressor)362 void ImageIOBase::InternalSetCompressor(const std::string &_compressor)
363 {
364   if (_compressor != "")
365     {
366     itkWarningMacro("Unknown compressor: \"" << _compressor << "\", setting to default.");
367     this->SetCompressor("");
368     }
369 }
370 
GetComponentSize() const371 unsigned int ImageIOBase::GetComponentSize() const
372 {
373   switch ( m_ComponentType )
374     {
375     case UCHAR:
376       return sizeof( unsigned char );
377     case CHAR:
378       return sizeof( char );
379     case USHORT:
380       return sizeof( unsigned short );
381     case SHORT:
382       return sizeof( short );
383     case UINT:
384       return sizeof( unsigned int );
385     case INT:
386       return sizeof( int );
387     case ULONG:
388       return sizeof( unsigned long );
389     case LONG:
390       return sizeof( long );
391     case ULONGLONG:
392       return sizeof( unsigned long long );
393     case LONGLONG:
394       return sizeof( long long );
395     case FLOAT:
396       return sizeof( float );
397     case DOUBLE:
398       return sizeof( double );
399     case UNKNOWNCOMPONENTTYPE:
400     default:
401       itkExceptionMacro ("Unknown component type: " << m_ComponentType);
402     }
403 }
404 
GetFileTypeAsString(FileType t) const405 std::string ImageIOBase::GetFileTypeAsString(FileType t) const
406 {
407   switch ( t )
408     {
409     case ASCII:
410       return std::string( "ASCII");
411     case Binary:
412       return std::string( "Binary");
413     case TypeNotApplicable:
414     default:
415       return std::string( "TypeNotApplicable");
416     }
417   //Not reachable return s = "TypeNotApplicable";
418 }
419 
GetByteOrderAsString(ByteOrder t) const420 std::string ImageIOBase::GetByteOrderAsString(ByteOrder t) const
421 {
422   switch ( t )
423     {
424     case BigEndian:
425       return std::string( "BigEndian");
426     case LittleEndian:
427       return std::string( "LittleEndian");
428     case OrderNotApplicable:
429     default:
430       return std::string( "OrderNotApplicable");
431     }
432 }
433 
GetComponentTypeAsString(IOComponentType t)434 std::string ImageIOBase::GetComponentTypeAsString(IOComponentType t)
435 {
436   switch ( t )
437     {
438     case UCHAR:
439       return std::string( "unsigned_char" );
440     case CHAR:
441       return std::string( "char" );
442     case USHORT:
443       return std::string( "unsigned_short" );
444     case SHORT:
445       return std::string( "short" );
446     case UINT:
447       return std::string( "unsigned_int" );
448     case INT:
449       return std::string( "int" );
450     case ULONG:
451       return std::string( "unsigned_long" );
452     case LONG:
453       return std::string( "long" );
454     case ULONGLONG:
455       return std::string( "unsigned_long_long" );
456     case LONGLONG:
457       return std::string( "long_long" );
458     case FLOAT:
459       return std::string( "float" );
460     case DOUBLE:
461       return std::string( "double" );
462     case UNKNOWNCOMPONENTTYPE:
463       return std::string( "unknown" );
464     default:
465       return std::string( "unknown" );
466     }
467 }
468 
GetComponentTypeFromString(const std::string & typeString)469 ImageIOBase::IOComponentType ImageIOBase::GetComponentTypeFromString(const std::string &typeString)
470 {
471   if(typeString.compare("unsigned_char") == 0)
472     {
473     return UCHAR;
474     }
475   else if(typeString.compare("char") == 0)
476     {
477     return CHAR;
478     }
479   else if(typeString.compare("unsigned_short") == 0)
480     {
481     return USHORT;
482     }
483   else if(typeString.compare("short") == 0)
484     {
485     return SHORT;
486     }
487   else if(typeString.compare("unsigned_int") == 0)
488     {
489     return UINT;
490     }
491   else if(typeString.compare("int") == 0)
492     {
493     return INT;
494     }
495   else if(typeString.compare("unsigned_long") == 0)
496     {
497     return ULONG;
498     }
499   else if(typeString.compare("long") == 0)
500     {
501     return LONG;
502     }
503   else if(typeString.compare("unsigned_long_long") == 0)
504     {
505     return ULONGLONG;
506     }
507   else if(typeString.compare("long_long") == 0)
508     {
509     return LONGLONG;
510     }
511   else if(typeString.compare("float") == 0)
512     {
513     return FLOAT;
514     }
515   else if(typeString.compare("double") == 0)
516     {
517     return DOUBLE;
518     }
519   else
520     {
521     return UNKNOWNCOMPONENTTYPE;
522     }
523 }
524 
GetPixelTypeAsString(IOPixelType t)525 std::string ImageIOBase::GetPixelTypeAsString(IOPixelType t)
526 {
527   switch ( t )
528     {
529     case SCALAR:
530       return std::string( "scalar" );
531     case VECTOR:
532       return std::string( "vector" );
533     case COVARIANTVECTOR:
534       return std::string( "covariant_vector" );
535     case POINT:
536       return std::string( "point" );
537     case OFFSET:
538       return std::string( "offset" );
539     case RGB:
540       return std::string( "rgb" );
541     case RGBA:
542       return std::string( "rgba" );
543     case SYMMETRICSECONDRANKTENSOR:
544       return std::string( "symmetric_second_rank_tensor" );
545     case DIFFUSIONTENSOR3D:
546       return std::string( "diffusion_tensor_3D" );
547     case COMPLEX:
548       return std::string( "complex" );
549     case FIXEDARRAY:
550       return std::string( "fixed_array" );
551     case MATRIX:
552       return std::string( "matrix" );
553     case UNKNOWNPIXELTYPE:
554       return std::string( "unknown" );
555     default:
556       return std::string( "unknown" );
557     }
558 }
559 
GetPixelTypeFromString(const std::string & pixelString)560 ImageIOBase::IOPixelType ImageIOBase::GetPixelTypeFromString(const std::string &pixelString)
561 {
562   if(pixelString.compare("scalar") == 0)
563     {
564     return SCALAR;
565     }
566   else if(pixelString.compare("vector") == 0)
567     {
568     return VECTOR;
569     }
570   else if(pixelString.compare("covariant_vector") == 0)
571     {
572     return COVARIANTVECTOR;
573     }
574   else if(pixelString.compare("point") == 0)
575     {
576     return POINT;
577     }
578   else if(pixelString.compare("offset") == 0)
579     {
580     return OFFSET;
581     }
582   else if(pixelString.compare("rgb") == 0)
583     {
584     return RGB;
585     }
586   else if(pixelString.compare("rgba") == 0)
587     {
588     return RGBA;
589     }
590   else if(pixelString.compare("symmetric_second_rank_tensor") == 0)
591     {
592     return SYMMETRICSECONDRANKTENSOR;
593     }
594   else if(pixelString.compare("diffusion_tensor_3D") == 0)
595     {
596     return DIFFUSIONTENSOR3D;
597     }
598   else if(pixelString.compare("complex") == 0)
599     {
600     return COMPLEX;
601     }
602   else if(pixelString.compare("fixed_array") == 0)
603     {
604     return FIXEDARRAY;
605     }
606   else if(pixelString.compare("matrix") == 0)
607     {
608     return MATRIX;
609     }
610   else
611     {
612     return UNKNOWNPIXELTYPE;
613     }
614 }
615 
OpenFileForReading(std::ifstream & inputStream,const std::string & filename,bool ascii)616 void ImageIOBase::OpenFileForReading(std::ifstream & inputStream, const std::string & filename,
617                                      bool ascii)
618 {
619   // Make sure that we have a file to
620   if ( filename.empty() )
621     {
622     itkExceptionMacro( << "A FileName must be specified." );
623     }
624 
625   // Close file from any previous image
626   if ( inputStream.is_open() )
627     {
628     inputStream.close();
629     }
630 
631   // Open the new file for reading
632   itkDebugMacro( << "Opening file for reading: " << filename );
633 
634   std::ios::openmode mode = std::ios::in;
635   if ( !ascii )
636     {
637     mode |= std::ios::binary;
638     }
639 
640   inputStream.open( filename.c_str(), mode );
641 
642   if ( !inputStream.is_open() || inputStream.fail() )
643     {
644     itkExceptionMacro( << "Could not open file: "
645                        << filename << " for reading."
646                        << std::endl
647                        << "Reason: "
648                        << itksys::SystemTools::GetLastSystemError() );
649     }
650 }
651 
OpenFileForWriting(std::ofstream & outputStream,const std::string & filename,bool truncate,bool ascii)652 void ImageIOBase::OpenFileForWriting(std::ofstream & outputStream, const std::string & filename,
653                                      bool truncate, bool ascii)
654 {
655   // Make sure that we have a file to
656   if ( filename.empty() )
657     {
658     itkExceptionMacro( << "A FileName must be specified." );
659     }
660 
661   // Close file from any previous image
662   if ( outputStream.is_open() )
663     {
664     outputStream.close();
665     }
666 
667   // Open the new file for writing
668   itkDebugMacro( << "Opening file for writing: " << filename );
669 
670   std::ios::openmode mode = std::ios::out;
671   if ( truncate )
672     {
673     // typically, ios::out also implies ios::trunc, but being explicit is safer
674     mode |= std::ios::trunc;
675     }
676   else
677     {
678     mode |= std::ios::in;
679     // opening a nonexistent file for reading + writing is not allowed on some platforms
680     if ( !itksys::SystemTools::FileExists( filename.c_str() ) )
681       {
682       itksys::SystemTools::Touch( filename.c_str(), true );
683       // don't worry about failure here, errors should be detected later when the file
684       // is "actually" opened, unless there is a race condition
685       }
686     }
687   if ( !ascii )
688     {
689     mode |= std::ios::binary;
690     }
691 
692   outputStream.open( filename.c_str(), mode );
693 
694   if ( !outputStream.is_open() || outputStream.fail() )
695     {
696     itkExceptionMacro( << "Could not open file: "
697                        << filename << " for writing."
698                        << std::endl
699                        << "Reason: "
700                        << itksys::SystemTools::GetLastSystemError() );
701     }
702 }
703 
704 namespace
705 {
706 template< typename TComponent >
WriteBuffer(std::ostream & os,const TComponent * buffer,ImageIOBase::SizeType num)707 void WriteBuffer(std::ostream & os, const TComponent *buffer, ImageIOBase::SizeType num)
708 {
709   const TComponent *ptr = buffer;
710 
711   using PrintType = typename itk::NumericTraits< TComponent >::PrintType;
712   for ( ImageIOBase::SizeType i = 0; i < num; i++ )
713     {
714     if ( !( i % 6 ) && i )
715       {
716       os << "\n";
717       }
718     os << PrintType(*ptr++) << " ";
719     }
720 }
721 }
WriteBufferAsASCII(std::ostream & os,const void * buffer,IOComponentType ctype,ImageIOBase::SizeType numComp)722 void ImageIOBase::WriteBufferAsASCII(std::ostream & os, const void *buffer,
723                                      IOComponentType ctype,
724                                      ImageIOBase::SizeType numComp)
725 {
726   switch ( ctype )
727     {
728     case UCHAR:
729       {
730       using Type = const unsigned char *;
731       auto buf = reinterpret_cast< Type >( buffer );
732       WriteBuffer(os, buf, numComp);
733       }
734       break;
735     case CHAR:
736       {
737       using Type = const char *;
738       auto buf = reinterpret_cast< Type >( buffer );
739       WriteBuffer(os, buf, numComp);
740       }
741       break;
742 
743     case USHORT:
744       {
745       using Type = const unsigned short *;
746       auto buf = reinterpret_cast< Type >( buffer );
747       WriteBuffer(os, buf, numComp);
748       }
749       break;
750 
751     case SHORT:
752       {
753       using Type = const short *;
754       auto buf = reinterpret_cast< Type >( buffer );
755       WriteBuffer(os, buf, numComp);
756       }
757       break;
758 
759     case UINT:
760       {
761       using Type = const unsigned int *;
762       auto buf = reinterpret_cast< Type >( buffer );
763       WriteBuffer(os, buf, numComp);
764       }
765       break;
766 
767     case INT:
768       {
769       using Type = const int *;
770       auto buf = reinterpret_cast< Type >( buffer );
771       WriteBuffer(os, buf, numComp);
772       }
773       break;
774 
775     case ULONG:
776       {
777       using Type = const unsigned long *;
778       auto buf = reinterpret_cast< Type >( buffer );
779       WriteBuffer(os, buf, numComp);
780       }
781       break;
782 
783     case LONG:
784       {
785       using Type = const long *;
786       auto buf = reinterpret_cast< Type >( buffer );
787       WriteBuffer(os, buf, numComp);
788       }
789       break;
790 
791     case ULONGLONG:
792       {
793       using Type = const unsigned long long *;
794       auto buf = reinterpret_cast< Type >( buffer );
795       WriteBuffer(os, buf, numComp);
796       }
797       break;
798 
799     case LONGLONG:
800       {
801       using Type = const long long *;
802       auto buf = reinterpret_cast< Type >( buffer );
803       WriteBuffer(os, buf, numComp);
804       }
805       break;
806 
807     case FLOAT:
808       {
809       using Type = const float *;
810       auto buf = reinterpret_cast< Type >( buffer );
811       WriteBuffer(os, buf, numComp);
812       }
813       break;
814 
815     case DOUBLE:
816       {
817       using Type = const double *;
818       auto buf = reinterpret_cast< Type >( buffer );
819       WriteBuffer(os, buf, numComp);
820       }
821       break;
822 
823     default:
824       break;
825     }
826 }
827 
828 namespace
829 {
830 template< typename TComponent >
ReadBuffer(std::istream & is,TComponent * buffer,ImageIOBase::SizeType num)831 void ReadBuffer(std::istream & is, TComponent *buffer, ImageIOBase::SizeType num)
832 {
833   using PrintType = typename itk::NumericTraits< TComponent >::PrintType;
834   PrintType   temp;
835   TComponent *ptr = buffer;
836   for ( ImageIOBase::SizeType i = 0; i < num; i++, ptr++ )
837     {
838     is >> temp;
839     *ptr = static_cast< TComponent >( temp );
840     }
841 }
842 }
ReadBufferAsASCII(std::istream & is,void * buffer,IOComponentType ctype,ImageIOBase::SizeType numComp)843 void ImageIOBase::ReadBufferAsASCII(std::istream & is, void *buffer,
844                                     IOComponentType ctype,
845                                     ImageIOBase::SizeType numComp)
846 {
847   switch ( ctype )
848     {
849     case UCHAR:
850       {
851       auto * buf = reinterpret_cast< unsigned char * >( buffer );
852       ReadBuffer(is, buf, numComp);
853       }
854       break;
855     case CHAR:
856       {
857       auto * buf = reinterpret_cast< char * >( buffer );
858       ReadBuffer(is, buf, numComp);
859       }
860       break;
861 
862     case USHORT:
863       {
864       auto * buf = reinterpret_cast< unsigned short * >( buffer );
865       ReadBuffer(is, buf, numComp);
866       }
867       break;
868 
869     case SHORT:
870       {
871       auto * buf = reinterpret_cast< short * >( buffer );
872       ReadBuffer(is, buf, numComp);
873       }
874       break;
875 
876     case UINT:
877       {
878       auto * buf = reinterpret_cast< unsigned int * >( buffer );
879       ReadBuffer(is, buf, numComp);
880       }
881       break;
882 
883     case INT:
884       {
885       auto * buf = reinterpret_cast< int * >( buffer );
886       ReadBuffer(is, buf, numComp);
887       }
888       break;
889 
890     case ULONG:
891       {
892       auto * buf = reinterpret_cast< unsigned long * >( buffer );
893       ReadBuffer(is, buf, numComp);
894       }
895       break;
896 
897     case LONG:
898       {
899       auto * buf = reinterpret_cast< long * >( buffer );
900       ReadBuffer(is, buf, numComp);
901       }
902       break;
903 
904     case ULONGLONG:
905       {
906       auto * buf = reinterpret_cast< unsigned long long * >( buffer );
907       ReadBuffer(is, buf, numComp);
908       }
909       break;
910 
911     case LONGLONG:
912       {
913       auto * buf = reinterpret_cast< long long * >( buffer );
914       ReadBuffer(is, buf, numComp);
915       }
916       break;
917 
918     case FLOAT:
919       {
920       auto * buf = reinterpret_cast< float * >( buffer );
921       ReadBuffer(is, buf, numComp);
922       }
923       break;
924 
925     case DOUBLE:
926       {
927       auto * buf = reinterpret_cast< double * >( buffer );
928       ReadBuffer(is, buf, numComp);
929       }
930       break;
931 
932     default:
933       break;
934     }
935 }
936 
937 namespace
938 {
939 std::mutex ioDefaultSplitterLock;
940 ImageRegionSplitterBase::Pointer ioDefaultSplitter;
941 
942 }
943 
944 const ImageRegionSplitterBase*
GetImageRegionSplitter() const945 ImageIOBase::GetImageRegionSplitter() const
946 {
947   if ( ioDefaultSplitter.IsNull() )
948     {
949     // thread safe lazy initialization,  prevent race condition on
950     // setting, with an atomic set if null.
951     std::lock_guard< std::mutex > lock( ioDefaultSplitterLock );
952     if ( ioDefaultSplitter.IsNull() )
953       {
954       ioDefaultSplitter = ImageRegionSplitterSlowDimension::New().GetPointer();
955       }
956     }
957   return ioDefaultSplitter;
958 }
959 
960 
961 bool
HasSupportedReadExtension(const char * fileName,bool ignoreCase)962 ImageIOBase::HasSupportedReadExtension( const char *fileName, bool ignoreCase )
963 {
964   return this->HasSupportedExtension( fileName,
965                                       this->GetSupportedReadExtensions(),
966                                       ignoreCase );
967 }
968 
969 
970 bool
HasSupportedWriteExtension(const char * fileName,bool ignoreCase)971 ImageIOBase::HasSupportedWriteExtension( const char *fileName, bool ignoreCase )
972 {
973 
974   return this->HasSupportedExtension( fileName,
975                                       this->GetSupportedWriteExtensions(),
976                                       ignoreCase );
977 }
978 
979 
980 bool
HasSupportedExtension(const char * filename,const ImageIOBase::ArrayOfExtensionsType & supportedExtensions,bool ignoreCase)981 ImageIOBase::HasSupportedExtension( const char * filename,
982                                     const ImageIOBase::ArrayOfExtensionsType &supportedExtensions,
983                                     bool ignoreCase )
984 {
985 
986   std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename);
987   if ( ignoreCase )
988     {
989     std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
990     }
991 
992   for ( auto && candidate : supportedExtensions )
993     {
994     if ( ignoreCase )
995       {
996       size_t n = candidate.size();
997       if ( n == ext.size() && n > 0)
998         {
999 
1000         while ( true )
1001           {
1002           --n;
1003           if (ext[n] != ::tolower(candidate[n]))
1004             {
1005             break;
1006             }
1007           if ( n == 0 )
1008             {
1009             return true;
1010             }
1011           }
1012 
1013         }
1014       }
1015     else
1016       {
1017       if ( candidate == ext )
1018         {
1019         return true;
1020         }
1021       }
1022     }
1023   return false;
1024 }
1025 
1026 unsigned int
GetActualNumberOfSplitsForWritingCanStreamWrite(unsigned int numberOfRequestedSplits,const ImageIORegion & pasteRegion) const1027 ImageIOBase::GetActualNumberOfSplitsForWritingCanStreamWrite(unsigned int numberOfRequestedSplits,
1028                                                              const ImageIORegion & pasteRegion) const
1029 {
1030   const ImageRegionSplitterBase* splitter = this->GetImageRegionSplitter();
1031   return splitter->GetNumberOfSplits(pasteRegion, numberOfRequestedSplits);
1032 }
1033 
1034 unsigned int
GetActualNumberOfSplitsForWriting(unsigned int numberOfRequestedSplits,const ImageIORegion & pasteRegion,const ImageIORegion & largestPossibleRegion)1035 ImageIOBase::GetActualNumberOfSplitsForWriting(unsigned int numberOfRequestedSplits,
1036                                                const ImageIORegion & pasteRegion,
1037                                                const ImageIORegion & largestPossibleRegion)
1038 {
1039   if ( this->CanStreamWrite() )
1040     {
1041     return GetActualNumberOfSplitsForWritingCanStreamWrite(numberOfRequestedSplits, pasteRegion);
1042     }
1043   if ( pasteRegion != largestPossibleRegion )
1044     {
1045     itkExceptionMacro( "Pasting is not supported! Can't write:" << this->GetFileName() );
1046     }
1047   if ( numberOfRequestedSplits != 1 )
1048     {
1049     itkDebugMacro("Requested more then 1 splits for streaming");
1050     itkDebugMacro("This IO class does not support streaming!");
1051     }
1052   return 1;
1053 }
1054 
1055 ImageIORegion
GetSplitRegionForWritingCanStreamWrite(unsigned int ithPiece,unsigned int numberOfActualSplits,const ImageIORegion & pasteRegion) const1056 ImageIOBase::GetSplitRegionForWritingCanStreamWrite(unsigned int ithPiece,
1057                                                     unsigned int numberOfActualSplits,
1058                                                     const ImageIORegion & pasteRegion) const
1059 {
1060   ImageIORegion splitRegion = pasteRegion;
1061 
1062   const ImageRegionSplitterBase* splitter = this->GetImageRegionSplitter();
1063   splitter->GetSplit( ithPiece, numberOfActualSplits, splitRegion );
1064 
1065   return splitRegion;
1066 }
1067 
1068 ImageIORegion
GetSplitRegionForWriting(unsigned int ithPiece,unsigned int numberOfActualSplits,const ImageIORegion & pasteRegion,const ImageIORegion & largestPossibleRegion)1069 ImageIOBase::GetSplitRegionForWriting(unsigned int ithPiece,
1070                                       unsigned int numberOfActualSplits,
1071                                       const ImageIORegion & pasteRegion,
1072                                       const ImageIORegion & largestPossibleRegion)
1073 {
1074   if ( this->CanStreamWrite() )
1075     {
1076     return GetSplitRegionForWritingCanStreamWrite(ithPiece, numberOfActualSplits, pasteRegion);
1077     }
1078   return largestPossibleRegion;
1079 }
1080 
1081 /** Given a requested region, determine what could be the region that we can
1082  * read from the file. This is called the streamable region, which will be
1083  * smaller than the LargestPossibleRegion and greater or equal to the
1084  * RequestedRegion */
1085 ImageIORegion
1086 ImageIOBase
GenerateStreamableReadRegionFromRequestedRegion(const ImageIORegion & requested) const1087 ::GenerateStreamableReadRegionFromRequestedRegion(
1088   const ImageIORegion & requested) const
1089 {
1090   //
1091   // The default implementations determines that the streamable region is
1092   // equal to the minimal size of the image in the file. That is two
1093   // say the return ImageIORegion::GetImageSizeInPixels() is equal to
1094   // the number in the file.
1095   //
1096 
1097   // Since the image in the file may have a lower or higher dimension
1098   // than the image type over which the ImageFileReader is
1099   // being instantiated we must choose an image dimension which will
1100   // represent all the pixels. That is we can trim trailing 1s.
1101 
1102   unsigned int minIODimension = this->m_NumberOfDimensions;
1103 
1104   while ( minIODimension )
1105     {
1106     if ( this->m_Dimensions[minIODimension - 1] == 1 )
1107       {
1108       --minIODimension;
1109       }
1110     else
1111       {
1112       break;
1113       }
1114     }
1115 
1116   // dimension size we use to represent the region
1117   unsigned int maxDimension =
1118     minIODimension > requested.GetImageDimension() ? minIODimension : requested.GetImageDimension();
1119 
1120   // First: allocate with the correct dimensions
1121   ImageIORegion streamableRegion(maxDimension);
1122 
1123   // Second: copy only the number of dimension that the file has.
1124   for ( unsigned int i = 0; i < minIODimension; i++ )
1125     {
1126     streamableRegion.SetSize(i, this->m_Dimensions[i]);
1127     streamableRegion.SetIndex(i, 0);
1128     }
1129 
1130   // Third: set the rest to the default : start = 0, size = 1
1131   for ( unsigned int j = minIODimension; j < streamableRegion.GetImageDimension(); j++ )
1132     {
1133     streamableRegion.SetSize(j, 1);
1134     streamableRegion.SetIndex(j, 0);
1135     }
1136 
1137   // Finally: return the streamable region
1138   return streamableRegion;
1139 }
1140 
1141 /** Return the directions that this particular ImageIO would use by default
1142  *  in the case the recipient image dimension is smaller than the dimension
1143  *  of the image in file. */
1144 std::vector< double >
1145 ImageIOBase
GetDefaultDirection(unsigned int k) const1146 ::GetDefaultDirection(unsigned int k) const
1147 {
1148   std::vector< double > axis;
1149   axis.resize( this->GetNumberOfDimensions() );
1150 
1151   // Fill up with the equivalent of a line from an Identity matrix
1152   for (double & axi : axis)
1153     {
1154     axi = 0.0;
1155     }
1156 
1157   axis[k] = 1.0;
1158 
1159   return axis;
1160 }
1161 
PrintSelf(std::ostream & os,Indent indent) const1162 void ImageIOBase::PrintSelf(std::ostream & os, Indent indent) const
1163 {
1164   using namespace print_helper;
1165 
1166   Superclass::PrintSelf(os, indent);
1167 
1168   os << indent << "FileName: " << m_FileName << std::endl;
1169   os << indent << "FileType: " << this->GetFileTypeAsString(m_FileType) << std::endl;
1170   os << indent << "ByteOrder: " << this->GetByteOrderAsString(m_ByteOrder) << std::endl;
1171   os << indent << "IORegion: " << std::endl;
1172   m_IORegion.Print( os, indent.GetNextIndent() );
1173   os << indent << "Number of Components/Pixel: " << m_NumberOfComponents << "\n";
1174   os << indent << "Pixel Type: " << this->GetPixelTypeAsString(m_PixelType) << std::endl;
1175   os << indent << "Component Type: " << this->GetComponentTypeAsString(m_ComponentType)
1176      << std::endl;
1177   os << indent << "Dimensions: " << m_Dimensions << std::endl;
1178   os << indent << "Origin: " << m_Origin << std::endl;
1179   os << indent << "Spacing: " << m_Spacing << std::endl;
1180   os << indent << "Direction: " << std::endl;
1181   for( const auto & direction : m_Direction )
1182     {
1183     os << indent << direction << std::endl;
1184     }
1185   if( m_UseCompression )
1186     {
1187     os << indent << "UseCompression: On" << std::endl;
1188     }
1189   else
1190     {
1191     os << indent << "UseCompression: Off" << std::endl;
1192     }
1193   os << indent << "CompressionLevel: " << m_CompressionLevel << std::endl;
1194   os << indent << "MaximumCompressionLevel: " << m_MaximumCompressionLevel << std::endl;
1195   os << indent << "Compressor: " << m_Compressor << std::endl;
1196   if( m_UseStreamedReading )
1197     {
1198     os << indent << "UseStreamedReading: On" << std::endl;
1199     }
1200   else
1201     {
1202     os << indent << "UseStreamedReading: Off" << std::endl;
1203     }
1204   if( m_UseStreamedWriting )
1205     {
1206     os << indent << "UseStreamedWriting: On" << std::endl;
1207     }
1208   else
1209     {
1210     os << indent << "UseStreamedWriting: Off" << std::endl;
1211     }
1212   if( m_ExpandRGBPalette )
1213     {
1214     os << indent << "ExpandRGBPalette: On" << std::endl;
1215     }
1216   else
1217     {
1218     os << indent << "ExpandRGBPalette: Off" << std::endl;
1219     }
1220   if( m_IsReadAsScalarPlusPalette )
1221     {
1222     os << indent << "IsReadAsScalarPlusPalette: True" << std::endl;
1223     }
1224   else
1225     {
1226     os << indent << "IsReadAsScalarPlusPalette: False" << std::endl;
1227     }
1228 }
1229 
1230 } //namespace itk
1231