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

Generated by: LCOV version 1.14