• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 13813968540

12 Mar 2025 02:33PM UTC coverage: 70.43% (-0.02%) from 70.446%
13813968540

Pull #11951

github

web-flow
Merge 0560ed8f8 into 5ab779ac6
Pull Request #11951: Doc: Build docs using CMake

553276 of 785573 relevant lines covered (70.43%)

222076.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.84
/third_party/libertiff/libertiff.hpp
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
std::unique_ptr<T> make_unique(Args &&...args)
1,442✔
67
{
68
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
1,442✔
69
}
70

71
/** Returns whether the host is little-endian ordered */
72
inline bool isHostLittleEndian()
1,338✔
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
    return true;
1,338✔
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
template <> inline uint16_t byteSwap(uint16_t v)
5,955✔
108
{
109
    return uint16_t((v >> 8) | ((v & 0xff) << 8));
5,955✔
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
template <> inline uint32_t byteSwap(uint32_t v)
309,705✔
125
{
126
    return (v >> 24) | (((v >> 16) & 0xff) << 8) | (((v >> 8) & 0xff) << 16) |
309,705✔
127
           ((v & 0xff) << 24);
309,705✔
128
}
129

130
/** Byte-swap a int32_t */
131
template <> inline int32_t byteSwap(int32_t v)
×
132
{
133
    uint32_t u;
134
    LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u));
135
    std::memcpy(&u, &v, sizeof(u));
×
136
    u = byteSwap(u);
×
137
    std::memcpy(&v, &u, sizeof(u));
×
138
    return v;
×
139
}
140

141
/** Byte-swap a uint64_t */
142
template <> inline uint64_t byteSwap(uint64_t v)
153,395✔
143
{
144
    return (uint64_t(byteSwap(uint32_t(v & ~(0U)))) << 32) |
153,395✔
145
           byteSwap(uint32_t(v >> 32));
153,395✔
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
    virtual ~FileReader() = default;
1,338✔
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
    ReadContext(const std::shared_ptr<const FileReader> &file,
1,338✔
207
                bool mustByteSwap)
208
        : m_file(file), m_mustByteSwap(mustByteSwap)
1,338✔
209
    {
210
    }
1,338✔
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
    inline bool mustByteSwap() const
6,423✔
216
    {
217
        return m_mustByteSwap;
6,423✔
218
    }
219

220
    /** Return file size */
221
    inline uint64_t size() const
108✔
222
    {
223
        return m_file->size();
108✔
224
    }
225

226
    /** Read count raw bytes at offset into buffer */
227
    void read(uint64_t offset, size_t count, void *buffer, bool &ok) const
7,845✔
228
    {
229
        if (m_file->read(offset, count, buffer) != count)
7,845✔
230
            ok = false;
17✔
231
    }
7,844✔
232

233
    /** Read single value at offset */
234
    template <class T> T read(uint64_t offset, bool &ok) const
89,793✔
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
        T res = 0;
89,793✔
246
        if (m_file->read(offset, sizeof(res), &res) != sizeof(res))
89,793✔
247
        {
248
            ok = false;
162✔
249
            return 0;
162✔
250
        }
251
        if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
252
        {
253
            if (m_mustByteSwap)
89,630✔
254
                res = byteSwap(res);
8,861✔
255
        }
256
        return res;
89,630✔
257
    }
258

259
    /** Read a unsigned rational (type == Type::Rational) */
260
    template <class T = uint32_t>
261
    double readRational(uint64_t offset, bool &ok) const
×
262
    {
263
        const auto numerator = read<T>(offset, ok);
×
264
        const auto denominator = read<T>(offset + sizeof(T), ok);
×
265
        if (denominator == 0)
×
266
        {
267
            ok = false;
×
268
            return std::numeric_limits<double>::quiet_NaN();
×
269
        }
270
        return double(numerator) / denominator;
×
271
    }
272

273
    /** Read a signed rational (type == Type::SRational) */
274
    double readSignedRational(uint64_t offset, bool &ok) const
×
275
    {
276
        return readRational<int32_t>(offset, ok);
×
277
    }
278

279
    /** Read length bytes at offset (typically for ASCII tag) as a string */
280
    std::string readString(std::string &res, uint64_t offset, size_t length,
96✔
281
                           bool &ok) const
282
    {
283
        res.resize(length);
96✔
284
        if (length > 0 && m_file->read(offset, length, &res[0]) != length)
96✔
285
        {
286
            ok = false;
×
287
            res.clear();
×
288
            return res;
×
289
        }
290
        // Strip trailing nul byte if found
291
        if (length > 0 && res.back() == 0)
96✔
292
            res.pop_back();
95✔
293
        return res;
96✔
294
    }
295

296
    /** Read length bytes at offset (typically for ASCII tag) as a string */
297
    std::string readString(uint64_t offset, size_t length, bool &ok) const
