LCOV - code coverage report
Current view: top level - third_party/libertiff - libertiff.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 393 454 86.6 %
Date: 2025-07-11 10:11:13 Functions: 75 87 86.2 %

          Line data    Source code
       1             : // SPDX-License-Identifier: MIT
       2             : // Copyright 2024, Even Rouault <even.rouault at spatialys.com>
       3             : 
       4             : // Canonical URL: https://github.com/libertiff/libertiff/blob/master/libertiff.hpp
       5             : 
       6             : #ifndef LIBERTIFF_HPP_INCLUDED
       7             : #define LIBERTIFF_HPP_INCLUDED
       8             : 
       9             : //////////////////////////////////////////////////////////////
      10             : // libertiff = libre TIFF or LIB E(ven) R(ouault) TIFF... ? //
      11             : //////////////////////////////////////////////////////////////
      12             : 
      13             : #if __cplusplus >= 202002L
      14             : #include <bit>  // std::endian
      15             : #endif
      16             : 
      17             : #include <algorithm>
      18             : #include <array>
      19             : #include <cassert>
      20             : #include <cstring>
      21             : #include <limits>
      22             : #include <memory>
      23             : #include <set>
      24             : #include <string>
      25             : #include <type_traits>
      26             : #include <vector>
      27             : 
      28             : #ifndef LIBERTIFF_NS
      29             : #define LIBERTIFF_NS libertiff
      30             : #endif
      31             : 
      32             : /** Libertiff is a C++11 simple, header-only, TIFF reader. It is MIT licensed.
      33             :  *
      34             :  * Handles both ClassicTIFF and BigTIFF, little-endian or big-endian ordered.
      35             :  *
      36             :  * The library does not (yet?) offer codec facilities. It is mostly aimed at
      37             :  * browsing through the linked chain of Image File Directory (IFD) and their tags.
      38             :  *
      39             :  * "Offline" tag values are not loaded at IFD opening time, but only upon
      40             :  * request, which helps handling files with tags with an arbitrarily large
      41             :  * number of values.
      42             :  *
      43             :  * The library is thread-safe (that is the instances that it returns can
      44             :  * be used from multiple threads), if passed FileReader instances are themselves
      45             :  * thread-safe.
      46             :  *
      47             :  * The library does not throw exceptions (but underlying std library might
      48             :  * throw exceptions in case of out-of-memory situations)
      49             :  *
      50             :  * Optional features:
      51             :  * - define LIBERTIFF_C_FILE_READER before including libertiff.hpp, so that
      52             :  *   the libertiff::CFileReader class is available
      53             :  */
      54             : namespace LIBERTIFF_NS
      55             : {
      56             : 
      57             : #if __cplusplus >= 201703L
      58             : #define LIBERTIFF_STATIC_ASSERT(x) static_assert(x)
      59             : #define LIBERTIFF_CONSTEXPR constexpr
      60             : #else
      61             : #define LIBERTIFF_STATIC_ASSERT(x) static_assert((x), #x)
      62             : #define LIBERTIFF_CONSTEXPR
      63             : #endif
      64             : 
      65             : template <typename T, typename... Args>
      66        1446 : std::unique_ptr<T> make_unique(Args &&...args)
      67             : {
      68        1446 :     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
      69             : }
      70             : 
      71             : /** Returns whether the host is little-endian ordered */
      72        1342 : inline bool isHostLittleEndian()
      73             : {
      74             : #if __cplusplus >= 202002L
      75             :     return std::endian::native == std::endian::little;
      76             : #elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) &&          \
      77             :        (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) ||                         \
      78             :     defined(_MSC_VER)
      79        1342 :     return true;
      80             : #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) &&              \
      81             :     (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
      82             :     return false;
      83             : #else
      84             :     uint32_t one = 1;
      85             :     char one_as_char_array[sizeof(uint32_t)];
      86             :     std::memcpy(one_as_char_array, &one, sizeof(uint32_t));
      87             :     return one_as_char_array[0] == 1;
      88             : #endif
      89             : }
      90             : 
      91             : /** Byte-swap */
      92             : template <class T> inline T byteSwap(T v);
      93             : 
      94             : /** Byte-swap a uint8_t */
      95             : template <> inline uint8_t byteSwap(uint8_t v)
      96             : {
      97             :     return v;
      98             : }
      99             : 
     100             : /** Byte-swap a int8_t */
     101             : template <> inline int8_t byteSwap(int8_t v)
     102             : {
     103             :     return v;
     104             : }
     105             : 
     106             : /** Byte-swap a uint16_t */
     107        5931 : template <> inline uint16_t byteSwap(uint16_t v)
     108             : {
     109        5931 :     return uint16_t((v >> 8) | ((v & 0xff) << 8));
     110             : }
     111             : 
     112             : /** Byte-swap a int16_t */
     113             : template <> inline int16_t byteSwap(int16_t v)
     114             : {
     115             :     uint16_t u;
     116             :     LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
     117             :     std::memcpy(&u, &v, sizeof(u));
     118             :     u = byteSwap(u);
     119             :     std::memcpy(&v, &u, sizeof(u));
     120             :     return v;
     121             : }
     122             : 
     123             : /** Byte-swap a uint32_t */
     124      309705 : template <> inline uint32_t byteSwap(uint32_t v)
     125             : {
     126      309705 :     return (v >> 24) | (((v >> 16) & 0xff) << 8) | (((v >> 8) & 0xff) << 16) |
     127      309705 :            ((v & 0xff) << 24);
     128             : }
     129             : 
     130             : /** Byte-swap a int32_t */
     131           0 : template <> inline int32_t byteSwap(int32_t v)
     132             : {
     133             :     uint32_t u;
     134             :     LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
     135           0 :     std::memcpy(&u, &v, sizeof(u));
     136           0 :     u = byteSwap(u);
     137           0 :     std::memcpy(&v, &u, sizeof(u));
     138           0 :     return v;
     139             : }
     140             : 
     141             : /** Byte-swap a uint64_t */
     142      153395 : template <> inline uint64_t byteSwap(uint64_t v)
     143             : {
     144      153395 :     return (uint64_t(byteSwap(uint32_t(v & ~(0U)))) << 32) |
     145      153395 :            byteSwap(uint32_t(v >> 32));
     146             : }
     147             : 
     148             : /** Byte-swap a int64_t */
     149             : template <> inline int64_t byteSwap(int64_t v)
     150             : {
     151             :     uint64_t u;
     152             :     std::memcpy(&u, &v, sizeof(u));
     153             :     u = byteSwap(u);
     154             :     std::memcpy(&v, &u, sizeof(u));
     155             :     return v;
     156             : }
     157             : 
     158             : /** Byte-swap a float */
     159             : template <> inline float byteSwap(float v)
     160             : {
     161             :     uint32_t u;
     162             :     LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
     163             :     std::memcpy(&u, &v, sizeof(u));
     164             :     u = byteSwap(u);
     165             :     std::memcpy(&v, &u, sizeof(u));
     166             :     return v;
     167             : }
     168             : 
     169             : /** Byte-swap a double */
     170             : template <> inline double byteSwap(double v)
     171             : {
     172             :     uint64_t u;
     173             :     LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
     174             :     std::memcpy(&u, &v, sizeof(u));
     175             :     u = byteSwap(u);
     176             :     std::memcpy(&v, &u, sizeof(u));
     177             :     return v;
     178             : }
     179             : }  // namespace LIBERTIFF_NS
     180             : 
     181             : namespace LIBERTIFF_NS
     182             : {
     183             : #if defined(__clang__)
     184             : #pragma clang diagnostic push
     185             : #pragma clang diagnostic ignored "-Wweak-vtables"
     186             : #endif
     187             : 
     188             : /** Interface to read from a file. */
     189             : class FileReader
     190             : {
     191             :   public:
     192        1342 :     virtual ~FileReader() = default;
     193             : 
     194             :     /** Return file size in bytes */
     195             :     virtual uint64_t size() const = 0;
     196             : 
     197             :     /** Read 'count' bytes from offset 'offset' into 'buffer' and
     198             :      * return the number of bytes actually read.
     199             :      */
     200             :     virtual size_t read(uint64_t offset, size_t count, void *buffer) const = 0;
     201             : };
     202             : 
     203             : #if defined(__clang__)
     204             : #pragma clang diagnostic pop
     205             : #endif
     206             : }  // namespace LIBERTIFF_NS
     207             : 
     208             : namespace LIBERTIFF_NS
     209             : {
     210             : /** Read context: associates a file, and the byte ordering of the TIFF file */
     211             : class ReadContext
     212             : {
     213             :   public:
     214             :     /** Constructor */
     215        1342 :     ReadContext(const std::shared_ptr<const FileReader> &file,
     216             :                 bool mustByteSwap)
     217        1342 :         : m_file(file), m_mustByteSwap(mustByteSwap)
     218             :     {
     219        1342 :     }
     220             : 
     221             :     /** Return if values of more than 1-byte must be byte swapped.
     222             :      * To be only taken into account when reading pixels. Tag values are
     223             :      * automatically byte-swapped */
     224        6408 :     inline bool mustByteSwap() const
     225             :     {
     226        6408 :         return m_mustByteSwap;
     227             :     }
     228             : 
     229             :     /** Return file size */
     230         108 :     inline uint64_t size() const
     231             :     {
     232         108 :         return m_file->size();
     233             :     }
     234             : 
     235             :     /** Read count raw bytes at offset into buffer */
     236        7773 :     void read(uint64_t offset, size_t count, void *buffer, bool &ok) const
     237             :     {
     238        7773 :         if (m_file->read(offset, count, buffer) != count)
     239          17 :             ok = false;
     240        7821 :     }
     241             : 
     242             :     /** Read single value at offset */
     243       90014 :     template <class T> T read(uint64_t offset, bool &ok) const
     244             :     {
     245             : #if __cplusplus >= 201703L
     246             :         static_assert(
     247             :             std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
     248             :             std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
     249             :             std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
     250             :             std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t> ||
     251             :             std::is_same_v<T, float> || std::is_same_v<T, double>);
     252             : #endif
     253             : 
     254       90014 :         T res = 0;
     255       90014 :         if (m_file->read(offset, sizeof(res), &res) != sizeof(res))
     256             :         {
     257         186 :             ok = false;
     258         186 :             return 0;
     259             :         }
     260             :         if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
     261             :         {
     262       89805 :             if (m_mustByteSwap)
     263        8837 :                 res = byteSwap(res);
     264             :         }
     265       89805 :         return res;
     266             :     }
     267             : 
     268             :     /** Read a unsigned rational (type == Type::Rational) */
     269             :     template <class T = uint32_t>
     270           0 :     double readRational(uint64_t offset, bool &ok) const
     271             :     {
     272           0 :         const auto numerator = read<T>(offset, ok);
     273           0 :         const auto denominator = read<T>(offset + sizeof(T), ok);
     274           0 :         if (denominator == 0)
     275             :         {
     276           0 :             ok = false;
     277           0 :             return std::numeric_limits<double>::quiet_NaN();
     278             :         }
     279           0 :         return double(numerator) / denominator;
     280             :     }
     281             : 
     282             :     /** Read a signed rational (type == Type::SRational) */
     283           0 :     double readSignedRational(uint64_t offset, bool &ok) const
     284             :     {
     285           0 :         return readRational<int32_t>(offset, ok);
     286             :     }
     287             : 
     288             :     /** Read length bytes at offset (typically for ASCII tag) as a string */
     289         100 :     std::string readString(std::string &res, uint64_t offset, size_t length,
     290             :                            bool &ok) const
     291             :     {
     292         100 :         res.resize(length);
     293         100 :         if (length > 0 && m_file->read(offset, length, &res[0]) != length)
     294             :         {
     295           0 :             ok = false;
     296           0 :             res.clear();
     297           0 :             return res;
     298             :         }
     299             :         // Strip trailing nul byte if found
     300         100 :         if (length > 0 && res.back() == 0)
     301          99 :             res.pop_back();
     302         100 :         return res;
     303             :     }
     304             : 
     305             :     /** Read length bytes at offset (typically for ASCII tag) as a string */
     306         100 :     std::string readString(uint64_t offset, size_t length, bool &ok) const
     307             :     {
     308         100 :         std::string res;
     309         100 :         readString(res, offset, length, ok);
     310         100 :         return res;
     311             :     }
     312             : 
     313             :     /** Read an array of count values starting at offset */
     314             :     template <class T>
     315        1084 :     void readArray(std::vector<T> &array, uint64_t offset, size_t count,
     316             :                    bool &ok) const
     317             :     {
     318             : #if __cplusplus >= 201703L
     319             :         static_assert(
     320             :             std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t> ||
     321             :             std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t> ||
     322             :             std::is_same_v<T, uint32_t> || std::is_same_v<T, int32_t> ||
     323             :             std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t> ||
     324             :             std::is_same_v<T, float> || std::is_same_v<T, double>);
     325             : #endif
     326             : 
     327        1084 :         array.resize(count);
     328        1084 :         const size_t countBytes = count * sizeof(T);
     329        2168 :         if (count > 0 &&
     330        1084 :             m_file->read(offset, countBytes, &array[0]) != countBytes)
     331             :         {
     332          11 :             ok = false;
     333          11 :             array.clear();
     334             :         }
     335             :         else if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
     336             :         {
     337        1036 :             if (m_mustByteSwap)
     338             :             {
     339             :                 if LIBERTIFF_CONSTEXPR (std::is_same<T, float>::value)
     340             :                 {
     341             :                     uint32_t *uint32Array =
     342             :                         reinterpret_cast<uint32_t *>(array.data());
     343             :                     for (size_t i = 0; i < count; ++i)
     344             :                     {
     345             :                         uint32Array[i] = byteSwap(uint32Array[i]);
     346             :                     }
     347             :                 }
     348             :                 else if LIBERTIFF_CONSTEXPR (std::is_same<T, double>::value)
     349             :                 {
     350             :                     uint64_t *uint64Array =
     351          20 :                         reinterpret_cast<uint64_t *>(array.data());
     352      153194 :                     for (size_t i = 0; i < count; ++i)
     353             :                     {
     354      153174 :                         uint64Array[i] = byteSwap(uint64Array[i]);
     355             :                     }
     356             :                 }
     357             :                 else
     358             :                 {
     359         240 :                     for (size_t i = 0; i < count; ++i)
     360             :                     {
     361         230 :                         array[i] = byteSwap(array[i]);
     362             :                     }
     363             :                 }
     364             :             }
     365             :         }
     366        1084 :     }
     367             : 
     368             :     /** Read an array of count values starting at offset */
     369             :     template <class T>
     370        1084 :     std::vector<T> readArray(uint64_t offset, size_t count, bool &ok) const
     371             :     {
     372        1084 :         std::vector<T> array;
     373        1084 :         readArray(array, offset, count, ok);
     374        1084 :         return array;
     375             :     }
     376             : 
     377             :   private:
     378             :     const std::shared_ptr<const FileReader> m_file;
     379             :     const bool m_mustByteSwap;
     380             : };
     381             : }  // namespace LIBERTIFF_NS
     382             : 
     383             : namespace LIBERTIFF_NS
     384             : {
     385             : /** Type of a TIFF tag code */
     386             : typedef uint16_t TagCodeType;
     387             : 
     388             : /** TIFF tag codes */
     389             : namespace TagCode
     390             : {
     391             : constexpr TagCodeType SubFileType = 254;
     392             : constexpr TagCodeType OldSubFileType = 255;
     393             : 
     394             : // Base line and extended TIFF tags
     395             : constexpr TagCodeType ImageWidth = 256;
     396             : constexpr TagCodeType ImageLength = 257;
     397             : constexpr TagCodeType BitsPerSample = 258;
     398             : constexpr TagCodeType Compression = 259;
     399             : constexpr TagCodeType PhotometricInterpretation = 262;
     400             : constexpr TagCodeType DocumentName = 269;
     401             : constexpr TagCodeType ImageDescription = 270;
     402             : constexpr TagCodeType StripOffsets = 273;
     403             : constexpr TagCodeType SamplesPerPixel = 277;
     404             : constexpr TagCodeType RowsPerStrip = 278;
     405             : constexpr TagCodeType StripByteCounts = 279;
     406             : constexpr TagCodeType PlanarConfiguration = 284;
     407             : constexpr TagCodeType Software = 305;
     408             : constexpr TagCodeType DateTime = 306;
     409             : constexpr TagCodeType Predictor = 317;
     410             : constexpr TagCodeType ColorMap = 320;
     411             : constexpr TagCodeType TileWidth = 322;
     412             : constexpr TagCodeType TileLength = 323;
     413             : constexpr TagCodeType TileOffsets = 324;
     414             : constexpr TagCodeType TileByteCounts = 325;
     415             : constexpr TagCodeType ExtraSamples = 338;
     416             : constexpr TagCodeType SampleFormat = 339;
     417             : constexpr TagCodeType JPEGTables = 347;
     418             : 
     419             : constexpr TagCodeType Copyright = 33432;
     420             : 
     421             : // GeoTIFF tags
     422             : constexpr TagCodeType GeoTIFFPixelScale = 33550;
     423             : constexpr TagCodeType GeoTIFFTiePoints = 33922;
     424             : constexpr TagCodeType GeoTIFFGeoTransMatrix = 34264;
     425             : constexpr TagCodeType GeoTIFFGeoKeyDirectory = 34735;
     426             : constexpr TagCodeType GeoTIFFDoubleParams = 34736;
     427             : constexpr TagCodeType GeoTIFFAsciiParams = 34737;
     428             : 
     429             : // GDAL tags
     430             : constexpr TagCodeType GDAL_METADATA = 42112;
     431             : constexpr TagCodeType GDAL_NODATA = 42113;
     432             : 
     433             : // GeoTIFF related
     434             : constexpr TagCodeType RPCCoefficients = 50844;
     435             : 
     436             : // LERC compression related
     437             : constexpr TagCodeType LERCParameters =
     438             :     50674; /* Stores LERC version and additional compression method */
     439             : 
     440             : }  // namespace TagCode
     441             : 
     442             : /** Binary or'ed value of SubFileType flags */
     443             : namespace SubFileTypeFlags
     444             : {
     445             : constexpr uint32_t ReducedImage = 0x1; /* reduced resolution version */
     446             : constexpr uint32_t Page = 0x2;         /* one page of many */
     447             : constexpr uint32_t Mask = 0x4;         /* transparency mask */
     448             : }  // namespace SubFileTypeFlags
     449             : 
     450             : #define LIBERTIFF_CASE_TAGCODE_STR(x)                                          \
     451             :     case TagCode::x:                                                           \
     452             :         return #x
     453             : 
     454             : inline const char *tagCodeName(TagCodeType tagCode)
     455             : {
     456             :     switch (tagCode)
     457             :     {
     458             :         LIBERTIFF_CASE_TAGCODE_STR(SubFileType);
     459             :         LIBERTIFF_CASE_TAGCODE_STR(OldSubFileType);
     460             :         LIBERTIFF_CASE_TAGCODE_STR(ImageWidth);
     461             :         LIBERTIFF_CASE_TAGCODE_STR(ImageLength);
     462             :         LIBERTIFF_CASE_TAGCODE_STR(BitsPerSample);
     463             :         LIBERTIFF_CASE_TAGCODE_STR(Compression);
     464             :         LIBERTIFF_CASE_TAGCODE_STR(PhotometricInterpretation);
     465             :         LIBERTIFF_CASE_TAGCODE_STR(DocumentName);
     466             :         LIBERTIFF_CASE_TAGCODE_STR(ImageDescription);
     467             :         LIBERTIFF_CASE_TAGCODE_STR(StripOffsets);
     468             :         LIBERTIFF_CASE_TAGCODE_STR(SamplesPerPixel);
     469             :         LIBERTIFF_CASE_TAGCODE_STR(RowsPerStrip);
     470             :         LIBERTIFF_CASE_TAGCODE_STR(StripByteCounts);
     471             :         LIBERTIFF_CASE_TAGCODE_STR(PlanarConfiguration);
     472             :         LIBERTIFF_CASE_TAGCODE_STR(Software);
     473             :         LIBERTIFF_CASE_TAGCODE_STR(DateTime);
     474             :         LIBERTIFF_CASE_TAGCODE_STR(Predictor);
     475             :         LIBERTIFF_CASE_TAGCODE_STR(ColorMap);
     476             :         LIBERTIFF_CASE_TAGCODE_STR(TileWidth);
     477             :         LIBERTIFF_CASE_TAGCODE_STR(TileLength);
     478             :         LIBERTIFF_CASE_TAGCODE_STR(TileOffsets);
     479             :         LIBERTIFF_CASE_TAGCODE_STR(TileByteCounts);
     480             :         LIBERTIFF_CASE_TAGCODE_STR(ExtraSamples);
     481             :         LIBERTIFF_CASE_TAGCODE_STR(SampleFormat);
     482             :         LIBERTIFF_CASE_TAGCODE_STR(Copyright);
     483             :         LIBERTIFF_CASE_TAGCODE_STR(JPEGTables);
     484             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFPixelScale);
     485             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFTiePoints);
     486             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFGeoTransMatrix);
     487             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFGeoKeyDirectory);
     488             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFDoubleParams);
     489             :         LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFAsciiParams);
     490             :         LIBERTIFF_CASE_TAGCODE_STR(GDAL_METADATA);
     491             :         LIBERTIFF_CASE_TAGCODE_STR(GDAL_NODATA);
     492             :         LIBERTIFF_CASE_TAGCODE_STR(RPCCoefficients);
     493             :         LIBERTIFF_CASE_TAGCODE_STR(LERCParameters);
     494             :         default:
     495             :             break;
     496             :     }
     497             :     return "(unknown)";
     498             : }
     499             : 
     500             : #undef LIBERTIFF_CASE_TAGCODE_STR
     501             : 
     502             : /** Type of a TIFF tag type */
     503             : typedef uint16_t TagTypeType;
     504             : 
     505             : /** TIFF tag data types */
     506             : namespace TagType
     507             : {
     508             : constexpr TagTypeType Byte = 1;  /*! Unsigned 8-bit integer */
     509             : constexpr TagTypeType ASCII = 2; /*! Character */
     510             : constexpr TagTypeType Short = 3; /*! Unsigned 16-bit integer */
     511             : constexpr TagTypeType Long = 4;  /*! Unsigned 32-bit integer */
     512             : constexpr TagTypeType Rational =
     513             :     5; /*! Positive number as a ratio of two unsigned 32-bit integers */
     514             : constexpr TagTypeType SByte = 6;     /*! Signed 8-bit integer */
     515             : constexpr TagTypeType Undefined = 7; /*! Untyped 8-bit data */
     516             : constexpr TagTypeType SShort = 8;    /*! Signed 16-bit integer */
     517             : constexpr TagTypeType SLong = 9;     /*! Signed 32-bit integer */
     518             : constexpr TagTypeType SRational =
     519             :     10; /*! Signed number as a ratio of two signed 32-bit integers */
     520             : constexpr TagTypeType Float = 11;  /*! 32-bit IEEE-754 floating point number */
     521             : constexpr TagTypeType Double = 12; /*! 64-bit IEEE-754 floating point number */
     522             : 
     523             : // BigTIFF additions
     524             : constexpr TagTypeType Long8 = 16;  /*! Unsigned 64-bit integer */
     525             : constexpr TagTypeType SLong8 = 17; /*! Signed 64-bit integer */
     526             : constexpr TagTypeType IFD8 = 18;   /*! Unsigned 64-bit IFD offset */
     527             : }  // namespace TagType
     528             : 
     529             : #define LIBERTIFF_CASE_TAGTYPE_STR(x)                                          \
     530             :     case TagType::x:                                                           \
     531             :         return #x
     532             : 
     533             : inline const char *tagTypeName(TagTypeType tagType)
     534             : {
     535             :     switch (tagType)
     536             :     {
     537             :         LIBERTIFF_CASE_TAGTYPE_STR(Byte);
     538             :         LIBERTIFF_CASE_TAGTYPE_STR(ASCII);
     539             :         LIBERTIFF_CASE_TAGTYPE_STR(Short);
     540             :         LIBERTIFF_CASE_TAGTYPE_STR(Long);
     541             :         LIBERTIFF_CASE_TAGTYPE_STR(Rational);
     542             :         LIBERTIFF_CASE_TAGTYPE_STR(SByte);
     543             :         LIBERTIFF_CASE_TAGTYPE_STR(Undefined);
     544             :         LIBERTIFF_CASE_TAGTYPE_STR(SShort);
     545             :         LIBERTIFF_CASE_TAGTYPE_STR(SLong);
     546             :         LIBERTIFF_CASE_TAGTYPE_STR(SRational);
     547             :         LIBERTIFF_CASE_TAGTYPE_STR(Float);
     548             :         LIBERTIFF_CASE_TAGTYPE_STR(Double);
     549             :         LIBERTIFF_CASE_TAGTYPE_STR(Long8);
     550             :         LIBERTIFF_CASE_TAGTYPE_STR(SLong8);
     551             :         LIBERTIFF_CASE_TAGTYPE_STR(IFD8);
     552             :         default:
     553             :             break;
     554             :     }
     555             :     return "(unknown)";
     556             : }
     557             : 
     558             : #undef LIBERTIFF_CASE_TAGTYPE_STR
     559             : 
     560             : /** Type of a PlanarConfiguration value */
     561             : typedef uint32_t PlanarConfigurationType;
     562             : 
     563             : /** Values of the PlanarConfiguration tag */
     564             : namespace PlanarConfiguration
     565             : {
     566             : constexpr PlanarConfigurationType Contiguous = 1; /*! Single image plane */
     567             : constexpr PlanarConfigurationType Separate =
     568             :     2; /*! Separate planes per sample */
     569             : }  // namespace PlanarConfiguration
     570             : 
     571             : #define LIBERTIFF_CASE_PLANAR_CONFIG_STR(x)                                    \
     572             :     case PlanarConfiguration::x:                                               \
     573             :         return #x
     574             : 
     575             : inline const char *
     576             : planarConfigurationName(PlanarConfigurationType planarConfiguration)
     577             : {
     578             :     switch (planarConfiguration)
     579             :     {
     580             :         LIBERTIFF_CASE_PLANAR_CONFIG_STR(Contiguous);
     581             :         LIBERTIFF_CASE_PLANAR_CONFIG_STR(Separate);
     582             :         default:
     583             :             break;
     584             :     }
     585             :     return "(unknown)";
     586             : }
     587             : 
     588             : #undef LIBERTIFF_CASE_PLANAR_CONFIG_STR
     589             : 
     590             : /** Type of a PlanarConfiguration value */
     591             : typedef uint32_t PhotometricInterpretationType;
     592             : 
     593             : /** Values of the PhotometricInterpretation tag */
     594             : namespace PhotometricInterpretation
     595             : {
     596             : constexpr PhotometricInterpretationType MinIsWhite = 0;
     597             : constexpr PhotometricInterpretationType MinIsBlack = 1;
     598             : constexpr PhotometricInterpretationType RGB = 2;
     599             : constexpr PhotometricInterpretationType Palette = 3;
     600             : constexpr PhotometricInterpretationType Mask = 4;
     601             : constexpr PhotometricInterpretationType Separated = 5;
     602             : constexpr PhotometricInterpretationType YCbCr = 6;
     603             : constexpr PhotometricInterpretationType CIELab = 8;
     604             : constexpr PhotometricInterpretationType ICCLab = 9;
     605             : constexpr PhotometricInterpretationType ITULab = 10;
     606             : }  // namespace PhotometricInterpretation
     607             : 
     608             : #define LIBERTIFF_CASE_PHOTOMETRIC_STR(x)                                      \
     609             :     case PhotometricInterpretation::x:                                         \
     610             :         return #x
     611             : 
     612             : inline const char *photometricInterpretationName(
     613             :     PhotometricInterpretationType photometricInterpretation)
     614             : {
     615             :     switch (photometricInterpretation)
     616             :     {
     617             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsWhite);
     618             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsBlack);
     619             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(RGB);
     620             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(Palette);
     621             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(Mask);
     622             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(Separated);
     623             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(YCbCr);
     624             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(CIELab);
     625             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(ICCLab);
     626             :         LIBERTIFF_CASE_PHOTOMETRIC_STR(ITULab);
     627             :         default:
     628             :             break;
     629             :     }
     630             :     return "(unknown)";
     631             : }
     632             : 
     633             : #undef LIBERTIFF_CASE_PHOTOMETRIC_STR
     634             : 
     635             : /** Type of a Compression value */
     636             : typedef uint32_t CompressionType;
     637             : 
     638             : /** Compression methods */
     639             : namespace Compression
     640             : {
     641             : constexpr CompressionType None = 1;
     642             : constexpr CompressionType CCITT_RLE = 2;
     643             : constexpr CompressionType CCITT_FAX3 = 3;
     644             : constexpr CompressionType CCITT_FAX4 = 4;
     645             : constexpr CompressionType LZW = 5;
     646             : constexpr CompressionType OldJPEG = 6;
     647             : constexpr CompressionType JPEG = 7;
     648             : constexpr CompressionType Deflate =
     649             :     8; /* Deflate compression, as recognized by Adobe */
     650             : constexpr CompressionType PackBits = 32773;
     651             : constexpr CompressionType LegacyDeflate =
     652             :     32946;                              /* Deflate compression, legacy tag */
     653             : constexpr CompressionType JBIG = 34661; /* ISO JBIG */
     654             : constexpr CompressionType LERC =
     655             :     34887; /* ESRI Lerc codec: https://github.com/Esri/lerc */
     656             : constexpr CompressionType LZMA = 34925; /* LZMA2 */
     657             : constexpr CompressionType ZSTD =
     658             :     50000; /* ZSTD: WARNING not registered in Adobe-maintained registry */
     659             : constexpr CompressionType WEBP =
     660             :     50001; /* WEBP: WARNING not registered in Adobe-maintained registry */
     661             : constexpr CompressionType JXL =
     662             :     50002; /* JPEGXL: WARNING not registered in Adobe-maintained registry */
     663             : constexpr CompressionType JXL_DNG_1_7 =
     664             :     52546; /* JPEGXL from DNG 1.7 specification */
     665             : }  // namespace Compression
     666             : 
     667             : #define LIBERTIFF_CASE_COMPRESSION_STR(x)                                      \
     668             :     case Compression::x:                                                       \
     669             :         return #x
     670             : 
     671          18 : inline const char *compressionName(CompressionType compression)
     672             : {
     673          18 :     switch (compression)
     674             :     {
     675           0 :         LIBERTIFF_CASE_COMPRESSION_STR(None);
     676           1 :         LIBERTIFF_CASE_COMPRESSION_STR(CCITT_RLE);
     677           0 :         LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX3);
     678           1 :         LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX4);
     679           0 :         LIBERTIFF_CASE_COMPRESSION_STR(LZW);
     680           2 :         LIBERTIFF_CASE_COMPRESSION_STR(OldJPEG);
     681           0 :         LIBERTIFF_CASE_COMPRESSION_STR(JPEG);
     682           0 :         LIBERTIFF_CASE_COMPRESSION_STR(Deflate);
     683           0 :         LIBERTIFF_CASE_COMPRESSION_STR(PackBits);
     684           0 :         LIBERTIFF_CASE_COMPRESSION_STR(LegacyDeflate);
     685           0 :         LIBERTIFF_CASE_COMPRESSION_STR(JBIG);
     686           0 :         LIBERTIFF_CASE_COMPRESSION_STR(LERC);
     687           0 :         LIBERTIFF_CASE_COMPRESSION_STR(LZMA);
     688           0 :         LIBERTIFF_CASE_COMPRESSION_STR(ZSTD);
     689           0 :         LIBERTIFF_CASE_COMPRESSION_STR(WEBP);
     690           0 :         LIBERTIFF_CASE_COMPRESSION_STR(JXL);
     691           0 :         LIBERTIFF_CASE_COMPRESSION_STR(JXL_DNG_1_7);
     692          14 :         default:
     693          14 :             break;
     694             :     }
     695          14 :     return "(unknown)";
     696             : }
     697             : 
     698             : #undef LIBERTIFF_CASE_COMPRESSION_STR
     699             : 
     700             : /** Type of a SampleFormat value */
     701             : typedef uint32_t SampleFormatType;
     702             : 
     703             : /** Sample format */
     704             : namespace SampleFormat
     705             : {
     706             : constexpr SampleFormatType UnsignedInt = 1;
     707             : constexpr SampleFormatType SignedInt = 2;
     708             : constexpr SampleFormatType IEEEFP = 3;
     709             : constexpr SampleFormatType Void = 4;
     710             : constexpr SampleFormatType ComplexInt = 5;
     711             : constexpr SampleFormatType ComplexIEEEFP = 6;
     712             : }  // namespace SampleFormat
     713             : 
     714             : #define LIBERTIFF_CASE_SAMPLE_FORMAT_STR(x)                                    \
     715             :     case SampleFormat::x:                                                      \
     716             :         return #x
     717             : 
     718             : inline const char *sampleFormatName(SampleFormatType sampleFormat)
     719             : {
     720             :     switch (sampleFormat)
     721             :     {
     722             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(UnsignedInt);
     723             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(SignedInt);
     724             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(IEEEFP);
     725             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(Void);
     726             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexInt);
     727             :         LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexIEEEFP);
     728             :         default:
     729             :             break;
     730             :     }
     731             :     return "(unknown)";
     732             : }
     733             : 
     734             : #undef LIBERTIFF_CASE_SAMPLE_FORMAT_STR
     735             : 
     736             : /** Type of a ExtraSamples value */
     737             : typedef uint32_t ExtraSamplesType;
     738             : 
     739             : /** Values of the ExtraSamples tag */
     740             : namespace ExtraSamples
     741             : {
     742             : constexpr ExtraSamplesType Unspecified = 0;
     743             : constexpr ExtraSamplesType AssociatedAlpha = 1;   /* premultiplied */
     744             : constexpr ExtraSamplesType UnAssociatedAlpha = 2; /* unpremultiplied */
     745             : }  // namespace ExtraSamples
     746             : 
     747             : /** Content of a tag entry in a Image File Directory (IFD) */
     748             : struct TagEntry
     749             : {
     750             :     TagCodeType tag = 0;
     751             :     TagTypeType type = 0;
     752             :     uint64_t count = 0;  // number of values in the tag
     753             : 
     754             :     // Inline values. Only valid if value_offset == 0.
     755             :     // The actual number in the arrays is count
     756             :     union
     757             :     {
     758             :         std::array<char, 8> charValues;
     759             :         std::array<uint8_t, 8> uint8Values;
     760             :         std::array<int8_t, 8> int8Values;
     761             :         std::array<uint16_t, 4> uint16Values;
     762             :         std::array<int16_t, 4> int16Values;
     763             :         std::array<uint32_t, 2> uint32Values;
     764             :         std::array<int32_t, 2> int32Values;
     765             :         std::array<float, 2> float32Values;
     766             :         std::array<double, 1>
     767             :             float64Values;  // Valid for Double, Rational, SRational
     768             :         std::array<uint64_t, 1> uint64Values = {0};
     769             :         std::array<int64_t, 1> int64Values;
     770             :     };
     771             : 
     772             :     uint64_t value_offset = 0;         // 0 for inline values
     773             :     bool invalid_value_offset = true;  // whether value_offset is invalid
     774             : };
     775             : 
     776             : // clang-format off
     777             : 
     778             : /** Return the size in bytes of a tag data type, or 0 if unknown */
     779       17402 : inline uint32_t tagTypeSize(TagTypeType type)
     780             : {
     781       17402 :     switch (type)
     782             :     {
     783          13 :         case TagType::Byte:      return 1;
     784         505 :         case TagType::ASCII:     return 1;
     785       12704 :         case TagType::Short:     return 2;
     786        2510 :         case TagType::Long:      return 4;
     787          84 :         case TagType::Rational:  return 8;  // 2 Long
     788           0 :         case TagType::SByte:     return 1;
     789          38 :         case TagType::Undefined: return 1;
     790           0 :         case TagType::SShort:    return 2;
     791           0 :         case TagType::SLong:     return 4;
     792           0 :         case TagType::SRational: return 8;  // 2 SLong
     793           0 :         case TagType::Float:     return 4;
     794         933 :         case TagType::Double:    return 8;
     795          41 :         case TagType::Long8:     return 8;
     796           1 :         case TagType::SLong8:    return 8;
     797           0 :         case TagType::IFD8:      return 8;
     798         573 :         default: break;
     799             :     }
     800         573 :     return 0;
     801             : }
     802             : 
     803             : // clang-format on
     804             : 
     805             : namespace detail
     806             : {
     807             : template <class T>
     808        1099 : inline std::vector<T> readTagAsVectorInternal(const ReadContext &rc,
     809             :                                               const TagEntry &tag,
     810             :                                               TagTypeType expectedType,
     811             :                                               const T *inlineValues, bool &ok)
     812             : {
     813        1099 :     if (tag.type == expectedType)
     814             :     {
     815        1099 :         if (tag.value_offset)
     816             :         {
     817        1076 :             if (!tag.invalid_value_offset)
     818             :             {
     819             :                 if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t))
     820             :                 {
     821             :                     if (tag.count > std::numeric_limits<size_t>::max())
     822             :                     {
     823             :                         ok = false;
     824             :                         return {};
     825             :                     }
     826             :                 }
     827        1076 :                 return rc.readArray<T>(tag.value_offset,
     828        1076 :                                        static_cast<size_t>(tag.count), ok);
     829             :             }
     830             :         }
     831             :         else
     832             :         {
     833             :             return std::vector<T>(
     834          23 :                 inlineValues, inlineValues + static_cast<size_t>(tag.count));
     835             :         }
     836             :     }
     837           0 :     ok = false;
     838           0 :     return {};
     839             : }
     840             : 
     841             : template <class T>
     842             : inline std::vector<T> readTagAsVector(const ReadContext &rc,
     843             :                                       const TagEntry &tag, bool &ok);
     844             : 
     845             : template <>
     846             : inline std::vector<int8_t> readTagAsVector(const ReadContext &rc,
     847             :                                            const TagEntry &tag, bool &ok)
     848             : {
     849             :     return readTagAsVectorInternal(rc, tag, TagType::SByte,
     850             :                                    tag.int8Values.data(), ok);
     851             : }
     852             : 
     853             : template <>
     854          37 : inline std::vector<uint8_t> readTagAsVector(const ReadContext &rc,
     855             :                                             const TagEntry &tag, bool &ok)
     856             : {
     857             :     return readTagAsVectorInternal(
     858          74 :         rc, tag, tag.type == TagType::Undefined ? tag.type : TagType::Byte,
     859          37 :         tag.uint8Values.data(), ok);
     860             : }
     861             : 
     862             : template <>
     863             : inline std::vector<int16_t> readTagAsVector(const ReadContext &rc,
     864             :                                             const TagEntry &tag, bool &ok)
     865             : {
     866             :     return readTagAsVectorInternal(rc, tag, TagType::SShort,
     867             :                                    tag.int16Values.data(), ok);
     868             : }
     869             : 
     870             : template <>
     871         351 : inline std::vector<uint16_t> readTagAsVector(const ReadContext &rc,
     872             :                                              const TagEntry &tag, bool &ok)
     873             : {
     874             :     return readTagAsVectorInternal(rc, tag, TagType::Short,
     875         351 :                                    tag.uint16Values.data(), ok);
     876             : }
     877             : 
     878             : template <>
     879             : inline std::vector<int32_t> readTagAsVector(const ReadContext &rc,
     880             :                                             const TagEntry &tag, bool &ok)
     881             : {
     882             :     return readTagAsVectorInternal(rc, tag, TagType::SLong,
     883             :                                    tag.int32Values.data(), ok);
     884             : }
     885             : 
     886             : template <>
     887          73 : inline std::vector<uint32_t> readTagAsVector(const ReadContext &rc,
     888             :                                              const TagEntry &tag, bool &ok)
     889             : {
     890             :     return readTagAsVectorInternal(rc, tag, TagType::Long,
     891          73 :                                    tag.uint32Values.data(), ok);
     892             : }
     893             : 
     894             : template <>
     895             : inline std::vector<int64_t> readTagAsVector(const ReadContext &rc,
     896             :                                             const TagEntry &tag, bool &ok)
     897             : {
     898             :     return readTagAsVectorInternal(rc, tag, TagType::SLong8,
     899             :                                    tag.int64Values.data(), ok);
     900             : }
     901             : 
     902             : template <>
     903           0 : inline std::vector<uint64_t> readTagAsVector(const ReadContext &rc,
     904             :                                              const TagEntry &tag, bool &ok)
     905             : {
     906             :     return readTagAsVectorInternal(rc, tag, TagType::Long8,
     907           0 :                                    tag.uint64Values.data(), ok);
     908             : }
     909             : 
     910             : template <>
     911             : inline std::vector<float> readTagAsVector(const ReadContext &rc,
     912             :                                           const TagEntry &tag, bool &ok)
     913             : {
     914             :     return readTagAsVectorInternal(rc, tag, TagType::Float,
     915             :                                    tag.float32Values.data(), ok);
     916             : }
     917             : 
     918             : template <>
     919         638 : inline std::vector<double> readTagAsVector(const ReadContext &rc,
     920             :                                            const TagEntry &tag, bool &ok)
     921             : {
     922             :     return readTagAsVectorInternal(rc, tag, TagType::Double,
     923         638 :                                    tag.float64Values.data(), ok);
     924             : }
     925             : 
     926             : }  // namespace detail
     927             : 
     928             : /** Represents a TIFF Image File Directory (IFD). */
     929             : class Image
     930             : {
     931             :   public:
     932             :     /** Constructor. Should not be called directly. Use the open() method */
     933        1446 :     Image(const std::shared_ptr<const ReadContext> &rc, bool isBigTIFF)
     934        1446 :         : m_rc(rc), m_isBigTIFF(isBigTIFF)
     935             :     {
     936        1446 :     }
     937             : 
     938             :     /** Return read context */
     939       14318 :     const std::shared_ptr<const ReadContext> &readContext() const
     940             :     {
     941       14318 :         return m_rc;
     942             :     }
     943             : 
     944             :     /** Return whether the file is BigTIFF (if false, classic TIFF) */
     945             :     inline bool isBigTIFF() const
     946             :     {
     947             :         return m_isBigTIFF;
     948             :     }
     949             : 
     950             :     /** Return if values of more than 1-byte must be byte swapped.
     951             :      * To be only taken into account when reading pixels. Tag values are
     952             :      * automatically byte-swapped */
     953           4 :     inline bool mustByteSwap() const
     954             :     {
     955           4 :         return m_rc->mustByteSwap();
     956             :     }
     957             : 
     958             :     /** Return the offset of the this IFD */
     959           3 :     inline uint64_t offset() const
     960             :     {
     961           3 :         return m_offset;
     962             :     }
     963             : 
     964             :     /** Return the offset of the next IFD (to pass to Image::open()),
     965             :          * or 0 if there is no more */
     966         111 :     inline uint64_t nextImageOffset() const
     967             :     {
     968         111 :         return m_nextImageOffset;
     969             :     }
     970             : 
     971             :     /** Return value of SubFileType tag */
     972        2447 :     inline uint32_t subFileType() const
     973             :     {
     974        2447 :         return m_subFileType;
     975             :     }
     976             : 
     977             :     /** Return width of the image in pixels */
     978        3532 :     inline uint32_t width() const
     979             :     {
     980        3532 :         return m_width;
     981             :     }
     982             : 
     983             :     /** Return height of the image in pixels */
     984        3819 :     inline uint32_t height() const
     985             :     {
     986        3819 :         return m_height;
     987             :     }
     988             : 
     989             :     /** Return number of bits per sample */
     990      155834 :     inline uint32_t bitsPerSample() const
     991             :     {
     992      155834 :         return m_bitsPerSample;
     993             :     }
     994             : 
     995             :     /** Return number of samples (a.k.a. channels, bands) per pixel */
     996        5213 :     inline uint32_t samplesPerPixel() const
     997             :     {
     998        5213 :         return m_samplesPerPixel;
     999             :     }
    1000             : 
    1001             :     /** Return planar configuration */
    1002       13560 :     inline PlanarConfigurationType planarConfiguration() const
    1003             :     {
    1004       13560 :         return m_planarConfiguration;
    1005             :     }
    1006             : 
    1007             :     /** Return planar configuration */
    1008      137606 :     inline PhotometricInterpretationType photometricInterpretation() const
    1009             :     {
    1010      137606 :         return m_photometricInterpretation;
    1011             :     }
    1012             : 
    1013             :     /** Return compression method used */
    1014       78992 :     inline CompressionType compression() const
    1015             :     {
    1016       78992 :         return m_compression;
    1017             :     }
    1018             : 
    1019             :     /** Return predictor value (used for Deflate, LZW, ZStd, etc. compression) */
    1020       15145 :     inline uint32_t predictor() const
    1021             :     {
    1022       15145 :         return m_predictor;
    1023             :     }
    1024             : 
    1025             :     /** Return sample format */
    1026        1334 :     inline SampleFormatType sampleFormat() const
    1027             :     {
    1028        1334 :         return m_sampleFormat;
    1029             :     }
    1030             : 
    1031             :     /** Return the number of rows per strip */
    1032           6 :     inline uint32_t rowsPerStrip() const
    1033             :     {
    1034           6 :         return m_rowsPerStrip;
    1035             :     }
    1036             : 
    1037             :     /** Return the sanitized number of rows per strip */
    1038        2268 :     inline uint32_t rowsPerStripSanitized() const
    1039             :     {
    1040        2268 :         return std::min(m_rowsPerStrip, m_height);
    1041             :     }
    1042             : 
    1043             :     /** Return the number of strips/tiles.
    1044             :      * Return 0 if inconsistent values between ByteCounts and Offsets arrays. */
    1045           6 :     inline uint64_t strileCount() const
    1046             :     {
    1047           6 :         return m_strileCount;
    1048             :     }
    1049             : 
    1050             :     /** Return whether image is tiled */
    1051       26173 :     inline bool isTiled() const
    1052             :     {
    1053       26173 :         return m_isTiled;
    1054             :     }
    1055             : 
    1056             :     /** Return tile width */
    1057         423 :     inline uint32_t tileWidth() const
    1058             :     {
    1059         423 :         return m_tileWidth;
    1060             :     }
    1061             : 
    1062             :     /** Return tile width */
    1063         419 :     inline uint32_t tileHeight() const
    1064             :     {
    1065         419 :         return m_tileHeight;
    1066             :     }
    1067             : 
    1068             :     /** Return number of tiles per row */
    1069        6401 :     uint32_t tilesPerRow() const
    1070             :     {
    1071        6401 :         if (m_tileWidth > 0)
    1072             :         {
    1073        6396 :             return uint32_t((uint64_t(m_width) + m_tileWidth - 1) /
    1074        6396 :                             m_tileWidth);
    1075             :         }
    1076           5 :         return 0;
    1077             :     }
    1078             : 
    1079             :     /** Return number of tiles per column */
    1080        6385 :     uint32_t tilesPerCol() const
    1081             :     {
    1082        6385 :         if (m_tileHeight > 0)
    1083             :         {
    1084        6408 :             return uint32_t((uint64_t(m_height) + m_tileHeight - 1) /
    1085        6408 :                             m_tileHeight);
    1086             :         }
    1087           0 :         return 0;
    1088             :     }
    1089             : 
    1090             :     /** Convert a tile coordinate (xtile, ytile, bandIdx) to a flat index */
    1091        6398 :     uint64_t tileCoordinateToIdx(uint32_t xtile, uint32_t ytile,
    1092             :                                  uint32_t bandIdx, bool &ok) const
    1093             :     {
    1094        6398 :         if (m_isTiled && m_tileWidth > 0 && m_tileHeight > 0)
    1095             :         {
    1096        6388 :             const uint32_t lTilesPerRow = tilesPerRow();
    1097        6392 :             const uint32_t lTilesPerCol = tilesPerCol();
    1098        6399 :             if (xtile >= lTilesPerRow || ytile >= lTilesPerCol)
    1099             :             {
    1100           0 :                 ok = false;
    1101           0 :                 return 0;
    1102             :             }
    1103        6399 :             uint64_t idx = uint64_t(ytile) * lTilesPerRow + xtile;
    1104        6399 :             if (bandIdx &&
    1105        3659 :                 m_planarConfiguration == PlanarConfiguration::Separate)
    1106             :             {
    1107        3659 :                 const uint64_t lTotalTiles =
    1108        3659 :                     uint64_t(lTilesPerCol) * lTilesPerRow;
    1109        3657 :                 if (lTotalTiles >
    1110        3659 :                     std::numeric_limits<uint64_t>::max() / bandIdx)
    1111             :                 {
    1112           0 :                     ok = false;
    1113           0 :                     return 0;
    1114             :                 }
    1115        3657 :                 idx += bandIdx * lTotalTiles;
    1116             :             }
    1117        6397 :             return idx;
    1118             :         }
    1119          10 :         ok = false;
    1120          10 :         return 0;
    1121             :     }
    1122             : 
    1123             :     /** Return the offset of strip/tile of index idx */
    1124        8083 :     uint64_t strileOffset(uint64_t idx, bool &ok) const
    1125             :     {
    1126        8083 :         return readUIntTag(m_strileOffsetsTag, idx, ok);
    1127             :     }
    1128             : 
    1129             :     /** Return the offset of a tile from its coordinates */
    1130             :     uint64_t tileOffset(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
    1131             :                         bool &ok) const
    1132             :     {
    1133             :         const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
    1134             :         return ok ? strileOffset(idx, ok) : 0;
    1135             :     }
    1136             : 
    1137             :     /** Return the byte count of strip/tile of index idx */
    1138        7927 :     uint64_t strileByteCount(uint64_t idx, bool &ok) const
    1139             :     {
    1140        7927 :         return readUIntTag(m_strileByteCountsTag, idx, ok);
    1141             :     }
    1142             : 
    1143             :     /** Return the offset of a tile from its coordinates */
    1144             :     uint64_t tileByteCount(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
    1145             :                            bool &ok) const
    1146             :     {
    1147             :         const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
    1148             :         return ok ? strileByteCount(idx, ok) : 0;
    1149             :     }
    1150             : 
    1151             :     /** Return the list of tags */
    1152             :     inline const std::vector<TagEntry> &tags() const
    1153             :     {
    1154             :         return m_tags;
    1155             :     }
    1156             : 
    1157             :     /** Return the (first) tag corresponding to a code, or nullptr if not found */
    1158       16597 :     const TagEntry *tag(TagCodeType tagCode) const
    1159             :     {
    1160      212547 :         for (const auto &tag : m_tags)
    1161             :         {
    1162      199987 :             if (tag.tag == tagCode)
    1163        4037 :                 return &tag;
    1164             :         }
    1165       12560 :         return nullptr;
    1166             :     }
    1167             : 
    1168             :     /** Read an ASCII tag as a string */
    1169         121 :     std::string readTagAsString(const TagEntry &tag, bool &ok) const
    1170             :     {
    1171         121 :         if (tag.type == TagType::ASCII)
    1172             :         {
    1173         121 :             if (tag.value_offset)
    1174             :             {
    1175             :                 if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t))
    1176             :                 {
    1177             :                     // "- 1" not strictly necessary, but pleases Coverity Scan
    1178             :                     if (tag.count > std::numeric_limits<size_t>::max() - 1)
    1179             :                     {
    1180             :                         ok = false;
    1181             :                         return std::string();
    1182             :                     }
    1183             :                 }
    1184          98 :                 return readContext()->readString(
    1185          98 :                     tag.value_offset, static_cast<size_t>(tag.count), ok);
    1186             :             }
    1187          23 :             if (tag.count)
    1188             :             {
    1189             :                 std::string res(tag.charValues.data(),
    1190          46 :                                 static_cast<size_t>(tag.count));
    1191          23 :                 if (res.back() == 0)
    1192          23 :                     res.pop_back();
    1193          23 :                 return res;
    1194             :             }
    1195             :         }
    1196           0 :         ok = false;
    1197           0 :         return std::string();
    1198             :     }
    1199             : 
    1200             :     /** Read a numeric tag as a vector. You must use a type T which is
    1201             :      * consistent with the tag.type value. For example, if
    1202             :      * tag.type == libertiff::TagType::Short, T must be uint16_t.
    1203             :      * libertiff::TagType::Undefined must be read with T=uint8_t.
    1204             :      */
    1205             :     template <class T>
    1206        1099 :     std::vector<T> readTagAsVector(const TagEntry &tag, bool &ok) const
    1207             :     {
    1208        1099 :         return detail::readTagAsVector<T>(*(m_rc.get()), tag, ok);
    1209             :     }
    1210             : 
    1211             :     /** Returns a new Image instance for the IFD starting at offset imageOffset */
    1212             :     template <bool isBigTIFF>
    1213             :     static std::unique_ptr<const Image>
    1214        2293 :     open(const std::shared_ptr<const ReadContext> &rc,
    1215             :          const uint64_t imageOffset,
    1216             :          const std::set<uint64_t> &alreadyVisitedImageOffsets =
    1217             :              std::set<uint64_t>())
    1218             :     {
    1219             :         // To prevent infinite looping on corrupted files
    1220        3739 :         if (imageOffset == 0 || alreadyVisitedImageOffsets.find(imageOffset) !=
    1221        3739 :                                     alreadyVisitedImageOffsets.end())
    1222             :         {
    1223         847 :             return nullptr;
    1224             :         }
    1225             : 
    1226        2892 :         auto image = LIBERTIFF_NS::make_unique<Image>(rc, isBigTIFF);
    1227             : 
    1228        1446 :         image->m_offset = imageOffset;
    1229        1446 :         image->m_alreadyVisitedImageOffsets = alreadyVisitedImageOffsets;
    1230        1446 :         image->m_alreadyVisitedImageOffsets.insert(imageOffset);
    1231             : 
    1232        1446 :         bool ok = true;
    1233        1446 :         int tagCount = 0;
    1234        1446 :         uint64_t offset = imageOffset;
    1235             :         if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1236             :         {
    1237             :             // To prevent unsigned integer overflows in later additions. The
    1238             :             // theoretical max should be much closer to UINT64_MAX, but half of
    1239             :             // it is already more than needed in practice :-)
    1240          29 :             if (offset >= std::numeric_limits<uint64_t>::max() / 2)
    1241           0 :                 return nullptr;
    1242             : 
    1243          29 :             const auto tagCount64Bit = rc->read<uint64_t>(offset, ok);
    1244             :             // Artificially limit to the same number of entries as ClassicTIFF
    1245          29 :             if (tagCount64Bit > std::numeric_limits<uint16_t>::max())
    1246           0 :                 return nullptr;
    1247          29 :             tagCount = static_cast<int>(tagCount64Bit);
    1248          29 :             offset += sizeof(uint64_t);
    1249             :         }
    1250             :         else
    1251             :         {
    1252        1417 :             tagCount = rc->read<uint16_t>(offset, ok);
    1253        1417 :             offset += sizeof(uint16_t);
    1254             :         }
    1255        1446 :         if (!ok)
    1256          78 :             return nullptr;
    1257        1368 :         image->m_tags.reserve(tagCount);
    1258        1368 :         assert(tagCount <= 65535);
    1259       18814 :         for (int i = 0; i < tagCount; ++i)
    1260             :         {
    1261       17489 :             TagEntry entry;
    1262             : 
    1263             :             // Read tag code
    1264       17489 :             entry.tag = rc->read<uint16_t>(offset, ok);
    1265       17489 :             offset += sizeof(uint16_t);
    1266             : 
    1267             :             // Read tag data type
    1268       17489 :             entry.type = rc->read<uint16_t>(offset, ok);
    1269       17489 :             offset += sizeof(uint16_t);
    1270             : 
    1271             :             // Read number of values
    1272             :             if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1273             :             {
    1274         327 :                 auto count = rc->read<uint64_t>(offset, ok);
    1275         327 :                 entry.count = count;
    1276         327 :                 offset += sizeof(count);
    1277             :             }
    1278             :             else
    1279             :             {
    1280       17162 :                 auto count = rc->read<uint32_t>(offset, ok);
    1281       17162 :                 entry.count = count;
    1282       17162 :                 offset += sizeof(count);
    1283             :             }
    1284             : 
    1285       17489 :             uint32_t singleValue = 0;
    1286       17489 :             bool singleValueFitsInUInt32 = false;
    1287       17489 :             if (entry.count)
    1288             :             {
    1289             :                 if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1290             :                 {
    1291         327 :                     image->ParseTagEntryDataOrOffset<uint64_t>(
    1292             :                         entry, offset, singleValueFitsInUInt32, singleValue,
    1293             :                         ok);
    1294             :                 }
    1295             :                 else
    1296             :                 {
    1297       17075 :                     image->ParseTagEntryDataOrOffset<uint32_t>(
    1298             :                         entry, offset, singleValueFitsInUInt32, singleValue,
    1299             :                         ok);
    1300             :                 }
    1301             :             }
    1302       17489 :             if (!ok)
    1303          43 :                 return nullptr;
    1304             : 
    1305       17446 :             image->processTag(entry, singleValueFitsInUInt32, singleValue);
    1306             : 
    1307       17446 :             image->m_tags.push_back(entry);
    1308             :         }
    1309             : 
    1310        1325 :         image->finalTagProcessing();
    1311             : 
    1312             :         if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1313          29 :             image->m_nextImageOffset = rc->read<uint64_t>(offset, ok);
    1314             :         else
    1315        1296 :             image->m_nextImageOffset = rc->read<uint32_t>(offset, ok);
    1316             : 
    1317        1325 :         image->m_openFunc = open<isBigTIFF>;
    1318             : 
    1319        1325 :         return std::unique_ptr<const Image>(image.release());
    1320             :     }
    1321             : 
    1322             :     /** Returns a new Image instance at the next IFD, or nullptr if there is none */
    1323         951 :     std::unique_ptr<const Image> next() const
    1324             :     {
    1325         951 :         return m_openFunc(m_rc, m_nextImageOffset,
    1326         951 :                           m_alreadyVisitedImageOffsets);
    1327             :     }
    1328             : 
    1329             :   private:
    1330             :     const std::shared_ptr<const ReadContext> m_rc;
    1331             :     std::unique_ptr<const Image> (*m_openFunc)(
    1332             :         const std::shared_ptr<const ReadContext> &, const uint64_t,
    1333             :         const std::set<uint64_t> &) = nullptr;
    1334             : 
    1335             :     std::set<uint64_t> m_alreadyVisitedImageOffsets{};
    1336             :     uint64_t m_offset = 0;
    1337             :     uint64_t m_nextImageOffset = 0;
    1338             :     uint32_t m_subFileType = 0;
    1339             :     uint32_t m_width = 0;
    1340             :     uint32_t m_height = 0;
    1341             :     uint32_t m_bitsPerSample = 0;
    1342             :     uint32_t m_samplesPerPixel = 0;
    1343             :     uint32_t m_rowsPerStrip = 0;
    1344             :     CompressionType m_compression = Compression::None;
    1345             :     SampleFormatType m_sampleFormat = SampleFormat::UnsignedInt;
    1346             :     PlanarConfigurationType m_planarConfiguration =
    1347             :         PlanarConfiguration::Contiguous;
    1348             :     PhotometricInterpretationType m_photometricInterpretation =
    1349             :         PhotometricInterpretation::MinIsBlack;
    1350             :     uint32_t m_predictor = 0;
    1351             : 
    1352             :     const bool m_isBigTIFF;
    1353             :     bool m_isTiled = false;
    1354             :     uint32_t m_tileWidth = 0;
    1355             :     uint32_t m_tileHeight = 0;
    1356             :     uint64_t m_strileCount = 0;
    1357             : 
    1358             :     std::vector<TagEntry> m_tags{};
    1359             :     const TagEntry *m_strileOffsetsTag = nullptr;
    1360             :     const TagEntry *m_strileByteCountsTag = nullptr;
    1361             : 
    1362             :     Image(const Image &) = delete;
    1363             :     Image &operator=(const Image &) = delete;
    1364             : 
    1365             :     /** Process tag */
    1366       17446 :     void processTag(const TagEntry &entry, bool singleValueFitsInUInt32,
    1367             :                     uint32_t singleValue)
    1368             :     {
    1369       17446 :         if (singleValueFitsInUInt32)
    1370             :         {
    1371       13445 :             switch (entry.tag)
    1372             :             {
    1373          27 :                 case TagCode::SubFileType:
    1374          27 :                     m_subFileType = singleValue;
    1375          27 :                     break;
    1376             : 
    1377        1323 :                 case TagCode::ImageWidth:
    1378        1323 :                     m_width = singleValue;
    1379        1323 :                     break;
    1380             : 
    1381        1313 :                 case TagCode::ImageLength:
    1382        1313 :                     m_height = singleValue;
    1383        1313 :                     break;
    1384             : 
    1385        1300 :                 case TagCode::Compression:
    1386        1300 :                     m_compression = singleValue;
    1387        1300 :                     break;
    1388             : 
    1389        1279 :                 case TagCode::SamplesPerPixel:
    1390        1279 :                     m_samplesPerPixel = singleValue;
    1391        1279 :                     break;
    1392             : 
    1393        1129 :                 case TagCode::RowsPerStrip:
    1394        1129 :                     m_rowsPerStrip = singleValue;
    1395        1129 :                     break;
    1396             : 
    1397        1277 :                 case TagCode::PlanarConfiguration:
    1398        1277 :                     m_planarConfiguration = singleValue;
    1399        1277 :                     break;
    1400             : 
    1401        1297 :                 case TagCode::PhotometricInterpretation:
    1402        1297 :                     m_photometricInterpretation = singleValue;
    1403        1297 :                     break;
    1404             : 
    1405         114 :                 case TagCode::Predictor:
    1406         114 :                     m_predictor = singleValue;
    1407         114 :                     break;
    1408             : 
    1409         149 :                 case TagCode::TileWidth:
    1410         149 :                     m_tileWidth = singleValue;
    1411         149 :                     break;
    1412             : 
    1413         149 :                 case TagCode::TileLength:
    1414         149 :                     m_tileHeight = singleValue;
    1415         149 :                     break;
    1416             : 
    1417        4088 :                 default:
    1418        4088 :                     break;
    1419             :             }
    1420             :         }
    1421             : 
    1422       17446 :         if (entry.count &&
    1423       17386 :             (entry.type == TagType::Byte || entry.type == TagType::Short ||
    1424        4684 :              entry.type == TagType::Long))
    1425             :         {
    1426             :             // Values of those 2 tags are repeated per sample, but should be
    1427             :             // at the same value.
    1428       15211 :             if (entry.tag == TagCode::SampleFormat)
    1429             :             {
    1430        1191 :                 bool localOk = true;
    1431             :                 const auto sampleFormat =
    1432        1191 :                     static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
    1433        1191 :                 if (localOk)
    1434             :                 {
    1435        1166 :                     m_sampleFormat = sampleFormat;
    1436             :                 }
    1437             :             }
    1438       14020 :             else if (entry.tag == TagCode::BitsPerSample)
    1439             :             {
    1440        1311 :                 bool localOk = true;
    1441             :                 const auto bitsPerSample =
    1442        1311 :                     static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
    1443        1311 :                 if (localOk)
    1444             :                 {
    1445        1311 :                     m_bitsPerSample = bitsPerSample;
    1446             :                 }
    1447             :             }
    1448             :         }
    1449       17446 :     }
    1450             : 
    1451             :     /** Final tag processing */
    1452        1325 :     void finalTagProcessing()
    1453             :     {
    1454        1325 :         m_strileOffsetsTag = tag(TagCode::TileOffsets);
    1455        1325 :         if (m_strileOffsetsTag)
    1456             :         {
    1457         149 :             m_strileByteCountsTag = tag(TagCode::TileByteCounts);
    1458         149 :             if (m_strileByteCountsTag &&
    1459         149 :                 m_strileOffsetsTag->count == m_strileByteCountsTag->count)
    1460             :             {
    1461         149 :                 m_isTiled = true;
    1462         149 :                 m_strileCount = m_strileOffsetsTag->count;
    1463             :             }
    1464             :         }
    1465             :         else
    1466             :         {
    1467        1176 :             m_strileOffsetsTag = tag(TagCode::StripOffsets);
    1468        1176 :             if (m_strileOffsetsTag)
    1469             :             {
    1470        1153 :                 m_strileByteCountsTag = tag(TagCode::StripByteCounts);
    1471        1153 :                 if (m_strileByteCountsTag &&
    1472        1130 :                     m_strileOffsetsTag->count == m_strileByteCountsTag->count)
    1473             :                 {
    1474        1095 :                     m_strileCount = m_strileOffsetsTag->count;
    1475             :                 }
    1476             :             }
    1477             :         }
    1478        1325 :     }
    1479             : 
    1480             :     /** Read a value from a byte/short/long/long8 array tag */
    1481       18528 :     uint64_t readUIntTag(const TagEntry *tag, uint64_t idx, bool &ok) const
    1482             :     {
    1483       18528 :         if (tag && idx < tag->count)
    1484             :         {
    1485       18504 :             if (tag->type == TagType::Byte)
    1486             :             {
    1487           7 :                 if (tag->count <= (m_isBigTIFF ? 8 : 4))
    1488             :                 {
    1489           7 :                     return tag->uint8Values[size_t(idx)];
    1490             :                 }
    1491           0 :                 return m_rc->read<uint8_t>(
    1492           0 :                     tag->value_offset + sizeof(uint8_t) * idx, ok);
    1493             :             }
    1494       18497 :             else if (tag->type == TagType::Short)
    1495             :             {
    1496        9031 :                 if (tag->count <= (m_isBigTIFF ? 4 : 2))
    1497             :                 {
    1498        1947 :                     return tag->uint16Values[size_t(idx)];
    1499             :                 }
    1500        7084 :                 return m_rc->read<uint16_t>(
    1501        7100 :                     tag->value_offset + sizeof(uint16_t) * idx, ok);
    1502             :             }
    1503        9466 :             else if (tag->type == TagType::Long)
    1504             :             {
    1505        9386 :                 if (tag->count <= (m_isBigTIFF ? 2 : 1))
    1506             :                 {
    1507        1356 :                     return tag->uint32Values[size_t(idx)];
    1508             :                 }
    1509        8030 :                 return m_rc->read<uint32_t>(
    1510        8008 :                     tag->value_offset + sizeof(uint32_t) * idx, ok);
    1511             :             }
    1512          80 :             else if (m_isBigTIFF && tag->type == TagType::Long8)
    1513             :             {
    1514          53 :                 if (tag->count <= 1)
    1515             :                 {
    1516          24 :                     return tag->uint64Values[size_t(idx)];
    1517             :                 }
    1518          29 :                 return m_rc->read<uint64_t>(
    1519          29 :                     tag->value_offset + sizeof(uint64_t) * idx, ok);
    1520             :             }
    1521             :         }
    1522          51 :         ok = false;
    1523          51 :         return 0;
    1524             :     }
    1525             : 
    1526             :     template <class DataOrOffsetType>
    1527       17402 :     void ParseTagEntryDataOrOffset(TagEntry &entry, uint64_t &offset,
    1528             :                                    bool &singleValueFitsInUInt32,
    1529             :                                    uint32_t &singleValue, bool &ok)
    1530             :     {
    1531             :         LIBERTIFF_STATIC_ASSERT(
    1532             :             (std::is_same<DataOrOffsetType, uint32_t>::value ||
    1533             :              std::is_same<DataOrOffsetType, uint64_t>::value));
    1534       17402 :         assert(entry.count > 0);
    1535             : 
    1536       17402 :         const uint32_t dataTypeSize = tagTypeSize(entry.type);
    1537       17402 :         if (dataTypeSize == 0)
    1538             :         {
    1539         573 :             return;
    1540             :         }
    1541             : 
    1542             :         // There are 2 cases:
    1543             :         // - either the number of values for the data type can fit
    1544             :         //   in the next DataOrOffsetType bytes
    1545             :         // - or it cannot, and then the next DataOrOffsetType bytes are an offset
    1546             :         //   to the values
    1547       16829 :         if (dataTypeSize > sizeof(DataOrOffsetType) / entry.count)
    1548             :         {
    1549             :             // Out-of-line values. We read a file offset
    1550        3258 :             entry.value_offset = m_rc->read<DataOrOffsetType>(offset, ok);
    1551        3258 :             if (entry.value_offset == 0)
    1552             :             {
    1553             :                 // value_offset = 0 for a out-of-line tag is obviously
    1554             :                 // wrong and would cause later confusion in readTagAsVector<>,
    1555             :                 // so better reject the file.
    1556          16 :                 ok = false;
    1557          16 :                 return;
    1558             :             }
    1559        6484 :             if (dataTypeSize >
    1560        3242 :                 std::numeric_limits<uint64_t>::max() / entry.count)
    1561             :             {
    1562           1 :                 entry.invalid_value_offset = true;
    1563             :             }
    1564             :             else
    1565             :             {
    1566        3241 :                 const uint64_t byteCount = uint64_t(dataTypeSize) * entry.count;
    1567             : 
    1568             :                 // Size of tag data beyond which we check the tag position and size
    1569             :                 // w.r.t the file size.
    1570        3241 :                 constexpr uint32_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1000 * 1000;
    1571             : 
    1572        3241 :                 entry.invalid_value_offset =
    1573        3318 :                     (byteCount > THRESHOLD_CHECK_FILE_SIZE &&
    1574          77 :                      (m_rc->size() < byteCount ||
    1575           3 :                       entry.value_offset > m_rc->size() - byteCount));
    1576             :             }
    1577             :         }
    1578       13571 :         else if (dataTypeSize == sizeof(uint8_t))
    1579             :         {
    1580             :             // Read up to 4 (classic) or 8 (BigTIFF) inline bytes
    1581          39 :             m_rc->read(offset, size_t(entry.count), &entry.uint8Values[0], ok);
    1582          39 :             if (entry.count == 1 && entry.type == TagType::Byte)
    1583             :             {
    1584          13 :                 singleValueFitsInUInt32 = true;
    1585          13 :                 singleValue = entry.uint8Values[0];
    1586             :             }
    1587             :         }
    1588       13532 :         else if (dataTypeSize == sizeof(uint16_t))
    1589             :         {
    1590             :             // Read up to 2 (classic) or 4 (BigTIFF) inline 16-bit values
    1591       11423 :             assert(entry.count <= 4);
    1592       22924 :             for (uint32_t idx = 0; idx < entry.count; ++idx)
    1593             :             {
    1594       11501 :                 entry.uint16Values[idx] =
    1595       11501 :                     m_rc->read<uint16_t>(offset + idx * sizeof(uint16_t), ok);
    1596             :             }
    1597       11423 :             if (entry.count == 1 && entry.type == TagType::Short)
    1598             :             {
    1599       11353 :                 singleValueFitsInUInt32 = true;
    1600       11353 :                 singleValue = entry.uint16Values[0];
    1601             :             }
    1602             :         }
    1603        2109 :         else if (dataTypeSize == sizeof(uint32_t))
    1604             :         {
    1605             :             // Read up to 1 (classic) or 2 (BigTIFF) inline 32-bit values
    1606        2087 :             entry.uint32Values[0] = m_rc->read<uint32_t>(offset, ok);
    1607        2087 :             if (entry.count == 1 && entry.type == TagType::Long)
    1608             :             {
    1609        2079 :                 singleValueFitsInUInt32 = true;
    1610        2079 :                 singleValue = entry.uint32Values[0];
    1611             :             }
    1612             :             if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
    1613             :                                                  uint64_t>::value)
    1614             :             {
    1615          16 :                 if (entry.count == 2)
    1616             :                 {
    1617           8 :                     entry.uint32Values[1] =
    1618           8 :                         m_rc->read<uint32_t>(offset + sizeof(uint32_t), ok);
    1619             :                 }
    1620             :             }
    1621             :         }
    1622             :         else if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
    1623             :                                                   uint64_t>::value)
    1624             :         {
    1625          22 :             if (dataTypeSize == sizeof(uint64_t))
    1626             :             {
    1627             :                 // Read one inline 64-bit value
    1628          22 :                 if (entry.type == TagType::Rational)
    1629           0 :                     entry.float64Values[0] = m_rc->readRational(offset, ok);
    1630          22 :                 else if (entry.type == TagType::SRational)
    1631           0 :                     entry.float64Values[0] =
    1632           0 :                         m_rc->readSignedRational(offset, ok);
    1633             :                 else
    1634          22 :                     entry.uint64Values[0] = m_rc->read<uint64_t>(offset, ok);
    1635             :             }
    1636             :             else
    1637             :             {
    1638           0 :                 assert(false);
    1639             :             }
    1640             :         }
    1641             :         else
    1642             :         {
    1643             :             // fprintf(stderr, "Unexpected case: tag=%u, dataType=%u, count=%u\n", entry.tag, entry.type, entry.count);
    1644           0 :             assert(false);
    1645             :         }
    1646             : 
    1647       16813 :         offset += sizeof(DataOrOffsetType);
    1648             :     }
    1649             : };
    1650             : 
    1651             : /** Open a TIFF file and return its first Image File Directory
    1652             :  */
    1653             : template <bool acceptBigTIFF = true>
    1654        1342 : std::unique_ptr<const Image> open(const std::shared_ptr<const FileReader> &file)
    1655             : {
    1656        1342 :     unsigned char signature[2] = {0, 0};
    1657        1342 :     (void)file->read(0, 2, signature);
    1658        1342 :     const bool littleEndian = signature[0] == 'I' && signature[1] == 'I';
    1659        1342 :     const bool bigEndian = signature[0] == 'M' && signature[1] == 'M';
    1660        1342 :     if (!littleEndian && !bigEndian)
    1661           0 :         return nullptr;
    1662             : 
    1663        1342 :     const bool mustByteSwap = littleEndian ^ isHostLittleEndian();
    1664             : 
    1665        2684 :     auto rc = std::make_shared<ReadContext>(file, mustByteSwap);
    1666        1342 :     bool ok = true;
    1667        1342 :     const int version = rc->read<uint16_t>(2, ok);
    1668        1342 :     constexpr int CLASSIC_TIFF_VERSION = 42;
    1669        1342 :     if (version == CLASSIC_TIFF_VERSION)
    1670             :     {
    1671        1313 :         const auto firstImageOffset = rc->read<uint32_t>(4, ok);
    1672        1313 :         return Image::open<false>(rc, firstImageOffset, {});
    1673             :     }
    1674             :     else if LIBERTIFF_CONSTEXPR (acceptBigTIFF)
    1675             :     {
    1676          29 :         constexpr int BIGTIFF_VERSION = 43;
    1677          29 :         if (version == BIGTIFF_VERSION)
    1678             :         {
    1679          29 :             const auto byteSizeOfOffsets = rc->read<uint16_t>(4, ok);
    1680          29 :             if (byteSizeOfOffsets != 8)
    1681           0 :                 return nullptr;
    1682          29 :             const auto zeroWord = rc->read<uint16_t>(6, ok);
    1683          29 :             if (zeroWord != 0 || !ok)
    1684           0 :                 return nullptr;
    1685          29 :             const auto firstImageOffset = rc->read<uint64_t>(8, ok);
    1686          29 :             return Image::open<true>(rc, firstImageOffset, {});
    1687             :         }
    1688             :     }
    1689             : 
    1690           0 :     return nullptr;
    1691             : }
    1692             : }  // namespace LIBERTIFF_NS
    1693             : 
    1694             : #ifdef LIBERTIFF_C_FILE_READER
    1695             : #include <cstdio>
    1696             : #include <mutex>
    1697             : 
    1698             : namespace LIBERTIFF_NS
    1699             : {
    1700             : /** Interface to read from a FILE* handle */
    1701             : class CFileReader final : public FileReader
    1702             : {
    1703             :   public:
    1704             :     explicit CFileReader(FILE *f) : m_f(f)
    1705             :     {
    1706             :     }
    1707             : 
    1708             :     ~CFileReader() override
    1709             :     {
    1710             :         fclose(m_f);
    1711             :     }
    1712             : 
    1713             :     uint64_t size() const override
    1714             :     {
    1715             :         std::lock_guard<std::mutex> oLock(m_oMutex);
    1716             :         fseek(m_f, 0, SEEK_END);
    1717             :         return ftell(m_f);
    1718             :     }
    1719             : 
    1720             :     size_t read(uint64_t offset, size_t count, void *buffer) const override
    1721             :     {
    1722             :         std::lock_guard<std::mutex> oLock(m_oMutex);
    1723             :         if (fseek(m_f, static_cast<long>(offset), SEEK_SET) != 0)
    1724             :             return 0;
    1725             :         return fread(buffer, 1, count, m_f);
    1726             :     }
    1727             : 
    1728             :   private:
    1729             :     FILE *const m_f;
    1730             :     mutable std::mutex m_oMutex{};
    1731             : 
    1732             :     CFileReader(const CFileReader &) = delete;
    1733             :     CFileReader &operator=(const CFileReader &) = delete;
    1734             : };
    1735             : }  // namespace LIBERTIFF_NS
    1736             : #endif
    1737             : 
    1738             : #endif  // LIBERTIFF_HPP_INCLUDED

Generated by: LCOV version 1.14