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 "itkMacro.h"
20 #include "itkTIFFReaderInternal.h"
21 #include <sys/stat.h>
22 
23 namespace itk
24 {
25 
Open(const char * filename)26 int TIFFReaderInternal::Open(const char *filename)
27 {
28   this->Clean();
29   struct stat fs;
30   if ( stat(filename, &fs) )
31     {
32 #if defined(_WIN32) && ! defined(__MINGW32_VERSION)
33     struct _stat64 fs64;
34     if ( _stat64(filename, &fs64) )
35       {
36       // Both stat() and _stat64() return != 0
37       return 0;
38       }
39 #else
40     return 0;
41 #endif
42     }
43 
44   this->m_Image = TIFFOpen(filename, "r");
45   if ( !this->m_Image )
46     {
47     this->Clean();
48     return 0;
49     }
50   if ( !this->Initialize() )
51     {
52     this->Clean();
53     return 0;
54     }
55 
56   this->m_IsOpen = true;
57   return 1;
58 }
59 
Clean()60 void TIFFReaderInternal::Clean()
61 {
62   if ( this->m_Image )
63     {
64     TIFFClose(this->m_Image);
65     }
66   this->m_Image = nullptr;
67   this->m_Width = 0;
68   this->m_Height = 0;
69   this->m_SamplesPerPixel = 0;
70   this->m_Compression = 0;
71   this->m_BitsPerSample = 0;
72   this->m_Photometrics = 0;
73   this->m_HasValidPhotometricInterpretation = false;
74   this->m_PlanarConfig = 0;
75   this->m_CurrentPage = 0;
76   this->m_NumberOfPages = 0;
77   this->m_NumberOfTiles = 0;
78   this->m_Orientation = ORIENTATION_TOPLEFT;
79   this->m_TileRows = 0;
80   this->m_TileColumns = 0;
81   this->m_TileWidth = 0;
82   this->m_TileHeight = 0;
83   this->m_XResolution = 1;
84   this->m_YResolution = 1;
85   this->m_SubFiles = 0;
86   this->m_IgnoredSubFiles = 0;
87   this->m_SampleFormat = 1;
88   this->m_ResolutionUnit = 1; // none
89   this->m_IsOpen = false;
90 }
91 
92 
TIFFReaderInternal()93 TIFFReaderInternal::TIFFReaderInternal()
94 {
95   this->m_Image           = nullptr;
96   this->Clean();
97 }
98 
Initialize()99 int TIFFReaderInternal::Initialize()
100 {
101   if ( this->m_Image )
102     {
103     if ( !TIFFGetField(this->m_Image, TIFFTAG_IMAGEWIDTH, &this->m_Width)
104          || !TIFFGetField(this->m_Image, TIFFTAG_IMAGELENGTH, &this->m_Height) )
105       {
106       return 0;
107       }
108 
109     // Get the resolution in each direction
110     TIFFGetField(this->m_Image,
111                  TIFFTAG_XRESOLUTION, &this->m_XResolution);
112     TIFFGetField(this->m_Image,
113                  TIFFTAG_YRESOLUTION, &this->m_YResolution);
114     TIFFGetField(this->m_Image,
115                  TIFFTAG_RESOLUTIONUNIT, &this->m_ResolutionUnit);
116 
117     // Check the number of pages. First by looking at the number of directories
118     this->m_NumberOfPages = TIFFNumberOfDirectories(this->m_Image);
119 
120     if ( this->m_NumberOfPages == 0 )
121       {
122       itkGenericExceptionMacro("No directories found in TIFF file.");
123       }
124 
125     if ( TIFFIsTiled(this->m_Image) )
126       {
127       this->m_NumberOfTiles = TIFFNumberOfTiles(this->m_Image);
128 
129       if ( !TIFFGetField(this->m_Image, TIFFTAG_TILEWIDTH, &this->m_TileWidth)
130            || !TIFFGetField(this->m_Image, TIFFTAG_TILELENGTH, &this->m_TileHeight) )
131         {
132         itkGenericExceptionMacro(
133           << "Cannot read tile width and tile length from file");
134         }
135       else
136         {
137         this->m_TileRows = this->m_Height / this->m_TileHeight;
138         this->m_TileColumns = this->m_Width / this->m_TileWidth;
139         }
140       }
141 
142     // Checking if the TIFF contains subfiles
143     if ( this->m_NumberOfPages > 1 )
144       {
145       this->m_SubFiles = 0;
146       this->m_IgnoredSubFiles = 0;
147 
148       for ( unsigned int page = 0; page < this->m_NumberOfPages; page++ )
149         {
150         int32 subfiletype = 6;
151         if ( TIFFGetField(this->m_Image, TIFFTAG_SUBFILETYPE, &subfiletype) )
152           {
153           if ( subfiletype == 0 )
154             {
155             this->m_SubFiles += 1;
156             }
157           // ignored flags
158           else if ( subfiletype & FILETYPE_REDUCEDIMAGE
159                     || subfiletype & FILETYPE_MASK )
160             {
161             ++this->m_IgnoredSubFiles;
162             }
163 
164           }
165         TIFFReadDirectory(this->m_Image);
166         }
167 
168       // Set the directory to the first image, and reads it
169       TIFFSetDirectory(this->m_Image, 0);
170       }
171 
172     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_ORIENTATION,
173                           &this->m_Orientation);
174     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_SAMPLESPERPIXEL,
175                           &this->m_SamplesPerPixel);
176     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_COMPRESSION, &this->m_Compression);
177     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_BITSPERSAMPLE,
178                           &this->m_BitsPerSample);
179     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_PLANARCONFIG, &this->m_PlanarConfig);
180     TIFFGetFieldDefaulted(this->m_Image, TIFFTAG_SAMPLEFORMAT, &this->m_SampleFormat);
181 
182     // If TIFFGetField returns false, there's no Photometric Interpretation
183     // set for this image, but that's a required field so we set a warning flag.
184     // (Because the "Photometrics" field is an enum, we can't rely on setting
185     // this->m_Photometrics to some signal value.)
186     if ( TIFFGetField(this->m_Image, TIFFTAG_PHOTOMETRIC, &this->m_Photometrics) )
187       {
188       this->m_HasValidPhotometricInterpretation = true;
189       }
190     else
191       {
192       this->m_HasValidPhotometricInterpretation = false;
193       }
194     }
195 
196   return 1;
197 }
198 
CanRead()199 int TIFFReaderInternal::CanRead()
200 {
201   const bool compressionSupported = ( TIFFIsCODECConfigured(this->m_Compression) == 1 );
202   return ( this->m_Image && ( this->m_Width > 0 ) && ( this->m_Height > 0 )
203            && ( this->m_SamplesPerPixel > 0 )
204            && compressionSupported
205            && ( m_NumberOfTiles == 0 ) // just use TIFFReadRGBAImage, an
206                                        // native optimized version would be nice
207            && ( this->m_HasValidPhotometricInterpretation )
208            && ( this->m_Photometrics == PHOTOMETRIC_RGB
209                 || this->m_Photometrics == PHOTOMETRIC_MINISWHITE
210                 || this->m_Photometrics == PHOTOMETRIC_MINISBLACK
211                 || ( this->m_Photometrics == PHOTOMETRIC_PALETTE
212                      && this->m_BitsPerSample != 32 )
213                 )
214            && ( this->m_PlanarConfig == PLANARCONFIG_CONTIG
215                 || this->m_SamplesPerPixel == 1 )
216            && ( this->m_Orientation == ORIENTATION_TOPLEFT
217                 || this->m_Orientation == ORIENTATION_BOTLEFT )
218            && ( this->m_BitsPerSample == 8 || this->m_BitsPerSample == 16 || this->m_BitsPerSample == 32 ) );
219 }
220 
221 }
222