96✔
298
    {
299
        std::string res;
96✔
300
        readString(res, offset, length, ok);
96✔
301
        return res;
96✔
302
    }
303

304
    /** Read an array of count values starting at offset */
305
    template <class T>
306
    void readArray(std::vector<T> &array, uint64_t offset, size_t count,
1,071✔
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
        array.resize(count);
1,071✔
319
        const size_t countBytes = count * sizeof(T);
1,071✔
320
        if (count > 0 &&
2,142✔
321
            m_file->read(offset, countBytes, &array[0]) != countBytes)
1,071✔
322
        {
323
            ok = false;
11✔
324
            array.clear();
11✔
325
        }
326
        else if LIBERTIFF_CONSTEXPR (sizeof(T) > 1)
327
        {
328
            if (m_mustByteSwap)
1,023✔
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
                        reinterpret_cast<uint64_t *>(array.data());
20✔
343
                    for (size_t i = 0; i < count; ++i)
153,194✔
344
                    {
345
                        uint64Array[i] = byteSwap(uint64Array[i]);
153,174✔
346
                    }
347
                }
348
                else
349
                {
350
                    for (size_t i = 0; i < count; ++i)
240✔
351
                    {
352
                        array[i] = byteSwap(array[i]);
230✔
353
                    }
354
                }
355
            }
356
        }
357
    }
1,071✔
358

359
    /** Read an array of count values starting at offset */
360
    template <class T>
361
    std::vector<T> readArray(uint64_t offset, size_t count, bool &ok) const
1,071✔
362
    {
363
        std::vector<T> array;
1,071✔
364
        readArray(array, offset, count, ok);
1,071✔
365
        return array;
1,071✔
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
inline const char *compressionName(CompressionType compression)
18✔
663
{
664
    switch (compression)
18✔
665
    {
666
        LIBERTIFF_CASE_COMPRESSION_STR(None);
×
667
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_RLE);
1✔
668
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX3);
×
669
        LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX4);
1✔
670
        LIBERTIFF_CASE_COMPRESSION_STR(LZW);
×
671
        LIBERTIFF_CASE_COMPRESSION_STR(OldJPEG);
2✔
672
        LIBERTIFF_CASE_COMPRESSION_STR(JPEG);
×
673
        LIBERTIFF_CASE_COMPRESSION_STR(Deflate);
×
674
        LIBERTIFF_CASE_COMPRESSION_STR(PackBits);
×
675
        LIBERTIFF_CASE_COMPRESSION_STR(LegacyDeflate);
×
676
        LIBERTIFF_CASE_COMPRESSION_STR(JBIG);
×
677
        LIBERTIFF_CASE_COMPRESSION_STR(LERC);
×
678
        LIBERTIFF_CASE_COMPRESSION_STR(LZMA);
×
679
        LIBERTIFF_CASE_COMPRESSION_STR(ZSTD);
×
680
        LIBERTIFF_CASE_COMPRESSION_STR(WEBP);
×
681
        LIBERTIFF_CASE_COMPRESSION_STR(JXL);
×
682
        LIBERTIFF_CASE_COMPRESSION_STR(JXL_DNG_1_7);
×
683
        default:
14✔
684
            break;
14✔
685
    }
686
    return "(unknown)";
14✔
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
inline uint32_t tagTypeSize(TagTypeType type)
17,328✔
771
{
772
    switch (type)
17,328✔
773
    {
774
        case TagType::Byte:      return 1;
13✔
775
        case TagType::ASCII:     return 1;
496✔
776
        case TagType::Short:     return 2;
12,659✔
777
        case TagType::Long:      return 4;
2,502✔
778
        case TagType::Rational:  return 8;  // 2 Long
84✔
779
        case TagType::SByte:     return 1;
×
780
        case TagType::Undefined: return 1;
38✔
781
        case TagType::SShort:    return 2;
×
782
        case TagType::SLong:     return 4;
×
783
        case TagType::SRational: return 8;  // 2 SLong
×
784
        case TagType::Float:     return 4;
×
785
        case TagType::Double:    return 8;
921✔
786
        case TagType::Long8:     return 8;
41✔
787
        case TagType::SLong8:    return 8;
1✔
788
        case TagType::IFD8:      return 8;
×
789
        default: break;
573✔
790
    }
791
    return 0;
573✔
792
}
793

794
// clang-format on
795

