LCOV - code coverage report
Current view: top level - third_party/libertiff - libertiff.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 391 452 86.5 %
Date: 2025-02-20 10:14:44 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        6424 :     inline bool mustByteSwap() const
     216             :     {
     217        6424 :         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        7843 :     void read(uint64_t offset, size_t count, void *buffer, bool &ok) const
     228             :     {
     229        7843 :         if (m_file->read(offset, count, buffer) != count)
     230          17 :             ok = false;
     231        7846 :     }
     232             : 
     233             :     /** Read single value at offset */
     234       89797 :     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       89797 :         T res = 0;
     246       89797 :         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       89623 :             if (m_mustByteSwap)
     254        8861 :                 res = byteSwap(res);
     255             :         }
     256       89623 :         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       14352 :     const std::shared_ptr<const ReadContext> &readContext() const
     931             :     {
     932       14352 :         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      155783 :     inline uint32_t bitsPerSample() const
     982             :     {
     983      155783 :         return m_bitsPerSample;
     984             :     }
     985             : 
     986             :     /** Return number of samples (a.k.a. channels, bands) per pixel */
     987        5175 :     inline uint32_t samplesPerPixel() const
     988             :     {
     989        5175 :         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       78874 :     inline CompressionType compression() const
    1006             :     {
    1007       78874 :         return m_compression;
    1008             :     }
    1009             : 
    1010             :     /** Return predictor value (used for Deflate, LZW, ZStd, etc. compression) */
    1011       15156 :     inline uint32_t predictor() const
    1012             :     {
    1013       15156 :         return m_predictor;
    1014             :     }
    1015             : 
    1016             :     /** Return sample format */
    1017        1309 :     inline SampleFormatType sampleFormat() const
    1018             :     {
    1019        1309 :         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       26293 :     inline bool isTiled() const
    1043             :     {
    1044       26293 :         return m_isTiled;
    1045             :     }
    1046             : 
    1047             :     /** Return tile width */
    1048         402 :     inline uint32_t tileWidth() const
    1049             :     {
    1050         402 :         return m_tileWidth;
    1051             :     }
    1052             : 
    1053             :     /** Return tile width */
    1054         398 :     inline uint32_t tileHeight() const
    1055             :     {
    1056         398 :         return m_tileHeight;
    1057             :     }
    1058             : 
    1059             :     /** Return number of tiles per row */
    1060        6444 :     uint32_t tilesPerRow() const
    1061             :     {
    1062        6444 :         if (m_tileWidth > 0)
    1063             :         {
    1064        6441 :             return uint32_t((uint64_t(m_width) + m_tileWidth - 1) /
    1065        6441 :                             m_tileWidth);
    1066             :         }
    1067           3 :         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        6442 :             return uint32_t((uint64_t(m_height) + m_tileHeight - 1) /
    1076        6442 :                             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        6445 :             const uint32_t lTilesPerRow = tilesPerRow();
    1088        6442 :             const uint32_t lTilesPerCol = tilesPerCol();
    1089        6439 :             if (xtile >= lTilesPerRow || ytile >= lTilesPerCol)
    1090             :             {
    1091           0 :                 ok = false;
    1092           0 :                 return 0;
    1093             :             }
    1094        6439 :             uint64_t idx = uint64_t(ytile) * lTilesPerRow + xtile;
    1095        6439 :             if (bandIdx &&
    1096        3677 :                 m_planarConfiguration == PlanarConfiguration::Separate)
    1097             :             {
    1098        3678 :                 const uint64_t lTotalTiles =
    1099        3678 :                     uint64_t(lTilesPerCol) * lTilesPerRow;
    1100        3678 :                 if (lTotalTiles >
    1101        3678 :                     std::numeric_limits<uint64_t>::max() / bandIdx)
    1102             :                 {
    1103           0 :                     ok = false;
    1104           0 :                     return 0;
    1105             :                 }
    1106        3678 :                 idx += bandIdx * lTotalTiles;
    1107             :             }
    1108        6439 :             return idx;
    1109             :         }
    1110           1 :         ok = false;
    1111           1 :         return 0;
    1112             :     }
    1113             : 
    1114             :     /** Return the offset of strip/tile of index idx */
    1115        8105 :     uint64_t strileOffset(uint64_t idx, bool &ok) const
    1116             :     {
    1117        8105 :         return readUIntTag(m_strileOffsetsTag, idx, ok);
    1118             :     }
    1119             : 
    1120             :     /** Return the offset of a tile from its coordinates */
    1121             :     uint64_t tileOffset(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
    1122             :                         bool &ok) const
    1123             :     {
    1124             :         const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
    1125             :         return ok ? strileOffset(idx, ok) : 0;
    1126             :     }
    1127             : 
    1128             :     /** Return the byte count of strip/tile of index idx */
    1129        8024 :     uint64_t strileByteCount(uint64_t idx, bool &ok) const
    1130             :     {
    1131        8024 :         return readUIntTag(m_strileByteCountsTag, idx, ok);
    1132             :     }
    1133             : 
    1134             :     /** Return the offset of a tile from its coordinates */
    1135             :     uint64_t tileByteCount(uint32_t xtile, uint32_t ytile, uint32_t bandIdx,
    1136             :                            bool &ok) const
    1137             :     {
    1138             :         const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok);
    1139             :         return ok ? strileByteCount(idx, ok) : 0;
    1140             :     }
    1141             : 
    1142             :     /** Return the list of tags */
    1143             :     inline const std::vector<TagEntry> &tags() const
    1144             :     {
    1145             :         return m_tags;
    1146             :     }
    1147             : 
    1148             :     /** Return the (first) tag corresponding to a code, or nullptr if not found */
    1149       16533 :     const TagEntry *tag(TagCodeType tagCode) const
    1150             :     {
    1151      211497 :         for (const auto &tag : m_tags)
    1152             :         {
    1153      198972 :             if (tag.tag == tagCode)
    1154        4008 :                 return &tag;
    1155             :         }
    1156       12525 :         return nullptr;
    1157             :     }
    1158             : 
    1159             :     /** Read an ASCII tag as a string */
    1160         116 :     std::string readTagAsString(const TagEntry &tag, bool &ok) const
    1161             :     {
    1162         116 :         if (tag.type == TagType::ASCII)
    1163             :         {
    1164         116 :             if (tag.value_offset)
    1165             :             {
    1166             :                 if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t))
    1167             :                 {
    1168             :                     // coverity[result_independent_of_operands]
    1169             :                     if (tag.count > std::numeric_limits<size_t>::max())
    1170             :                     {
    1171             :                         ok = false;
    1172             :                         return std::string();
    1173             :                     }
    1174             :                 }
    1175          94 :                 return readContext()->readString(
    1176          94 :                     tag.value_offset, static_cast<size_t>(tag.count), ok);
    1177             :             }
    1178          22 :             if (tag.count)
    1179             :             {
    1180             :                 std::string res(tag.charValues.data(),
    1181          44 :                                 static_cast<size_t>(tag.count));
    1182          22 :                 if (res.back() == 0)
    1183          22 :                     res.pop_back();
    1184          22 :                 return res;
    1185             :             }
    1186             :         }
    1187           0 :         ok = false;
    1188           0 :         return std::string();
    1189             :     }
    1190             : 
    1191             :     /** Read a numeric tag as a vector. You must use a type T which is
    1192             :      * consistent with the tag.type value. For example, if
    1193             :      * tag.type == libertiff::TagType::Short, T must be uint16_t.
    1194             :      * libertiff::TagType::Undefined must be read with T=uint8_t.
    1195             :      */
    1196             :     template <class T>
    1197        1083 :     std::vector<T> readTagAsVector(const TagEntry &tag, bool &ok) const
    1198             :     {
    1199        1083 :         return detail::readTagAsVector<T>(*(m_rc.get()), tag, ok);
    1200             :     }
    1201             : 
    1202             :     /** Returns a new Image instance for the IFD starting at offset imageOffset */
    1203             :     template <bool isBigTIFF>
    1204             :     static std::unique_ptr<const Image>
    1205        2285 :     open(const std::shared_ptr<const ReadContext> &rc,
    1206             :          const uint64_t imageOffset,
    1207             :          const std::set<uint64_t> &alreadyVisitedImageOffsets =
    1208             :              std::set<uint64_t>())
    1209             :     {
    1210             :         // To prevent infinite looping on corrupted files
    1211        3727 :         if (imageOffset == 0 || alreadyVisitedImageOffsets.find(imageOffset) !=
    1212        3727 :                                     alreadyVisitedImageOffsets.end())
    1213             :         {
    1214         843 :             return nullptr;
    1215             :         }
    1216             : 
    1217        2884 :         auto image = LIBERTIFF_NS::make_unique<Image>(rc, isBigTIFF);
    1218             : 
    1219        1442 :         image->m_offset = imageOffset;
    1220        1442 :         image->m_alreadyVisitedImageOffsets = alreadyVisitedImageOffsets;
    1221        1442 :         image->m_alreadyVisitedImageOffsets.insert(imageOffset);
    1222             : 
    1223        1442 :         bool ok = true;
    1224        1442 :         int tagCount = 0;
    1225        1442 :         uint64_t offset = imageOffset;
    1226             :         if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1227             :         {
    1228             :             // To prevent unsigned integer overflows in later additions. The
    1229             :             // theoretical max should be much closer to UINT64_MAX, but half of
    1230             :             // it is already more than needed in practice :-)
    1231          29 :             if (offset >= std::numeric_limits<uint64_t>::max() / 2)
    1232           0 :                 return nullptr;
    1233             : 
    1234          29 :             const auto tagCount64Bit = rc->read<uint64_t>(offset, ok);
    1235             :             // Artificially limit to the same number of entries as ClassicTIFF
    1236          29 :             if (tagCount64Bit > std::numeric_limits<uint16_t>::max())
    1237           0 :                 return nullptr;
    1238          29 :             tagCount = static_cast<int>(tagCount64Bit);
    1239          29 :             offset += sizeof(uint64_t);
    1240             :         }
    1241             :         else
    1242             :         {
    1243        1413 :             tagCount = rc->read<uint16_t>(offset, ok);
    1244        1413 :             offset += sizeof(uint16_t);
    1245             :         }
    1246        1442 :         if (!ok)
    1247          78 :             return nullptr;
    1248        1364 :         image->m_tags.reserve(tagCount);
    1249             :         // coverity[tainted_data]
    1250       18736 :         for (int i = 0; i < tagCount; ++i)
    1251             :         {
    1252       17415 :             TagEntry entry;
    1253             : 
    1254             :             // Read tag code
    1255       17415 :             entry.tag = rc->read<uint16_t>(offset, ok);
    1256       17415 :             offset += sizeof(uint16_t);
    1257             : 
    1258             :             // Read tag data type
    1259       17415 :             entry.type = rc->read<uint16_t>(offset, ok);
    1260       17415 :             offset += sizeof(uint16_t);
    1261             : 
    1262             :             // Read number of values
    1263             :             if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1264             :             {
    1265         327 :                 auto count = rc->read<uint64_t>(offset, ok);
    1266         327 :                 entry.count = count;
    1267         327 :                 offset += sizeof(count);
    1268             :             }
    1269             :             else
    1270             :             {
    1271       17088 :                 auto count = rc->read<uint32_t>(offset, ok);
    1272       17088 :                 entry.count = count;
    1273       17088 :                 offset += sizeof(count);
    1274             :             }
    1275             : 
    1276       17415 :             uint32_t singleValue = 0;
    1277       17415 :             bool singleValueFitsInUInt32 = false;
    1278       17415 :             if (entry.count)
    1279             :             {
    1280             :                 if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1281             :                 {
    1282         327 :                     image->ParseTagEntryDataOrOffset<uint64_t>(
    1283             :                         entry, offset, singleValueFitsInUInt32, singleValue,
    1284             :                         ok);
    1285             :                 }
    1286             :                 else
    1287             :                 {
    1288       17001 :                     image->ParseTagEntryDataOrOffset<uint32_t>(
    1289             :                         entry, offset, singleValueFitsInUInt32, singleValue,
    1290             :                         ok);
    1291             :                 }
    1292             :             }
    1293       17415 :             if (!ok)
    1294          43 :                 return nullptr;
    1295             : 
    1296       17372 :             image->processTag(entry, singleValueFitsInUInt32, singleValue);
    1297             : 
    1298       17372 :             image->m_tags.push_back(entry);
    1299             :         }
    1300             : 
    1301        1321 :         image->finalTagProcessing();
    1302             : 
    1303             :         if LIBERTIFF_CONSTEXPR (isBigTIFF)
    1304          29 :             image->m_nextImageOffset = rc->read<uint64_t>(offset, ok);
    1305             :         else
    1306        1292 :             image->m_nextImageOffset = rc->read<uint32_t>(offset, ok);
    1307             : 
    1308        1321 :         image->m_openFunc = open<isBigTIFF>;
    1309             : 
    1310        1321 :         return std::unique_ptr<const Image>(image.release());
    1311             :     }
    1312             : 
    1313             :     /** Returns a new Image instance at the next IFD, or nullptr if there is none */
    1314         947 :     std::unique_ptr<const Image> next() const
    1315             :     {
    1316         947 :         return m_openFunc(m_rc, m_nextImageOffset,
    1317         947 :                           m_alreadyVisitedImageOffsets);
    1318             :     }
    1319             : 
    1320             :   private:
    1321             :     const std::shared_ptr<const ReadContext> m_rc;
    1322             :     std::unique_ptr<const Image> (*m_openFunc)(
    1323             :         const std::shared_ptr<const ReadContext> &, const uint64_t,
    1324             :         const std::set<uint64_t> &) = nullptr;
    1325             : 
    1326             :     std::set<uint64_t> m_alreadyVisitedImageOffsets{};
    1327             :     uint64_t m_offset = 0;
    1328             :     uint64_t m_nextImageOffset = 0;
    1329             :     uint32_t m_subFileType = 0;
    1330             :     uint32_t m_width = 0;
    1331             :     uint32_t m_height = 0;
    1332             :     uint32_t m_bitsPerSample = 0;
    1333             :     uint32_t m_samplesPerPixel = 0;
    1334             :     uint32_t m_rowsPerStrip = 0;
    1335             :     CompressionType m_compression = Compression::None;
    1336             :     SampleFormatType m_sampleFormat = SampleFormat::UnsignedInt;
    1337             :     PlanarConfigurationType m_planarConfiguration =
    1338             :         PlanarConfiguration::Contiguous;
    1339             :     PhotometricInterpretationType m_photometricInterpretation =
    1340             :         PhotometricInterpretation::MinIsBlack;
    1341             :     uint32_t m_predictor = 0;
    1342             : 
    1343             :     const bool m_isBigTIFF;
    1344             :     bool m_isTiled = false;
    1345             :     uint32_t m_tileWidth = 0;
    1346             :     uint32_t m_tileHeight = 0;
    1347             :     uint64_t m_strileCount = 0;
    1348             : 
    1349             :     std::vector<TagEntry> m_tags{};
    1350             :     const TagEntry *m_strileOffsetsTag = nullptr;
    1351             :     const TagEntry *m_strileByteCountsTag = nullptr;
    1352             : 
    1353             :     Image(const Image &) = delete;
    1354             :     Image &operator=(const Image &) = delete;
    1355             : 
    1356             :     /** Process tag */
    1357       17372 :     void processTag(const TagEntry &entry, bool singleValueFitsInUInt32,
    1358             :                     uint32_t singleValue)
    1359             :     {
    1360       17372 :         if (singleValueFitsInUInt32)
    1361             :         {
    1362       13411 :             switch (entry.tag)
    1363             :             {
    1364          27 :                 case TagCode::SubFileType:
    1365          27 :                     m_subFileType = singleValue;
    1366          27 :                     break;
    1367             : 
    1368        1319 :                 case TagCode::ImageWidth:
    1369        1319 :                     m_width = singleValue;
    1370        1319 :                     break;
    1371             : 
    1372        1309 :                 case TagCode::ImageLength:
    1373        1309 :                     m_height = singleValue;
    1374        1309 :                     break;
    1375             : 
    1376        1296 :                 case TagCode::Compression:
    1377        1296 :                     m_compression = singleValue;
    1378        1296 :                     break;
    1379             : 
    1380        1275 :                 case TagCode::SamplesPerPixel:
    1381        1275 :                     m_samplesPerPixel = singleValue;
    1382        1275 :                     break;
    1383             : 
    1384        1125 :                 case TagCode::RowsPerStrip:
    1385        1125 :                     m_rowsPerStrip = singleValue;
    1386        1125 :                     break;
    1387             : 
    1388        1273 :                 case TagCode::PlanarConfiguration:
    1389        1273 :                     m_planarConfiguration = singleValue;
    1390        1273 :                     break;
    1391             : 
    1392        1293 :                 case TagCode::PhotometricInterpretation:
    1393        1293 :                     m_photometricInterpretation = singleValue;
    1394        1293 :                     break;
    1395             : 
    1396         113 :                 case TagCode::Predictor:
    1397         113 :                     m_predictor = singleValue;
    1398         113 :                     break;
    1399             : 
    1400         149 :                 case TagCode::TileWidth:
    1401         149 :                     m_tileWidth = singleValue;
    1402         149 :                     break;
    1403             : 
    1404         149 :                 case TagCode::TileLength:
    1405         149 :                     m_tileHeight = singleValue;
    1406         149 :                     break;
    1407             : 
    1408        4083 :                 default:
    1409        4083 :                     break;
    1410             :             }
    1411             :         }
    1412             : 
    1413       17372 :         if (entry.count &&
    1414       17312 :             (entry.type == TagType::Byte || entry.type == TagType::Short ||
    1415        4655 :              entry.type == TagType::Long))
    1416             :         {
    1417             :             // Values of those 2 tags are repeated per sample, but should be
    1418             :             // at the same value.
    1419       15158 :             if (entry.tag == TagCode::SampleFormat)
    1420             :             {
    1421        1187 :                 bool localOk = true;
    1422             :                 const auto sampleFormat =
    1423        1187 :                     static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
    1424        1187 :                 if (localOk)
    1425             :                 {
    1426        1186 :                     m_sampleFormat = sampleFormat;
    1427             :                 }
    1428             :             }
    1429       13971 :             else if (entry.tag == TagCode::BitsPerSample)
    1430             :             {
    1431        1307 :                 bool localOk = true;
    1432             :                 const auto bitsPerSample =
    1433        1307 :                     static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
    1434        1307 :                 if (localOk)
    1435             :                 {
    1436        1307 :                     m_bitsPerSample = bitsPerSample;
    1437             :                 }
    1438             :             }
    1439             :         }
    1440       17372 :     }
    1441             : 
    1442             :     /** Final tag processing */
    1443        1321 :     void finalTagProcessing()
    1444             :     {
    1445        1321 :         m_strileOffsetsTag = tag(TagCode::TileOffsets);
    1446        1321 :         if (m_strileOffsetsTag)
    1447             :         {
    1448         149 :             m_strileByteCountsTag = tag(TagCode::TileByteCounts);
    1449         149 :             if (m_strileByteCountsTag &&
    1450         149 :                 m_strileOffsetsTag->count == m_strileByteCountsTag->count)
    1451             :             {
    1452         149 :                 m_isTiled = true;
    1453         149 :                 m_strileCount = m_strileOffsetsTag->count;
    1454             :             }
    1455             :         }
    1456             :         else
    1457             :         {
    1458        1172 :             m_strileOffsetsTag = tag(TagCode::StripOffsets);
    1459        1172 :             if (m_strileOffsetsTag)
    1460             :             {
    1461        1149 :                 m_strileByteCountsTag = tag(TagCode::StripByteCounts);
    1462        1149 :                 if (m_strileByteCountsTag &&
    1463        1126 :                     m_strileOffsetsTag->count == m_strileByteCountsTag->count)
    1464             :                 {
    1465        1091 :                     m_strileCount = m_strileOffsetsTag->count;
    1466             :                 }
    1467             :             }
    1468             :         }
    1469        1321 :     }
    1470             : 
    1471             :     /** Read a value from a byte/short/long/long8 array tag */
    1472       18622 :     uint64_t readUIntTag(const TagEntry *tag, uint64_t idx, bool &ok) const
    1473             :     {
    1474       18622 :         if (tag && idx < tag->count)
    1475             :         {
    1476       18584 :             if (tag->type == TagType::Byte)
    1477             :             {
    1478           7 :                 if (tag->count <= (m_isBigTIFF ? 8 : 4))
    1479             :                 {
    1480           7 :                     return tag->uint8Values[size_t(idx)];
    1481             :                 }
    1482           0 :                 return m_rc->read<uint8_t>(
    1483           0 :                     tag->value_offset + sizeof(uint8_t) * idx, ok);
    1484             :             }
    1485       18577 :             else if (tag->type == TagType::Short)
    1486             :             {
    1487        9129 :                 if (tag->count <= (m_isBigTIFF ? 4 : 2))
    1488             :                 {
    1489        1947 :                     return tag->uint16Values[size_t(idx)];
    1490             :                 }
    1491        7182 :                 return m_rc->read<uint16_t>(
    1492        7186 :                     tag->value_offset + sizeof(uint16_t) * idx, ok);
    1493             :             }
    1494        9448 :             else if (tag->type == TagType::Long)
    1495             :             {
    1496        9384 :                 if (tag->count <= (m_isBigTIFF ? 2 : 1))
    1497             :                 {
    1498        1354 :                     return tag->uint32Values[size_t(idx)];
    1499             :                 }
    1500        8030 :                 return m_rc->read<uint32_t>(
    1501        8032 :                     tag->value_offset + sizeof(uint32_t) * idx, ok);
    1502             :             }
    1503          64 :             else if (m_isBigTIFF && tag->type == TagType::Long8)
    1504             :             {
    1505          53 :                 if (tag->count <= 1)
    1506             :                 {
    1507          24 :                     return tag->uint64Values[size_t(idx)];
    1508             :                 }
    1509          29 :                 return m_rc->read<uint64_t>(
    1510          29 :                     tag->value_offset + sizeof(uint64_t) * idx, ok);
    1511             :             }
    1512             :         }
    1513          49 :         ok = false;
    1514          49 :         return 0;
    1515             :     }
    1516             : 
    1517             :     template <class DataOrOffsetType>
    1518       17328 :     void ParseTagEntryDataOrOffset(TagEntry &entry, uint64_t &offset,
    1519             :                                    bool &singleValueFitsInUInt32,
    1520             :                                    uint32_t &singleValue, bool &ok)
    1521             :     {
    1522             :         LIBERTIFF_STATIC_ASSERT(
    1523             :             (std::is_same<DataOrOffsetType, uint32_t>::value ||
    1524             :              std::is_same<DataOrOffsetType, uint64_t>::value));
    1525       17328 :         assert(entry.count > 0);
    1526             : 
    1527       17328 :         const uint32_t dataTypeSize = tagTypeSize(entry.type);
    1528       17328 :         if (dataTypeSize == 0)
    1529             :         {
    1530         573 :             return;
    1531             :         }
    1532             : 
    1533             :         // There are 2 cases:
    1534             :         // - either the number of values for the data type can fit
    1535             :         //   in the next DataOrOffsetType bytes
    1536             :         // - or it cannot, and then the next DataOrOffsetType bytes are an offset
    1537             :         //   to the values
    1538       16755 :         if (dataTypeSize > sizeof(DataOrOffsetType) / entry.count)
    1539             :         {
    1540             :             // Out-of-line values. We read a file offset
    1541        3219 :             entry.value_offset = m_rc->read<DataOrOffsetType>(offset, ok);
    1542        3219 :             if (entry.value_offset == 0)
    1543             :             {
    1544             :                 // value_offset = 0 for a out-of-line tag is obviously
    1545             :                 // wrong and would cause later confusion in readTagAsVector<>,
    1546             :                 // so better reject the file.
    1547          16 :                 ok = false;
    1548          16 :                 return;
    1549             :             }
    1550        6406 :             if (dataTypeSize >
    1551        3203 :                 std::numeric_limits<uint64_t>::max() / entry.count)
    1552             :             {
    1553           1 :                 entry.invalid_value_offset = true;
    1554             :             }
    1555             :             else
    1556             :             {
    1557        3202 :                 const uint64_t byteCount = uint64_t(dataTypeSize) * entry.count;
    1558             : 
    1559             :                 // Size of tag data beyond which we check the tag position and size
    1560             :                 // w.r.t the file size.
    1561        3202 :                 constexpr uint32_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1000 * 1000;
    1562             : 
    1563        3202 :                 entry.invalid_value_offset =
    1564        3279 :                     (byteCount > THRESHOLD_CHECK_FILE_SIZE &&
    1565          77 :                      (m_rc->size() < byteCount ||
    1566           3 :                       entry.value_offset > m_rc->size() - byteCount));
    1567             :             }
    1568             :         }
    1569       13536 :         else if (dataTypeSize == sizeof(uint8_t))
    1570             :         {
    1571             :             // Read up to 4 (classic) or 8 (BigTIFF) inline bytes
    1572          38 :             m_rc->read(offset, size_t(entry.count), &entry.uint8Values[0], ok);
    1573          38 :             if (entry.count == 1 && entry.type == TagType::Byte)
    1574             :             {
    1575          13 :                 singleValueFitsInUInt32 = true;
    1576          13 :                 singleValue = entry.uint8Values[0];
    1577             :             }
    1578             :         }
    1579       13498 :         else if (dataTypeSize == sizeof(uint16_t))
    1580             :         {
    1581             :             // Read up to 2 (classic) or 4 (BigTIFF) inline 16-bit values
    1582       22860 :             for (uint32_t idx = 0; idx < entry.count; ++idx)
    1583             :             {
    1584       11469 :                 entry.uint16Values[idx] =
    1585       11469 :                     m_rc->read<uint16_t>(offset + idx * sizeof(uint16_t), ok);
    1586             :             }
    1587       11391 :             if (entry.count == 1 && entry.type == TagType::Short)
    1588             :             {
    1589       11321 :                 singleValueFitsInUInt32 = true;
    1590       11321 :                 singleValue = entry.uint16Values[0];
    1591             :             }
    1592             :         }
    1593        2107 :         else if (dataTypeSize == sizeof(uint32_t))
    1594             :         {
    1595             :             // Read up to 1 (classic) or 2 (BigTIFF) inline 32-bit values
    1596        2085 :             entry.uint32Values[0] = m_rc->read<uint32_t>(offset, ok);
    1597        2085 :             if (entry.count == 1 && entry.type == TagType::Long)
    1598             :             {
    1599        2077 :                 singleValueFitsInUInt32 = true;
    1600        2077 :                 singleValue = entry.uint32Values[0];
    1601             :             }
    1602             :             if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
    1603             :                                                  uint64_t>::value)
    1604             :             {
    1605          16 :                 if (entry.count == 2)
    1606             :                 {
    1607           8 :                     entry.uint32Values[1] =
    1608           8 :                         m_rc->read<uint32_t>(offset + sizeof(uint32_t), ok);
    1609             :                 }
    1610             :             }
    1611             :         }
    1612             :         else if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
    1613             :                                                   uint64_t>::value)
    1614             :         {
    1615          22 :             if (dataTypeSize == sizeof(uint64_t))
    1616             :             {
    1617             :                 // Read one inline 64-bit value
    1618          22 :                 if (entry.type == TagType::Rational)
    1619           0 :                     entry.float64Values[0] = m_rc->readRational(offset, ok);
    1620          22 :                 else if (entry.type == TagType::SRational)
    1621           0 :                     entry.float64Values[0] =
    1622           0 :                         m_rc->readSignedRational(offset, ok);
    1623             :                 else
    1624          22 :                     entry.uint64Values[0] = m_rc->read<uint64_t>(offset, ok);
    1625             :             }
    1626             :             else
    1627             :             {
    1628           0 :                 assert(false);
    1629             :             }
    1630             :         }
    1631             :         else
    1632             :         {
    1633             :             // fprintf(stderr, "Unexpected case: tag=%u, dataType=%u, count=%u\n", entry.tag, entry.type, entry.count);
    1634           0 :             assert(false);
    1635             :         }
    1636             : 
    1637       16739 :         offset += sizeof(DataOrOffsetType);
    1638             :     }
    1639             : };
    1640             : 
    1641             : /** Open a TIFF file and return its first Image File Directory
    1642             :  */
    1643             : template <bool acceptBigTIFF = true>
    1644        1338 : std::unique_ptr<const Image> open(const std::shared_ptr<const FileReader> &file)
    1645             : {
    1646        1338 :     unsigned char signature[2] = {0, 0};
    1647        1338 :     (void)file->read(0, 2, signature);
    1648        1338 :     const bool littleEndian = signature[0] == 'I' && signature[1] == 'I';
    1649        1338 :     const bool bigEndian = signature[0] == 'M' && signature[1] == 'M';
    1650        1338 :     if (!littleEndian && !bigEndian)
    1651           0 :         return nullptr;
    1652             : 
    1653        1338 :     const bool mustByteSwap = littleEndian ^ isHostLittleEndian();
    1654             : 
    1655        2676 :     auto rc = std::make_shared<ReadContext>(file, mustByteSwap);
    1656        1338 :     bool ok = true;
    1657        1338 :     const int version = rc->read<uint16_t>(2, ok);
    1658        1338 :     constexpr int CLASSIC_TIFF_VERSION = 42;
    1659        1338 :     if (version == CLASSIC_TIFF_VERSION)
    1660             :     {
    1661        1309 :         const auto firstImageOffset = rc->read<uint32_t>(4, ok);
    1662        1309 :         return Image::open<false>(rc, firstImageOffset, {});
    1663             :     }
    1664             :     else if LIBERTIFF_CONSTEXPR (acceptBigTIFF)
    1665             :     {
    1666          29 :         constexpr int BIGTIFF_VERSION = 43;
    1667          29 :         if (version == BIGTIFF_VERSION)
    1668             :         {
    1669          29 :             const auto byteSizeOfOffsets = rc->read<uint16_t>(4, ok);
    1670          29 :             if (byteSizeOfOffsets != 8)
    1671           0 :                 return nullptr;
    1672          29 :             const auto zeroWord = rc->read<uint16_t>(6, ok);
    1673          29 :             if (zeroWord != 0 || !ok)
    1674           0 :                 return nullptr;
    1675          29 :             const auto firstImageOffset = rc->read<uint64_t>(8, ok);
    1676          29 :             return Image::open<true>(rc, firstImageOffset, {});
    1677             :         }
    1678             :     }
    1679             : 
    1680           0 :     return nullptr;
    1681             : }
    1682             : }  // namespace LIBERTIFF_NS
    1683             : 
    1684             : #ifdef LIBERTIFF_C_FILE_READER
    1685             : #include <cstdio>
    1686             : #include <mutex>
    1687             : 
    1688             : namespace LIBERTIFF_NS
    1689             : {
    1690             : /** Interface to read from a FILE* handle */
    1691             : class CFileReader final : public FileReader
    1692             : {
    1693             :   public:
    1694             :     explicit CFileReader(FILE *f) : m_f(f)
    1695             :     {
    1696             :     }
    1697             : 
    1698             :     ~CFileReader() override
    1699             :     {
    1700             :         fclose(m_f);
    1701             :     }
    1702             : 
    1703             :     uint64_t size() const override
    1704             :     {
    1705             :         std::lock_guard<std::mutex> oLock(m_oMutex);
    1706             :         fseek(m_f, 0, SEEK_END);
    1707             :         return ftell(m_f);
    1708             :     }
    1709             : 
    1710             :     size_t read(uint64_t offset, size_t count, void *buffer) const override
    1711             :     {
    1712             :         std::lock_guard<std::mutex> oLock(m_oMutex);
    1713             :         if (fseek(m_f, static_cast<long>(offset), SEEK_SET) != 0)
    1714             :             return 0;
    1715             :         return fread(buffer, 1, count, m_f);
    1716             :     }
    1717             : 
    1718             :   private:
    1719             :     FILE *const m_f;
    1720             :     mutable std::mutex m_oMutex{};
    1721             : 
    1722             :     CFileReader(const CFileReader &) = delete;
    1723             :     CFileReader &operator=(const CFileReader &) = delete;
    1724             : };
    1725             : }  // namespace LIBERTIFF_NS
    1726             : #endif
    1727             : 
    1728             : #endif  // LIBERTIFF_HPP_INCLUDED

Generated by: LCOV version 1.14