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
|