796
namespace detail
797
{
798
template <class T>
799
inline std::vector<T> readTagAsVectorInternal(const ReadContext &rc,
1,083✔
800
                                              const TagEntry &tag,
801
                                              TagTypeType expectedType,
802
                                              const T *inlineValues, bool &ok)
803
{
804
    if (tag.type == expectedType)
1,083✔
805
    {
806
        if (tag.value_offset)
1,083✔
807
        {
808
            if (!tag.invalid_value_offset)
1,063✔
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
                return rc.readArray<T>(tag.value_offset,
1,063✔
819
                                       static_cast<size_t>(tag.count), ok);
1,063✔
820
            }
821
        }
822
        else
823
        {
824
            return std::vector<T>(
825
                inlineValues, inlineValues + static_cast<size_t>(tag.count));
20✔
826
        }
827
    }
828
    ok = false;
×
829
    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
inline std::vector<uint8_t> readTagAsVector(const ReadContext &rc,
37✔
846
                                            const TagEntry &tag, bool &ok)
847
{
848
    return readTagAsVectorInternal(
849
        rc, tag, tag.type == TagType::Undefined ? tag.type : TagType::Byte,
74✔
850
        tag.uint8Values.data(), ok);
37✔
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
inline std::vector<uint16_t> readTagAsVector(const ReadContext &rc,
343✔
863
                                             const TagEntry &tag, bool &ok)
864
{
865
    return readTagAsVectorInternal(rc, tag, TagType::Short,
866
                                   tag.uint16Values.data(), ok);
343✔
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
inline std::vector<uint32_t> readTagAsVector(const ReadContext &rc,
73✔
879
                                             const TagEntry &tag, bool &ok)
880
{
881
    return readTagAsVectorInternal(rc, tag, TagType::Long,
882
                                   tag.uint32Values.data(), ok);
73✔
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
inline std::vector<uint64_t> readTagAsVector(const ReadContext &rc,
×
895
                                             const TagEntry &tag, bool &ok)
896
{
897
    return readTagAsVectorInternal(rc, tag, TagType::Long8,
898
                                   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
inline std::vector<double> readTagAsVector(const ReadContext &rc,
630✔
911
                                           const TagEntry &tag, bool &ok)
912
{
913
    return readTagAsVectorInternal(rc, tag, TagType::Double,
914
                                   tag.float64Values.data(), ok);
630✔
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
    Image(const std::shared_ptr<const ReadContext> &rc, bool isBigTIFF)
1,442✔
925
        : m_rc(rc), m_isBigTIFF(isBigTIFF)
1,442✔
926
    {
927
    }
1,442✔
928

929
    /** Return read context */
930
    const std::shared_ptr<const ReadContext> &readContext() const
14,358✔
931
    {
932
        return m_rc;
14,358✔
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
    inline bool mustByteSwap() const
4✔
945
    {
946
        return m_rc->mustByteSwap();
4✔
947
    }
948

949
    /** Return the offset of the this IFD */
950
    inline uint64_t offset() const
3✔
951
    {
952
        return m_offset;
3✔
953
    }
954

955
    /** Return the offset of the next IFD (to pass to Image::open()),
956
         * or 0 if there is no more */
957
    inline uint64_t nextImageOffset() const
111✔
958
    {
959
        return m_nextImageOffset;
111✔
960
    }
961

962
    /** Return value of SubFileType tag */
963
    inline uint32_t subFileType() const
2,439✔
964
    {
965
        return m_subFileType;
2,439✔
966
    }
967

968
    /** Return width of the image in pixels */
969
    inline uint32_t width() const
3,520✔
970
    {
971
        return m_width;
3,520✔
972
    }
973

974
    /** Return height of the image in pixels */
975
    inline uint32_t height() const
3,807✔
976
    {
977
        return m_height;
3,807✔
978
    }
979

980
    /** Return number of bits per sample */
981
    inline uint32_t bitsPerSample() const
155,812✔
982
    {
983
        return m_bitsPerSample;
155,812✔
984
    }
985

986
    /** Return number of samples (a.k.a. channels, bands) per pixel */
987
    inline uint32_t samplesPerPixel() const
5,187✔
988
    {
989
        return m_samplesPerPixel;
5,187✔
990
    }
991

992
    /** Return planar configuration */
993
    inline PlanarConfigurationType planarConfiguration() const
13,513✔
994
    {
995
        return m_planarConfiguration;
13,513✔
996
    }
997

998
    /** Return planar configuration */
999
    inline PhotometricInterpretationType photometricInterpretation() const
137,555✔
1000
    {
1001
        return m_photometricInterpretation;
137,555✔
1002
    }
1003

1004
    /** Return compression method used */
1005
    inline CompressionType compression() const
78,906✔
1006
    {
1007
        return m_compression;
78,906✔
1008
    }
1009

1010
    /** Return predictor value (used for Deflate, LZW, ZStd, etc. compression) */
1011
    inline uint32_t predictor() const
15,160✔
1012
    {
1013
        return m_predictor;
15,160✔
1014
    }
1015

1016
    /** Return sample format */
1017
    inline SampleFormatType sampleFormat() const
1,321✔
1018
    {
1019
        return m_sampleFormat;
1,321✔
1020
    }
1021

1022
    /** Return the number of rows per strip */
1023
    inline uint32_t rowsPerStrip() const
6✔
1024
    {
1025
        return m_rowsPerStrip;
6✔
1026
    }
1027

1028
    /** Return the sanitized number of rows per strip */
1029
    inline uint32_t rowsPerStripSanitized() const
2,260✔
1030
    {
1031
        return std::min(m_rowsPerStrip, m_height);
2,260✔
1032
    }
1033

1034
    /** Return the number of strips/tiles.
1035
     * Return 0 if inconsistent values between ByteCounts and Offsets arrays. */
1036
    inline uint64_t strileCount() const
6✔
1037
    {
1038
        return m_strileCount;
6✔
1039
    }
1040

1041
    /** Return whether image is tiled */
1042
    inline bool isTiled() const
26,288✔
1043
    {
1044
        return m_isTiled;
26,288✔
1045
    }
1046

1047
    /** Return tile width */
1048
    inline uint32_t tileWidth() const
414✔
1049
    {
1050
        return m_tileWidth;
414✔
1051
    }
1052

1053
    /** Return tile width */
1054
    inline uint32_t tileHeight() const
410✔
1055
    {
1056
        return m_tileHeight;
410✔
1057
    }
1058

1059
    /** Return number of tiles per row */
1060
    uint32_t tilesPerRow() const
6,448✔
1061
    {
1062
        if (m_tileWidth > 0)
6,448✔
1063
        {
1064
            return uint32_t((uint64_t(m_width) + m_tileWidth - 1) /
6,448✔
1065
                            m_tileWidth);
6,448✔
1066
        }
1067
        return 0;
×
1068
    }
1069

1070
    /** Return number of tiles per column */
1071
    uint32_t tilesPerCol() const
6,446✔
1072
    {
1073
        if (m_tileHeight > 0)
6,446✔
1074
        {
1075
            return uint32_t((uint64_t(m_height) + m_tileHeight - 1) /
6,446✔
1076
                            m_tileHeight);
6,446✔
1077
        }
1078
        return 0;
×
1079
    }
1080

1081
    /** Convert a tile coordinate (xtile, ytile, bandIdx) to a flat index */
1082
    uint64_t tileCoordinateToIdx(uint32_t xtile, uint32_t ytile,
6,449✔
1083
                                 uint32_t bandIdx, bool &ok) const
1084
    {
1085
        if (m_isTiled && m_tileWidth > 0 && m_tileHeight > 0)
6,449✔
1086
        {
1087
            const uint32_t lTilesPerRow = tilesPerRow();
6,449✔
1088
            const uint32_t lTilesPerCol = tilesPerCol();
6,445✔
1089
            if (xtile >= lTilesPerRow || ytile >= lTilesPerCol)
6,446✔
1090
            {
1091
                ok = false;
×
1092
                return 0;
×
1093
            }
1094
            uint64_t idx = uint64_t(ytile) * lTilesPerRow + xtile;
6,446✔
1095
            if (bandIdx &&
6,446✔
1096
                m_planarConfiguration == PlanarConfiguration::Separate)
3,679✔
1097
            {
1098
                const uint64_t lTotalTiles =
3,679✔
1099
                    uint64_t(lTilesPerCol) * lTilesPerRow;
3,679✔
1100
                if (lTotalTiles >
3,680✔
1101
                    std::numeric_limits<uint64_t>::max() / bandIdx)
3,679✔
1102
                {
1103
                    ok = false;
×
1104
                    return 0;
×
1105
                }
1106
                idx += bandIdx * lTotalTiles;
3,680✔
1107
            }
1108
            return idx;
6,447✔
1109
        }
1110
        ok = false;
×
1111
        return 0;
×
1112
    }
1113

1114
    /** Return the offset of strip/tile of index idx */
1115
    uint64_t strileOffset(uint64_t idx, bool &ok) const
8,106✔
1116
    {
1117
        return readUIntTag(m_strileOffsetsTag, idx, ok);
8,106✔
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
    uint64_t strileByteCount(uint64_t idx, bool &ok) const
8,018✔
1130
    {
1131
        return readUIntTag(m_strileByteCountsTag, idx, ok);
8,018✔
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
    const TagEntry *tag(TagCodeType tagCode) const
16,533✔
1150
    {
1151
        for (const auto &tag : m_tags)
211,497✔
1152
        {
1153
            if (tag.tag == tagCode)
198,972✔
1154
                return &tag;
4,008✔
1155
        }
1156
        return nullptr;
12,525✔
1157
    }
1158

1159
    /** Read an ASCII tag as a string */
1160
    std::string readTagAsString(const TagEntry &tag, bool &ok) const
116✔
1161
    {
1162
        if (tag.type == TagType::ASCII)
116✔
1163
        {
1164
            if (tag.value_offset)
116✔
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
                return readContext()->readString(
94✔
1176
                    tag.value_offset, static_cast<size_t>(tag.count), ok);
94✔
1177
            }
1178
            if (tag.count)
22✔
1179
            {
1180
                std::string res(tag.charValues.data(),
1181
                                static_cast<size_t>(tag.count));
44✔
1182
                if (res.back() == 0)
22✔
1183
                    res.pop_back();
22✔
1184
                return res;
22✔
1185
            }
1186
        }
1187
        ok = false;
×
1188
        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
    std::vector<T> readTagAsVector(const TagEntry &tag, bool &ok) const
1,083✔
1198
    {
1199
        return detail::readTagAsVector<T>(*(m_rc.get()), tag, ok);
1,083✔
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
    open(const std::shared_ptr<const ReadContext> &rc,
2,285✔
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
        if (imageOffset == 0 || alreadyVisitedImageOffsets.find(imageOffset) !=
3,727✔
1212
                                    alreadyVisitedImageOffsets.end())
3,727✔
1213
        {
1214
            return nullptr;
843✔
1215
        }
1216

1217
        auto image = LIBERTIFF_NS::make_unique<Image>(rc, isBigTIFF);
2,884✔
1218

1219
        image->m_offset = imageOffset;
1,442✔
1220
        image->m_alreadyVisitedImageOffsets = alreadyVisitedImageOffsets;
1,442✔
1221
        image->m_alreadyVisitedImageOffsets.insert(imageOffset);
1,442✔
1222

1223
        bool ok = true;
1,442✔
1224
        int tagCount = 0;
1,442✔
1225
        uint64_t offset = imageOffset;
1,442✔
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
            if (offset >= std::numeric_limits<uint64_t>::max() / 2)
29✔
1232
                return nullptr;
×
1233

1234
            const auto tagCount64Bit = rc->read<uint64_t>(offset, ok);
29✔
1235
            // Artificially limit to the same number of entries as ClassicTIFF
1236
            if (tagCount64Bit > std::numeric_limits<uint16_t>::max())
29✔
1237
                return nullptr;
×
1238
            tagCount = static_cast<int>(tagCount64Bit);
29✔
1239
            offset += sizeof(uint64_t);
29✔
1240
        }
1241
        else
1242
        {
1243
            tagCount = rc->read<uint16_t>(offset, ok);
1,413✔
1244
            offset += sizeof(uint16_t);
1,413✔
1245
        }
1246
        if (!ok)
1,442✔
1247
            return nullptr;
78✔
1248
        image->m_tags.reserve(tagCount);
1,364✔
1249
        // coverity[tainted_data]
1250
        for (int i = 0; i < tagCount; ++i)
18,736✔
1251
        {
1252
            TagEntry entry;
17,415✔
1253

1254
            // Read tag code
1255
            entry.tag = rc->read<uint16_t>(offset, ok);
17,415✔
1256
            offset += sizeof(uint16_t);
17,415✔
1257

1258
            // Read tag data type
1259
            entry.type = rc->read<uint16_t>(offset, ok);
17,415✔
1260
            offset += sizeof(uint16_t);
17,415✔
1261

1262
            // Read number of values
1263
            if LIBERTIFF_CONSTEXPR (isBigTIFF)
1264
            {
1265
                auto count = rc->read<uint64_t>(offset, ok);
327✔
1266
                entry.count = count;
327✔
1267
                offset += sizeof(count);
327✔
1268
            }
1269
            else
1270
            {
1271
                auto count = rc->read<uint32_t>(offset, ok);
17,088✔
1272
                entry.count = count;
17,088✔
1273
                offset += sizeof(count);
17,088✔
1274
            }
1275

1276
            uint32_t singleValue = 0;
17,415✔
1277
            bool singleValueFitsInUInt32 = false;
17,415✔
1278
            if (entry.count)
17,415✔
1279
            {
1280
                if LIBERTIFF_CONSTEXPR (isBigTIFF)
1281
                {
1282
                    image->ParseTagEntryDataOrOffset<uint64_t>(
327✔
1283
                        entry, offset, singleValueFitsInUInt32, singleValue,
1284
                        ok);
1285
                }
1286
                else
1287
                {
1288
                    image->ParseTagEntryDataOrOffset<uint32_t>(
17,001✔
1289
                        entry, offset, singleValueFitsInUInt32, singleValue,
1290
                        ok);
1291
                }
1292
            }
1293
            if (!ok)
17,415✔
1294
                return nullptr;
43✔
1295

1296
            image->processTag(entry, singleValueFitsInUInt32, singleValue);
17,372✔
1297

1298
            image->m_tags.push_back(entry);
17,372✔
1299
        }
1300

1301
        image->finalTagProcessing();
1,321✔
1302

1303
        if LIBERTIFF_CONSTEXPR (isBigTIFF)
1304
            image->m_nextImageOffset = rc->read<uint64_t>(offset, ok);
29✔
1305
        else
1306
            image->m_nextImageOffset = rc->read<uint32_t>(offset, ok);
1,292✔
1307

1308
        image->m_openFunc = open<isBigTIFF>;
1,321✔
1309

1310
        return std::unique_ptr<const Image>(image.release());
1,321✔
1311
    }
1312

1313
    /** Returns a new Image instance at the next IFD, or nullptr if there is none */
1314
    std::unique_ptr<const Image> next() const
947✔
1315
    {
1316
        return m_openFunc(m_rc, m_nextImageOffset,
947✔
1317
                          m_alreadyVisitedImageOffsets);
947✔
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
    void processTag(const TagEntry &entry, bool singleValueFitsInUInt32,
17,372✔
1358
                    uint32_t singleValue)
1359
    {
1360
        if (singleValueFitsInUInt32)
17,372✔
1361
        {
1362
            switch (entry.tag)
13,411✔
1363
            {
1364
                case TagCode::SubFileType:
27✔
1365
                    m_subFileType = singleValue;
27✔
1366
                    break;
27✔
1367

1368
                case TagCode::ImageWidth:
1,319✔
1369
                    m_width = singleValue;
1,319✔
1370
                    break;
1,319✔
1371

1372
                case TagCode::ImageLength:
1,309✔
1373
                    m_height = singleValue;
1,309✔
1374
                    break;
1,309✔
1375

1376
                case TagCode::Compression:
1,296✔
1377
                    m_compression = singleValue;
1,296✔
1378
                    break;
1,296✔
1379

1380
                case TagCode::SamplesPerPixel:
1,275✔
1381
                    m_samplesPerPixel = singleValue;
1,275✔
1382
                    break;
1,275✔
1383

1384
                case TagCode::RowsPerStrip:
1,125✔
1385
                    m_rowsPerStrip = singleValue;
1,125✔
1386
                    break;
1,125✔
1387

1388
                case TagCode::PlanarConfiguration:
1,273✔
1389
                    m_planarConfiguration = singleValue;
1,273✔
1390
                    break;
1,273✔
1391

1392
                case TagCode::PhotometricInterpretation:
1,293✔
1393
                    m_photometricInterpretation = singleValue;
1,293✔
1394
                    break;
1,293✔
1395

1396
                case TagCode::Predictor:
113✔
1397
                    m_predictor = singleValue;
113✔
1398
                    break;
113✔
1399

1400
                case TagCode::TileWidth:
149✔
1401
                    m_tileWidth = singleValue;
149✔
1402
                    break;
149✔
1403

1404
                case TagCode::TileLength:
149✔
1405
                    m_tileHeight = singleValue;
149✔
1406
                    break;
149✔
1407

1408
                default:
4,083✔
1409
                    break;
4,083✔
1410
            }
1411
        }
1412

1413
        if (entry.count &&
17,372✔
1414
            (entry.type == TagType::Byte || entry.type == TagType::Short ||
17,312✔
1415
             entry.type == TagType::Long))
4,655✔
1416
        {
1417
            // Values of those 2 tags are repeated per sample, but should be
1418
            // at the same value.
1419
            if (entry.tag == TagCode::SampleFormat)
15,158✔
1420
            {
1421
                bool localOk = true;
1,187✔
1422
                const auto sampleFormat =
1423
                    static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
1,187✔
1424
                if (localOk)
1,187✔
1425
                {
1426
                    m_sampleFormat = sampleFormat;
1,186✔
1427
                }
1428
            }
1429
            else if (entry.tag == TagCode::BitsPerSample)
13,971✔
1430
            {
1431
                bool localOk = true;
1,307✔
1432
                const auto bitsPerSample =
1433
                    static_cast<uint32_t>(readUIntTag(&entry, 0, localOk));
1,307✔
1434
                if (localOk)
1,307✔
1435
                {
1436
                    m_bitsPerSample = bitsPerSample;
1,307✔
1437
                }
1438
            }
1439
        }
1440
    }
17,372✔
1441

1442
    /** Final tag processing */
1443
    void finalTagProcessing()
1,321✔
1444
    {
1445
        m_strileOffsetsTag = tag(TagCode::TileOffsets);
1,321✔
1446
        if (m_strileOffsetsTag)
1,321✔
1447
        {
1448
            m_strileByteCountsTag = tag(TagCode::TileByteCounts);
149✔
1449
            if (m_strileByteCountsTag &&
149✔
1450
                m_strileOffsetsTag->count == m_strileByteCountsTag->count)
149✔
1451
            {
1452
                m_isTiled = true;
149✔
1453
                m_strileCount = m_strileOffsetsTag->count;
149✔
1454
            }
1455
        }
1456
        else
1457
        {
1458
            m_strileOffsetsTag = tag(TagCode::StripOffsets);
1,172✔
1459
            if (m_strileOffsetsTag)
1,172✔
1460
            {
1461
                m_strileByteCountsTag = tag(TagCode::StripByteCounts);
1,149✔
1462
                if (m_strileByteCountsTag &&
1,149✔
1463
                    m_strileOffsetsTag->count == m_strileByteCountsTag->count)
1,126✔
1464
                {
1465
                    m_strileCount = m_strileOffsetsTag->count;
1,091✔
1466
                }
1467
            }
1468
        }
1469
    }
1,321✔
1470

1471
    /** Read a value from a byte/short/long/long8 array tag */
1472
    uint64_t readUIntTag(const TagEntry *tag, uint64_t idx, bool &ok) const
18,615✔
1473
    {
1474
        if (tag && idx < tag->count)
18,615✔
1475
        {
1476
            if (tag->type == TagType::Byte)
18,583✔
1477
            {
1478
                if (tag->count <= (m_isBigTIFF ? 8 : 4))
7✔
1479
                {
1480
                    return tag->uint8Values[size_t(idx)];
7✔
1481
                }
1482
                return m_rc->read<uint8_t>(
×
1483
                    tag->value_offset + sizeof(uint8_t) * idx, ok);
×
1484
            }
1485
            else if (tag->type == TagType::Short)
18,576✔
1486
            {
1487
                if (tag->count <= (m_isBigTIFF ? 4 : 2))
9,132✔
1488
                {
1489
                    return tag->uint16Values[size_t(idx)];
1,947✔
1490
                }
1491
                return m_rc->read<uint16_t>(
7,185✔
1492
                    tag->value_offset + sizeof(uint16_t) * idx, ok);
7,183✔
1493
            }
1494
            else if (tag->type == TagType::Long)
9,444✔
1495
            {
1496
                if (tag->count <= (m_isBigTIFF ? 2 : 1))
9,387✔
1497
                {
1498
                    return tag->uint32Values[size_t(idx)];
1,354✔
1499
                }
1500
                return m_rc->read<uint32_t>(
8,033✔
1501
                    tag->value_offset + sizeof(uint32_t) * idx, ok);
8,031✔
1502
            }
1503
            else if (m_isBigTIFF && tag->type == TagType::Long8)
57✔
1504
            {
1505
                if (tag->count <= 1)
53✔
1506
                {
1507
                    return tag->uint64Values[size_t(idx)];
24✔
1508
                }
1509
                return m_rc->read<uint64_t>(
29✔
1510
                    tag->value_offset + sizeof(uint64_t) * idx, ok);
29✔
1511
            }
1512
        }
1513
        ok = false;
36✔
1514
        return 0;
36✔
1515
    }
1516

1517
    template <class DataOrOffsetType>
1518
    void ParseTagEntryDataOrOffset(TagEntry &entry, uint64_t &offset,
17,328✔
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
        assert(entry.count > 0);
17,328✔
1526

1527
        const uint32_t dataTypeSize = tagTypeSize(entry.type);
17,328✔
1528
        if (dataTypeSize == 0)
17,328✔
1529
        {
1530
            return;
573✔
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
        if (dataTypeSize > sizeof(DataOrOffsetType) / entry.count)
16,755✔
1539
        {
1540
            // Out-of-line values. We read a file offset
1541
            entry.value_offset = m_rc->read<DataOrOffsetType>(offset, ok);
3,219✔
1542
            if (entry.value_offset == 0)
3,219✔
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
                ok = false;
16✔
1548
                return;
16✔
1549
            }
1550
            if (dataTypeSize >
6,406✔
1551
                std::numeric_limits<uint64_t>::max() / entry.count)
3,203✔
1552
            {
1553
                entry.invalid_value_offset = true;
1✔
1554
            }
1555
            else
1556
            {
1557
                const uint64_t byteCount = uint64_t(dataTypeSize) * entry.count;
3,202✔
1558

1559
                // Size of tag data beyond which we check the tag position and size
1560
                // w.r.t the file size.
1561
                constexpr uint32_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1000 * 1000;
3,202✔
1562

1563
                entry.invalid_value_offset =
3,202✔
1564
                    (byteCount > THRESHOLD_CHECK_FILE_SIZE &&
3,279✔
1565
                     (m_rc->size() < byteCount ||
77✔
1566
                      entry.value_offset > m_rc->size() - byteCount));
3✔
1567
            }
1568
        }
1569
        else if (dataTypeSize == sizeof(uint8_t))
13,536✔
1570
        {
1571
            // Read up to 4 (classic) or 8 (BigTIFF) inline bytes
1572
            m_rc->read(offset, size_t(entry.count), &entry.uint8Values[0], ok);
38✔
1573
            if (entry.count == 1 && entry.type == TagType::Byte)
38✔
1574
            {
1575
                singleValueFitsInUInt32 = true;
13✔
1576
                singleValue = entry.uint8Values[0];
13✔
1577
            }
1578
        }
1579
        else if (dataTypeSize == sizeof(uint16_t))
13,498✔
1580
        {
1581
            // Read up to 2 (classic) or 4 (BigTIFF) inline 16-bit values
1582
            for (uint32_t idx = 0; idx < entry.count; ++idx)
22,860✔
1583
            {
1584
                entry.uint16Values[idx] =
11,469✔
1585
                    m_rc->read<uint16_t>(offset + idx * sizeof(uint16_t), ok);
11,469✔
1586
            }
1587
            if (entry.count == 1 && entry.type == TagType::Short)
11,391✔
1588
            {
1589
                singleValueFitsInUInt32 = true;
11,321✔
1590
                singleValue = entry.uint16Values[0];
11,321✔
1591
            }
1592
        }
1593
        else if (dataTypeSize == sizeof(uint32_t))
2,107✔
1594
        {
1595
            // Read up to 1 (classic) or 2 (BigTIFF) inline 32-bit values
1596
            entry.uint32Values[0] = m_rc->read<uint32_t>(offset, ok);
2,085✔
1597
            if (entry.count == 1 && entry.type == TagType::Long)
2,085✔
1598
            {
1599
                singleValueFitsInUInt32 = true;
2,077✔
1600
                singleValue = entry.uint32Values[0];
2,077✔
1601
            }
1602
            if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
1603
                                                 uint64_t>::value)
1604
            {
1605
                if (entry.count == 2)
16✔
1606
                {
1607
                    entry.uint32Values[1] =
8✔
1608
                        m_rc->read<uint32_t>(offset + sizeof(uint32_t), ok);
8✔
1609
                }
1610
            }
1611
        }
1612
        else if LIBERTIFF_CONSTEXPR (std::is_same<DataOrOffsetType,
1613
                                                  uint64_t>::value)
1614
        {
1615
            if (dataTypeSize == sizeof(uint64_t))
22✔
1616
            {
1617
                // Read one inline 64-bit value
1618
                if (entry.type == TagType::Rational)
22✔
1619
                    entry.float64Values[0] = m_rc->readRational(offset, ok);
×
1620
                else if (entry.type == TagType::SRational)
22✔
1621
                    entry.float64Values[0] =
×
1622
                        m_rc->readSignedRational(offset, ok);
×
1623
                else
1624
                    entry.uint64Values[0] = m_rc->read<uint64_t>(offset, ok);
22✔
1625
            }
1626
            else
1627
            {
1628
                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
            assert(false);
×
1635
        }
1636

1637
        offset += sizeof(DataOrOffsetType);
16,739✔
1638
    }
1639
};
1640

1641
/** Open a TIFF file and return its first Image File Directory
1642
 */
1643
template <bool acceptBigTIFF = true>
1644
std::unique_ptr<const Image> open(const std::shared_ptr<const FileReader> &file)
1,338✔
1645
{
1646
    unsigned char signature[2] = {0, 0};
1,338✔
1647
    (void)file->read(0, 2, signature);
1,338✔
1648
    const bool littleEndian = signature[0] == 'I' && signature[1] == 'I';
1,338✔
1649
    const bool bigEndian = signature[0] == 'M' && signature[1] == 'M';
1,338✔
1650
    if (!littleEndian && !bigEndian)
1,338✔
1651
        return nullptr;
×
1652

1653
    const bool mustByteSwap = littleEndian ^ isHostLittleEndian();
1,338✔
1654

1655
    auto rc = std::make_shared<ReadContext>(file, mustByteSwap);
2,676✔
1656
    bool ok = true;
1,338✔
1657
    const int version = rc->read<uint16_t>(2, ok);
1,338✔
1658
    constexpr int CLASSIC_TIFF_VERSION = 42;
1,338✔
1659
    if (version == CLASSIC_TIFF_VERSION)
1,338✔
1660
    {
1661
        const auto firstImageOffset = rc->read<uint32_t>(4, ok);
1,309✔
1662
        return Image::open<false>(rc, firstImageOffset, {});
1,309✔
1663
    }
1664
    else if LIBERTIFF_CONSTEXPR (acceptBigTIFF)
1665
    {
1666
        constexpr int BIGTIFF_VERSION = 43;
29✔
1667
        if (version == BIGTIFF_VERSION)
29✔
1668
        {
1669
            const auto byteSizeOfOffsets = rc->read<uint16_t>(4, ok);
29✔
1670
            if (byteSizeOfOffsets != 8)
29✔
1671
                return nullptr;
×
1672
            const auto zeroWord = rc->read<uint16_t>(6, ok);
29✔
1673
            if (zeroWord != 0 || !ok)
29✔
1674
                return nullptr;
×
1675
            const auto firstImageOffset = rc->read<uint64_t>(8, ok);
29✔
1676
            return Image::open<true>(rc, firstImageOffset, {});
29✔
1677
        }
1678
    }
1679

1680
    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
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc