LCOV - code coverage report
Current view: top level - third_party/libertiff - libertiff.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 268 423 63.4 %
Date: 2024-11-21 22:18:42 Functions: 45 96 46.9 %

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

Generated by: LCOV version 1.14