Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: GeoTIFF thread safe reader using libertiff library.
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_compressor.h"
14 : #include "cpl_mem_cache.h"
15 : #include "cpl_minixml.h"
16 : #include "cpl_vsi_virtual.h"
17 : #include "cpl_multiproc.h" // CPLGetNumCPUs()
18 : #include "cpl_worker_thread_pool.h" // CPLJobQueue, CPLWorkerThreadPool
19 :
20 : #include <algorithm>
21 : #include <array>
22 : #include <atomic>
23 : #include <limits>
24 : #include <map>
25 : #include <mutex>
26 : #include <type_traits>
27 :
28 : #include "gdal_pam.h"
29 : #include "gdal_mdreader.h"
30 : #include "gdal_interpolateatpoint.h"
31 : #include "gdal_thread_pool.h"
32 : #include "memdataset.h"
33 :
34 : #define LIBERTIFF_NS GDAL_libertiff
35 : #include "libertiff.hpp"
36 :
37 : #include "libtiff_codecs.h"
38 :
39 : #if defined(__x86_64__) || defined(_M_X64)
40 : #include <emmintrin.h>
41 : #endif
42 :
43 : #define STRINGIFY(x) #x
44 : #define XSTRINGIFY(x) STRINGIFY(x)
45 :
46 : /************************************************************************/
47 : /* LIBERTIFFDatasetFileReader */
48 : /************************************************************************/
49 :
50 : struct LIBERTIFFDatasetFileReader final : public LIBERTIFF_NS::FileReader
51 : {
52 : VSILFILE *const m_fp;
53 : const bool m_bHasPread;
54 : mutable bool m_bPReadAllowed = false;
55 : mutable uint64_t m_nFileSize = 0;
56 : mutable std::mutex m_oMutex{};
57 :
58 1203 : explicit LIBERTIFFDatasetFileReader(VSILFILE *fp)
59 1203 : : m_fp(fp), m_bHasPread(m_fp->HasPRead())
60 : {
61 1203 : }
62 :
63 98 : uint64_t size() const override
64 : {
65 98 : std::lock_guard oLock(m_oMutex);
66 98 : if (m_nFileSize == 0)
67 : {
68 91 : m_fp->Seek(0, SEEK_END);
69 91 : m_nFileSize = m_fp->Tell();
70 : }
71 196 : return m_nFileSize;
72 : }
73 :
74 92964 : size_t read(uint64_t offset, size_t count, void *buffer) const override
75 : {
76 92964 : if (m_bHasPread && m_bPReadAllowed)
77 : {
78 22273 : return m_fp->PRead(buffer, count, offset);
79 : }
80 : else
81 : {
82 70691 : std::lock_guard oLock(m_oMutex);
83 70660 : return m_fp->Seek(offset, SEEK_SET) == 0
84 70660 : ? m_fp->Read(buffer, 1, count)
85 70660 : : 0;
86 : }
87 : }
88 :
89 898 : void setPReadAllowed() const
90 : {
91 898 : m_bPReadAllowed = true;
92 898 : }
93 :
94 : CPL_DISALLOW_COPY_ASSIGN(LIBERTIFFDatasetFileReader)
95 : };
96 :
97 : /************************************************************************/
98 : /* LIBERTIFFDataset */
99 : /************************************************************************/
100 :
101 : class LIBERTIFFDataset final : public GDALPamDataset
102 : {
103 : public:
104 2476 : LIBERTIFFDataset() = default;
105 :
106 : static int Identify(GDALOpenInfo *poOpenInfo);
107 : static GDALDataset *OpenStatic(GDALOpenInfo *poOpenInfo);
108 :
109 6 : const OGRSpatialReference *GetSpatialRef() const override
110 : {
111 6 : return m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
112 : }
113 :
114 3 : CPLErr GetGeoTransform(double *padfGeoTransform) override
115 : {
116 3 : memcpy(padfGeoTransform, m_geotransform.data(),
117 3 : m_geotransform.size() * sizeof(double));
118 3 : return m_geotransformValid ? CE_None : CE_Failure;
119 : }
120 :
121 4 : int GetGCPCount() override
122 : {
123 4 : return static_cast<int>(m_aoGCPs.size());
124 : }
125 :
126 2 : const OGRSpatialReference *GetGCPSpatialRef() const override
127 : {
128 2 : return !m_aoGCPs.empty() && !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
129 : }
130 :
131 2 : const GDAL_GCP *GetGCPs() override
132 : {
133 2 : return gdal::GCP::c_ptr(m_aoGCPs);
134 : }
135 :
136 : protected:
137 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
138 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
139 : GDALDataType eBufType, int nBandCount,
140 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
141 : GSpacing nLineSpace, GSpacing nBandSpace,
142 : GDALRasterIOExtraArg *psExtraArg) override;
143 :
144 11 : CPLErr BlockBasedRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
145 : int nXSize, int nYSize, void *pData,
146 : int nBufXSize, int nBufYSize,
147 : GDALDataType eBufType, int nBandCount,
148 : const int *panBandMap, GSpacing nPixelSpace,
149 : GSpacing nLineSpace, GSpacing nBandSpace,
150 : GDALRasterIOExtraArg *psExtraArg) override
151 : {
152 11 : return IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
153 : nBufXSize, nBufYSize, eBufType, nBandCount,
154 : const_cast<BANDMAP_TYPE>(panBandMap), nPixelSpace,
155 11 : nLineSpace, nBandSpace, psExtraArg);
156 : }
157 :
158 : private:
159 : friend class LIBERTIFFBand;
160 : VSIVirtualHandleUniquePtr m_poFile{};
161 : std::shared_ptr<const LIBERTIFFDatasetFileReader> m_fileReader{};
162 : std::unique_ptr<const LIBERTIFF_NS::Image> m_image{};
163 : const CPLCompressor *m_decompressor = nullptr;
164 1238 : std::shared_ptr<int> m_validityPtr = std::make_shared<int>(0);
165 : OGRSpatialReference m_oSRS{};
166 : bool m_geotransformValid = false;
167 : std::array<double, 6> m_geotransform{1, 0, 0, 0, 0, 1};
168 : std::vector<gdal::GCP> m_aoGCPs{};
169 : std::vector<std::unique_ptr<LIBERTIFFDataset>> m_apoOvrDSOwned{};
170 : std::vector<LIBERTIFFDataset *> m_apoOvrDS{};
171 : GDALRasterBand *m_poAlphaBand = nullptr;
172 : std::unique_ptr<LIBERTIFFDataset> m_poMaskDS{};
173 : bool m_bExpand1To255 = false;
174 : std::vector<uint8_t> m_jpegTablesOri{};
175 : std::vector<uint8_t> m_jpegTables{};
176 : std::vector<uint32_t> m_tileOffsets{};
177 : std::vector<uint64_t> m_tileOffsets64{};
178 : std::vector<uint32_t> m_tileByteCounts{};
179 : int m_lercVersion = LERC_VERSION_2_4;
180 : int m_lercAdditionalCompression = LERC_ADD_COMPRESSION_NONE;
181 : std::vector<uint16_t> m_extraSamples{};
182 : CPLWorkerThreadPool *m_poThreadPool = nullptr;
183 :
184 : struct ThreadLocalState
185 : {
186 : private:
187 : std::weak_ptr<int> m_validityTest{};
188 :
189 : public:
190 968 : explicit ThreadLocalState(const LIBERTIFFDataset *ds)
191 968 : : m_validityTest(ds->m_validityPtr)
192 : {
193 968 : memset(&m_tiff, 0, sizeof(m_tiff));
194 968 : }
195 :
196 964 : ~ThreadLocalState()
197 964 : {
198 964 : if (m_tiff.tif_cleanup)
199 142 : m_tiff.tif_cleanup(&m_tiff);
200 964 : }
201 :
202 8667 : inline bool isValid() const
203 : {
204 8667 : return m_validityTest.lock() != nullptr;
205 : }
206 :
207 : // Used by IRasterIO()
208 : std::vector<GByte> m_abyIRasterIOBuffer{};
209 :
210 : // Used by ReadBlock()
211 : uint64_t m_curStrileIdx = std::numeric_limits<uint64_t>::max();
212 : bool m_curStrileMissing = false;
213 : std::vector<GByte> m_decompressedBuffer{};
214 : std::vector<GByte> m_compressedBuffer{};
215 : std::vector<GByte> m_bufferForOneBitExpansion{};
216 : std::vector<void *> m_apabyDest{};
217 : std::vector<uint8_t> m_floatingPointHorizPredictorDecodeTmpBuffer{};
218 :
219 : TIFF m_tiff{};
220 : };
221 :
222 : GDALDataType ComputeGDALDataType() const;
223 : bool ProcessCompressionMethod();
224 :
225 : bool Open(std::unique_ptr<const LIBERTIFF_NS::Image> image);
226 :
227 : bool Open(GDALOpenInfo *poOpenInfo);
228 :
229 : ThreadLocalState &GetTLSState() const;
230 :
231 : void ReadSRS();
232 : void ReadGeoTransform();
233 : void ReadRPCTag();
234 :
235 : bool ReadBlock(GByte *pabyBlockData, int nBlockXOff, int nBlockYOff,
236 : int nBandCount, BANDMAP_TYPE panBandMap,
237 : GDALDataType eBufType, GSpacing nPixelSpace,
238 : GSpacing nLineSpace, GSpacing nBandSpace) const;
239 :
240 : CPL_DISALLOW_COPY_ASSIGN(LIBERTIFFDataset)
241 : };
242 :
243 : /************************************************************************/
244 : /* LIBERTIFFBand */
245 : /************************************************************************/
246 :
247 : class LIBERTIFFBand final : public GDALPamRasterBand
248 : {
249 : public:
250 66924 : LIBERTIFFBand(LIBERTIFFDataset *poDSIn, int nBandIn, GDALDataType eDT,
251 : int nBlockXSizeIn, int nBlockYSizeIn)
252 66924 : {
253 66924 : poDS = poDSIn;
254 66924 : nBand = nBandIn;
255 66924 : eDataType = eDT;
256 66924 : nBlockXSize = nBlockXSizeIn;
257 66924 : nBlockYSize = nBlockYSizeIn;
258 66924 : }
259 :
260 37 : double GetNoDataValue(int *pbHasNoData) override
261 : {
262 37 : if (pbHasNoData)
263 5 : *pbHasNoData = m_bHasNoData;
264 37 : return m_dfNoData;
265 : }
266 :
267 1 : double GetScale(int *pbHasNoData) override
268 : {
269 1 : if (pbHasNoData)
270 1 : *pbHasNoData = m_bHaveOffsetScale;
271 1 : return m_dfScale;
272 : }
273 :
274 1 : double GetOffset(int *pbHasNoData) override
275 : {
276 1 : if (pbHasNoData)
277 1 : *pbHasNoData = m_bHaveOffsetScale;
278 1 : return m_dfOffset;
279 : }
280 :
281 4 : const char *GetDescription() const override
282 : {
283 4 : return m_osDescription.c_str();
284 : }
285 :
286 1 : const char *GetUnitType() override
287 : {
288 1 : return m_osUnitType.c_str();
289 : }
290 :
291 3 : GDALColorInterp GetColorInterpretation() override
292 : {
293 3 : return m_eColorInterp;
294 : }
295 :
296 3 : GDALColorTable *GetColorTable() override
297 : {
298 3 : return m_poColorTable.get();
299 : }
300 :
301 16 : int GetOverviewCount() override
302 : {
303 16 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
304 16 : return static_cast<int>(l_poDS->m_apoOvrDS.size());
305 : }
306 :
307 9 : GDALRasterBand *GetOverview(int idx) override
308 : {
309 9 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
310 9 : if (idx >= 0 && idx < GetOverviewCount())
311 7 : return l_poDS->m_apoOvrDS[idx]->GetRasterBand(nBand);
312 2 : return nullptr;
313 : }
314 :
315 3 : int GetMaskFlags() override
316 : {
317 3 : return nMaskFlags;
318 : }
319 :
320 2 : GDALRasterBand *GetMaskBand() override
321 : {
322 2 : return poMask.get();
323 : }
324 :
325 : const char *GetMetadataItem(const char *pszName,
326 : const char *pszDomain) override;
327 :
328 : CPLErr InterpolateAtPoint(double dfPixel, double dfLine,
329 : GDALRIOResampleAlg eInterpolation,
330 : double *pdfRealValue,
331 : double *pdfImagValue = nullptr) const override;
332 :
333 : // We could do a smarter implementation by manually managing blocks in
334 : // the TLS structure, but given we should rarely use that method, the
335 : // current approach with a mutex should be good enough
336 2 : GDALRasterBlock *GetLockedBlockRef(int nXBlockOff, int nYBlockOff,
337 : int bJustInitialize = FALSE) override
338 : {
339 2 : if (!m_bDebugGetLockedBlockRef)
340 : {
341 2 : m_bDebugGetLockedBlockRef = true;
342 2 : CPLDebug("LIBERTIFF", "GetLockedBlockRef() called");
343 : }
344 4 : std::lock_guard oLock(m_oMutexBlockCache);
345 : // coverity[sleep]
346 2 : return GDALRasterBand::GetLockedBlockRef(nXBlockOff, nYBlockOff,
347 4 : bJustInitialize);
348 : }
349 :
350 2 : GDALRasterBlock *TryGetLockedBlockRef(int nXBlockOff,
351 : int nYBlockOff) override
352 : {
353 4 : std::lock_guard oLock(m_oMutexBlockCache);
354 4 : return GDALRasterBand::TryGetLockedBlockRef(nXBlockOff, nYBlockOff);
355 : }
356 :
357 0 : CPLErr FlushBlock(int nXBlockOff, int nYBlockOff,
358 : int bWriteDirtyBlock = TRUE) override
359 : {
360 0 : std::lock_guard oLock(m_oMutexBlockCache);
361 0 : return GDALRasterBand::FlushBlock(nXBlockOff, nYBlockOff,
362 0 : bWriteDirtyBlock);
363 : }
364 :
365 : protected:
366 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
367 :
368 2407 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
369 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
370 : GDALDataType eBufType, GSpacing nPixelSpace,
371 : GSpacing nLineSpace,
372 : GDALRasterIOExtraArg *psExtraArg) override
373 : {
374 2407 : int anBand[] = {nBand};
375 2407 : return cpl::down_cast<LIBERTIFFDataset *>(poDS)->IRasterIO(
376 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
377 4814 : eBufType, 1, anBand, nPixelSpace, nLineSpace, 0, psExtraArg);
378 : }
379 :
380 : private:
381 : friend class LIBERTIFFDataset;
382 :
383 : std::recursive_mutex m_oMutexBlockCache{};
384 : GDALColorInterp m_eColorInterp = GCI_Undefined;
385 : std::unique_ptr<GDALColorTable> m_poColorTable{};
386 : bool m_bHasNoData = false;
387 : bool m_bHaveOffsetScale = false;
388 : bool m_bDebugGetLockedBlockRef = false;
389 : double m_dfNoData = 0;
390 : double m_dfScale = 1.0;
391 : double m_dfOffset = 0.0;
392 : std::string m_osUnitType{};
393 : std::string m_osDescription{};
394 :
395 : struct ThreadLocalState
396 : {
397 : private:
398 : std::weak_ptr<int> m_validityTest{};
399 :
400 : public:
401 1 : explicit ThreadLocalState(const LIBERTIFFBand *band)
402 1 : : m_validityTest(
403 1 : cpl::down_cast<LIBERTIFFDataset *>(band->poDS)->m_validityPtr)
404 : {
405 1 : }
406 :
407 0 : inline bool isValid() const
408 : {
409 0 : return m_validityTest.lock() != nullptr;
410 : }
411 :
412 : GDALDoublePointsCache m_pointsCache{};
413 : };
414 :
415 : ThreadLocalState &GetTLSState() const;
416 :
417 : void ReadColorMap();
418 :
419 : void InitMaskBand();
420 : };
421 :
422 : /************************************************************************/
423 : /* IReadBlock() */
424 : /************************************************************************/
425 :
426 2 : CPLErr LIBERTIFFBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pData)
427 : {
428 : int nXValid, nYValid;
429 2 : GetActualBlockSize(nBlockXOff, nBlockYOff, &nXValid, &nYValid);
430 : GDALRasterIOExtraArg sExtraArg;
431 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
432 2 : int anBand[] = {nBand};
433 2 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
434 2 : return cpl::down_cast<LIBERTIFFDataset *>(poDS)->IRasterIO(
435 2 : GF_Read, nBlockXOff * nBlockXSize, nBlockYOff * nBlockYSize, nXValid,
436 : nYValid, pData, nXValid, nYValid, eDataType, 1, anBand, nDTSize,
437 4 : static_cast<GSpacing>(nDTSize) * nBlockXSize, 0, &sExtraArg);
438 : }
439 :
440 : /************************************************************************/
441 : /* ReadColorMap() */
442 : /************************************************************************/
443 :
444 650 : void LIBERTIFFBand::ReadColorMap()
445 : {
446 650 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
447 :
448 : const auto psTagColorMap =
449 650 : l_poDS->m_image->tag(LIBERTIFF_NS::TagCode::ColorMap);
450 8 : if (psTagColorMap && psTagColorMap->type == LIBERTIFF_NS::TagType::Short &&
451 8 : psTagColorMap->count >= 3 && (psTagColorMap->count % 3) == 0 &&
452 666 : psTagColorMap->count == (1U << l_poDS->m_image->bitsPerSample()) * 3U &&
453 8 : !psTagColorMap->invalid_value_offset)
454 : {
455 8 : bool ok = true;
456 : const auto colorMap =
457 16 : l_poDS->m_image->readTagAsVector<uint16_t>(*psTagColorMap, ok);
458 8 : if (colorMap.size() == psTagColorMap->count)
459 : {
460 6 : constexpr int DEFAULT_COLOR_TABLE_MULTIPLIER_257 = 257;
461 6 : const int nColorCount = static_cast<int>(psTagColorMap->count) / 3;
462 6 : const auto *panRed = colorMap.data();
463 6 : const auto *panGreen = colorMap.data() + nColorCount;
464 6 : const auto *panBlue = colorMap.data() + 2 * nColorCount;
465 6 : int nColorTableMultiplier = 0;
466 6 : m_poColorTable = gdal::tiff_common::TIFFColorMapTagToColorTable(
467 : panRed, panGreen, panBlue, nColorCount, nColorTableMultiplier,
468 6 : DEFAULT_COLOR_TABLE_MULTIPLIER_257, m_bHasNoData, m_dfNoData);
469 6 : m_eColorInterp = GCI_PaletteIndex;
470 : }
471 : }
472 650 : }
473 :
474 : /************************************************************************/
475 : /* GetTLSState() */
476 : /************************************************************************/
477 :
478 1 : LIBERTIFFBand::ThreadLocalState &LIBERTIFFBand::GetTLSState() const
479 : {
480 : static thread_local lru11::Cache<const LIBERTIFFBand *,
481 : std::shared_ptr<ThreadLocalState>>
482 1 : tlsState;
483 1 : std::shared_ptr<ThreadLocalState> value;
484 1 : if (tlsState.tryGet(this, value))
485 : {
486 0 : if (value->isValid())
487 0 : return *value.get();
488 : }
489 1 : value = std::make_shared<ThreadLocalState>(this);
490 1 : tlsState.insert(this, value);
491 1 : return *value.get();
492 : }
493 :
494 : /************************************************************************/
495 : /* InterpolateAtPoint() */
496 : /************************************************************************/
497 :
498 1 : CPLErr LIBERTIFFBand::InterpolateAtPoint(double dfPixel, double dfLine,
499 : GDALRIOResampleAlg eInterpolation,
500 : double *pdfRealValue,
501 : double *pdfImagValue) const
502 : {
503 1 : if (eInterpolation != GRIORA_NearestNeighbour &&
504 0 : eInterpolation != GRIORA_Bilinear && eInterpolation != GRIORA_Cubic &&
505 : eInterpolation != GRIORA_CubicSpline)
506 : {
507 0 : CPLError(CE_Failure, CPLE_AppDefined,
508 : "Only nearest, bilinear, cubic and cubicspline interpolation "
509 : "methods "
510 : "allowed");
511 :
512 0 : return CE_Failure;
513 : }
514 :
515 1 : LIBERTIFFBand *pBand = const_cast<LIBERTIFFBand *>(this);
516 1 : auto &pointsCache = GetTLSState().m_pointsCache;
517 : const bool res =
518 1 : GDALInterpolateAtPoint(pBand, eInterpolation, pointsCache.cache,
519 : dfPixel, dfLine, pdfRealValue, pdfImagValue);
520 :
521 1 : return res ? CE_None : CE_Failure;
522 : }
523 :
524 : /************************************************************************/
525 : /* InitMaskBand() */
526 : /************************************************************************/
527 :
528 66902 : void LIBERTIFFBand::InitMaskBand()
529 : {
530 66902 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
531 66902 : if (m_bHasNoData)
532 : {
533 32 : nMaskFlags = GMF_NODATA;
534 32 : poMask.reset(std::make_unique<GDALNoDataMaskBand>(this));
535 : }
536 66870 : else if (l_poDS->m_poMaskDS)
537 : {
538 20 : nMaskFlags = GMF_PER_DATASET;
539 20 : poMask.reset(l_poDS->m_poMaskDS->GetRasterBand(1), false);
540 : }
541 66850 : else if (l_poDS->m_poAlphaBand && l_poDS->m_poAlphaBand != this)
542 : {
543 35 : nMaskFlags = GMF_PER_DATASET | GMF_ALPHA;
544 35 : poMask.reset(l_poDS->m_poAlphaBand, false);
545 : }
546 : else
547 : {
548 66815 : nMaskFlags = GMF_ALL_VALID;
549 66815 : poMask.reset(std::make_unique<GDALAllValidMaskBand>(this));
550 : }
551 66902 : }
552 :
553 : /************************************************************************/
554 : /* GetMetadataItem() */
555 : /************************************************************************/
556 :
557 359 : const char *LIBERTIFFBand::GetMetadataItem(const char *pszName,
558 : const char *pszDomain)
559 : {
560 :
561 359 : if (pszName != nullptr && pszDomain != nullptr && EQUAL(pszDomain, "TIFF"))
562 : {
563 357 : int nBlockXOff = 0;
564 357 : int nBlockYOff = 0;
565 :
566 357 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
567 :
568 357 : if (EQUAL(pszName, "JPEGTABLES"))
569 : {
570 2 : if (l_poDS->m_jpegTablesOri.empty())
571 357 : return nullptr;
572 : char *const pszHex =
573 1 : CPLBinaryToHex(static_cast<int>(l_poDS->m_jpegTablesOri.size()),
574 1 : l_poDS->m_jpegTablesOri.data());
575 1 : const char *pszReturn = CPLSPrintf("%s", pszHex);
576 1 : CPLFree(pszHex);
577 :
578 1 : return pszReturn;
579 : }
580 :
581 355 : if (EQUAL(pszName, "IFD_OFFSET"))
582 : {
583 3 : return CPLSPrintf(CPL_FRMT_GUIB,
584 6 : static_cast<GUIntBig>(l_poDS->m_image->offset()));
585 : }
586 :
587 352 : if (sscanf(pszName, "BLOCK_OFFSET_%d_%d", &nBlockXOff, &nBlockYOff) ==
588 : 2)
589 : {
590 204 : if (nBlockXOff < 0 ||
591 204 : nBlockXOff >= DIV_ROUND_UP(nRasterXSize, nBlockXSize) ||
592 203 : nBlockYOff < 0 ||
593 203 : nBlockYOff >= DIV_ROUND_UP(nRasterYSize, nBlockYSize))
594 19 : return nullptr;
595 :
596 185 : uint64_t curStrileIdx =
597 370 : static_cast<uint64_t>(nBlockYOff) *
598 185 : DIV_ROUND_UP(nRasterXSize, nBlockXSize) +
599 185 : nBlockXOff;
600 185 : if (l_poDS->m_image->planarConfiguration() ==
601 : LIBERTIFF_NS::PlanarConfiguration::Separate)
602 : {
603 180 : curStrileIdx += (nBand - 1) *
604 120 : static_cast<uint64_t>(
605 120 : DIV_ROUND_UP(nRasterXSize, nBlockXSize)) *
606 60 : DIV_ROUND_UP(nRasterYSize, nBlockYSize);
607 : }
608 :
609 185 : bool ok = true;
610 : const uint64_t offset =
611 185 : l_poDS->m_image->strileOffset(curStrileIdx, ok);
612 185 : if (!offset)
613 : {
614 1 : return nullptr;
615 : }
616 :
617 184 : return CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(offset));
618 : }
619 :
620 148 : if (sscanf(pszName, "BLOCK_SIZE_%d_%d", &nBlockXOff, &nBlockYOff) == 2)
621 : {
622 148 : if (nBlockXOff < 0 ||
623 148 : nBlockXOff >= DIV_ROUND_UP(nRasterXSize, nBlockXSize) ||
624 147 : nBlockYOff < 0 ||
625 147 : nBlockYOff >= DIV_ROUND_UP(nRasterYSize, nBlockYSize))
626 2 : return nullptr;
627 :
628 146 : uint64_t curStrileIdx =
629 292 : static_cast<uint64_t>(nBlockYOff) *
630 146 : DIV_ROUND_UP(nRasterXSize, nBlockXSize) +
631 146 : nBlockXOff;
632 146 : if (l_poDS->m_image->planarConfiguration() ==
633 : LIBERTIFF_NS::PlanarConfiguration::Separate)
634 : {
635 180 : curStrileIdx += (nBand - 1) *
636 120 : static_cast<uint64_t>(
637 120 : DIV_ROUND_UP(nRasterXSize, nBlockXSize)) *
638 60 : DIV_ROUND_UP(nRasterYSize, nBlockYSize);
639 : }
640 :
641 146 : bool ok = true;
642 : const uint64_t size =
643 146 : l_poDS->m_image->strileByteCount(curStrileIdx, ok);
644 146 : if (!size)
645 : {
646 1 : return nullptr;
647 : }
648 :
649 145 : return CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(size));
650 : }
651 : }
652 2 : return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
653 : }
654 :
655 : /************************************************************************/
656 : /* GetTLSState() */
657 : /************************************************************************/
658 :
659 8714 : LIBERTIFFDataset::ThreadLocalState &LIBERTIFFDataset::GetTLSState() const
660 : {
661 : static thread_local lru11::Cache<const LIBERTIFFDataset *,
662 : std::shared_ptr<ThreadLocalState>>
663 8714 : tlsState;
664 :
665 8685 : std::shared_ptr<ThreadLocalState> value;
666 8714 : if (tlsState.tryGet(this, value))
667 : {
668 8683 : if (value->isValid())
669 7722 : return *value.get();
670 : }
671 970 : value = std::make_shared<ThreadLocalState>(this);
672 968 : tlsState.insert(this, value);
673 968 : return *value.get();
674 : }
675 :
676 : /************************************************************************/
677 : /* IRasterIO() */
678 : /************************************************************************/
679 :
680 3096 : CPLErr LIBERTIFFDataset::IRasterIO(
681 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
682 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
683 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
684 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
685 : {
686 3096 : if (eRWFlag != GF_Read)
687 0 : return CE_Failure;
688 :
689 : // Try to pass the request to the most appropriate overview dataset.
690 3096 : if (nBufXSize < nXSize && nBufYSize < nYSize)
691 : {
692 7 : int bTried = FALSE;
693 7 : const CPLErr eErr = TryOverviewRasterIO(
694 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
695 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
696 : nBandSpace, psExtraArg, &bTried);
697 7 : if (bTried)
698 1 : return eErr;
699 : }
700 :
701 3095 : const GDALDataType eNativeDT = papoBands[0]->GetRasterDataType();
702 : const size_t nNativeDTSize =
703 3095 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eNativeDT));
704 : int nBlockXSize, nBlockYSize;
705 3095 : papoBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
706 :
707 3095 : const int iXBlockMin = nXOff / nBlockXSize;
708 3095 : const int iYBlockMin = nYOff / nBlockYSize;
709 3095 : if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
710 : {
711 678 : ThreadLocalState &tlsState = GetTLSState();
712 : const double dfNoData =
713 678 : cpl::down_cast<LIBERTIFFBand *>(papoBands[0])->m_dfNoData;
714 678 : const size_t nXYOffset =
715 678 : static_cast<size_t>(nYOff % nBlockYSize) * nBlockXSize +
716 678 : (nXOff % nBlockXSize);
717 678 : if (m_image->planarConfiguration() ==
718 : LIBERTIFF_NS::PlanarConfiguration::Separate)
719 : {
720 61 : for (int iBand = 0; iBand < nBandCount; ++iBand)
721 : {
722 46 : int anBand[] = {panBandMap[iBand]};
723 46 : if (!ReadBlock(nullptr, iXBlockMin, iYBlockMin, 1, anBand,
724 : eBufType, nPixelSpace, nLineSpace, nBandSpace))
725 : {
726 2 : return CE_Failure;
727 : }
728 44 : if (tlsState.m_curStrileMissing)
729 : {
730 1 : GDALCopyWords64(&dfNoData, GDT_Float64, 0,
731 : static_cast<GByte *>(pData) +
732 1 : iBand * nBandSpace,
733 : eBufType, 0, 1);
734 : }
735 : else
736 : {
737 86 : GDALCopyWords64(tlsState.m_decompressedBuffer.data() +
738 43 : nNativeDTSize * nXYOffset,
739 : eNativeDT, 0,
740 : static_cast<GByte *>(pData) +
741 43 : iBand * nBandSpace,
742 : eBufType, 0, 1);
743 : }
744 : }
745 : }
746 : else
747 : {
748 661 : if (!ReadBlock(nullptr, iXBlockMin, iYBlockMin, nBandCount,
749 : panBandMap, eBufType, nPixelSpace, nLineSpace,
750 : nBandSpace))
751 : {
752 117 : return CE_Failure;
753 : }
754 1158 : for (int iBand = 0; iBand < nBandCount; ++iBand)
755 : {
756 614 : if (tlsState.m_curStrileMissing)
757 : {
758 13 : GDALCopyWords64(&dfNoData, GDT_Float64, 0,
759 : static_cast<GByte *>(pData) +
760 13 : iBand * nBandSpace,
761 : eBufType, 0, 1);
762 : }
763 : else
764 : {
765 1202 : GDALCopyWords64(
766 1202 : tlsState.m_decompressedBuffer.data() +
767 601 : nNativeDTSize *
768 601 : (panBandMap[iBand] - 1 + nXYOffset * nBands),
769 : eNativeDT, 0,
770 601 : static_cast<GByte *>(pData) + iBand * nBandSpace,
771 : eBufType, 0, 1);
772 : }
773 : }
774 : }
775 559 : return CE_None;
776 : }
777 :
778 : // Check that request is full resolution and aligned on block boundaries
779 : // (with the exception of the right and bottom most blocks that can be
780 : // truncated)
781 2417 : if (nXSize != nBufXSize || nYSize != nBufYSize ||
782 2411 : (nXOff % nBlockXSize) != 0 || (nYOff % nBlockYSize) != 0 ||
783 2408 : !(nXOff + nXSize == nRasterXSize || (nBufXSize % nBlockXSize) == 0) ||
784 2408 : !(nYOff + nYSize == nRasterYSize || (nBufYSize % nBlockYSize) == 0))
785 : {
786 9 : const int nXOffMod = (nXOff / nBlockXSize) * nBlockXSize;
787 9 : const int nYOffMod = (nYOff / nBlockYSize) * nBlockYSize;
788 9 : const int nXOff2Mod = static_cast<int>(std::min(
789 18 : static_cast<int64_t>(nRasterXSize),
790 9 : static_cast<int64_t>(DIV_ROUND_UP(nXOff + nXSize, nBlockXSize)) *
791 9 : nBlockXSize));
792 9 : const int nYOff2Mod = static_cast<int>(std::min(
793 18 : static_cast<int64_t>(nRasterYSize),
794 9 : static_cast<int64_t>(DIV_ROUND_UP(nYOff + nYSize, nBlockYSize)) *
795 9 : nBlockYSize));
796 9 : const int nXSizeMod = nXOff2Mod - nXOffMod;
797 9 : const int nYSizeMod = nYOff2Mod - nYOffMod;
798 9 : std::vector<GByte> &abyTmp = GetTLSState().m_abyIRasterIOBuffer;
799 :
800 : try
801 : {
802 18 : if (nNativeDTSize * nBandCount >
803 9 : std::numeric_limits<size_t>::max() /
804 9 : (static_cast<uint64_t>(nXSizeMod) * nYSizeMod))
805 : {
806 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
807 : "Out of memory allocating temporary buffer");
808 0 : return CE_Failure;
809 : }
810 9 : const size_t nSize =
811 9 : nNativeDTSize * nBandCount * nXSizeMod * nYSizeMod;
812 9 : if (abyTmp.size() < nSize)
813 3 : abyTmp.resize(nSize);
814 : }
815 0 : catch (const std::exception &)
816 : {
817 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
818 : "Out of memory allocating temporary buffer");
819 0 : return CE_Failure;
820 : }
821 :
822 : {
823 : GDALRasterIOExtraArg sExtraArg;
824 9 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
825 :
826 9 : if (IRasterIO(GF_Read, nXOffMod, nYOffMod, nXSizeMod, nYSizeMod,
827 9 : abyTmp.data(), nXSizeMod, nYSizeMod, eNativeDT,
828 : nBandCount, panBandMap,
829 9 : GDALGetDataTypeSizeBytes(eNativeDT),
830 9 : nNativeDTSize * nXSizeMod,
831 9 : nNativeDTSize * nXSizeMod * nYSizeMod,
832 9 : &sExtraArg) != CE_None)
833 : {
834 0 : return CE_Failure;
835 : }
836 : }
837 :
838 : auto poMEMDS = std::unique_ptr<MEMDataset>(MEMDataset::Create(
839 18 : "", nXSizeMod, nYSizeMod, 0, GDT_Unknown, nullptr));
840 9 : if (!poMEMDS)
841 : {
842 0 : return CE_Failure;
843 : }
844 30 : for (int i = 0; i < nBandCount; ++i)
845 : {
846 : GByte *pabyData =
847 21 : abyTmp.data() + i * nNativeDTSize * nXSizeMod * nYSizeMod;
848 21 : GDALRasterBandH hMEMBand = MEMCreateRasterBandEx(
849 21 : poMEMDS.get(), i + 1, pabyData, eNativeDT, nNativeDTSize,
850 21 : nNativeDTSize * nXSizeMod, false);
851 21 : poMEMDS->AddMEMBand(hMEMBand);
852 : }
853 :
854 : GDALRasterIOExtraArg sExtraArg;
855 9 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
856 : // cppcheck-suppress redundantAssignment
857 9 : sExtraArg.eResampleAlg = psExtraArg->eResampleAlg;
858 9 : sExtraArg.bFloatingPointWindowValidity =
859 9 : psExtraArg->bFloatingPointWindowValidity;
860 9 : if (sExtraArg.bFloatingPointWindowValidity)
861 : {
862 2 : sExtraArg.dfXOff = psExtraArg->dfXOff - nXOffMod;
863 2 : sExtraArg.dfYOff = psExtraArg->dfYOff - nYOffMod;
864 2 : sExtraArg.dfXSize = psExtraArg->dfXSize;
865 2 : sExtraArg.dfYSize = psExtraArg->dfYSize;
866 : }
867 9 : return poMEMDS->RasterIO(GF_Read, nXOff - nXOffMod, nYOff - nYOffMod,
868 : nXSize, nYSize, pData, nBufXSize, nBufYSize,
869 : eBufType, nBandCount, nullptr, nPixelSpace,
870 9 : nLineSpace, nBandSpace, &sExtraArg);
871 : }
872 :
873 2408 : const int iYBlockMax = DIV_ROUND_UP(nYOff + nBufYSize, nBlockYSize);
874 2408 : const int iXBlockMax = DIV_ROUND_UP(nXOff + nBufXSize, nBlockXSize);
875 :
876 0 : CPLJobQueuePtr poQueue;
877 2408 : const bool bIsSeparate = m_image->planarConfiguration() ==
878 2408 : LIBERTIFF_NS::PlanarConfiguration::Separate;
879 2408 : if (m_poThreadPool &&
880 506 : (iYBlockMax - iYBlockMin > 1 || iXBlockMax - iXBlockMin > 1 ||
881 62 : (bIsSeparate && nBandCount > 1)))
882 : {
883 261 : poQueue = m_poThreadPool->CreateJobQueue();
884 : }
885 2407 : std::atomic<bool> bSuccess(true);
886 :
887 4922 : for (int iYBlock = iYBlockMin, iY = 0; iYBlock < iYBlockMax && bSuccess;
888 : ++iYBlock, ++iY)
889 : {
890 9822 : for (int iXBlock = iXBlockMin, iX = 0; iXBlock < iXBlockMax && bSuccess;
891 : ++iXBlock, ++iX)
892 : {
893 7304 : if (bIsSeparate)
894 : {
895 11331 : for (int iBand = 0; iBand < nBandCount; ++iBand)
896 : {
897 5670 : const auto lambda = [this, &bSuccess, iBand, panBandMap,
898 : pData, iY, nLineSpace, nBlockYSize, iX,
899 : nPixelSpace, nBlockXSize, nBandSpace,
900 5670 : iXBlock, iYBlock, eBufType]()
901 : {
902 5670 : int anBand[] = {panBandMap[iBand]};
903 5682 : if (!ReadBlock(static_cast<GByte *>(pData) +
904 5670 : iY * nLineSpace * nBlockYSize +
905 5670 : iX * nPixelSpace * nBlockXSize +
906 5670 : iBand * nBandSpace,
907 : iXBlock, iYBlock, 1, anBand, eBufType,
908 : nPixelSpace, nLineSpace, nBandSpace))
909 : {
910 0 : bSuccess = false;
911 : }
912 5682 : };
913 5675 : if (poQueue)
914 : {
915 496 : poQueue->SubmitJob(lambda);
916 : }
917 : else
918 : {
919 5176 : lambda();
920 : }
921 : }
922 : }
923 : else
924 : {
925 1655 : const auto lambda = [this, &bSuccess, nBandCount, panBandMap,
926 : pData, iY, nLineSpace, nBlockYSize, iX,
927 : nPixelSpace, nBlockXSize, nBandSpace,
928 1674 : iXBlock, iYBlock, eBufType]()
929 : {
930 1655 : if (!ReadBlock(static_cast<GByte *>(pData) +
931 1655 : iY * nLineSpace * nBlockYSize +
932 1655 : iX * nPixelSpace * nBlockXSize,
933 : iXBlock, iYBlock, nBandCount, panBandMap,
934 : eBufType, nPixelSpace, nLineSpace,
935 : nBandSpace))
936 : {
937 19 : bSuccess = false;
938 : }
939 3308 : };
940 1653 : if (poQueue)
941 : {
942 544 : poQueue->SubmitJob(lambda);
943 : }
944 : else
945 : {
946 1111 : lambda();
947 : }
948 : }
949 : }
950 : }
951 :
952 2408 : if (poQueue)
953 260 : poQueue->WaitCompletion();
954 :
955 2408 : return bSuccess ? CE_None : CE_Failure;
956 : }
957 :
958 : /************************************************************************/
959 : /* HorizPredictorDecode() */
960 : /************************************************************************/
961 :
962 : template <class T, class U>
963 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static void
964 200 : HorizPredictorDecode1Component(void *bufferIn, size_t nPixelCount)
965 : {
966 200 : T *buffer = static_cast<T *>(bufferIn);
967 200 : constexpr T mask = std::numeric_limits<T>::max();
968 200 : U acc = buffer[0];
969 200 : size_t i = 1;
970 2300 : for (; i + 3 < nPixelCount; i += 4)
971 : {
972 2100 : acc += buffer[i];
973 2100 : buffer[i] = static_cast<T>(acc & mask);
974 2100 : acc += buffer[i + 1];
975 2100 : buffer[i + 1] = static_cast<T>(acc & mask);
976 2100 : acc += buffer[i + 2];
977 2100 : buffer[i + 2] = static_cast<T>(acc & mask);
978 2100 : acc += buffer[i + 3];
979 2100 : buffer[i + 3] = static_cast<T>(acc & mask);
980 : }
981 800 : for (; i < nPixelCount; ++i)
982 : {
983 600 : acc += buffer[i];
984 600 : buffer[i] = static_cast<T>(acc & mask);
985 : }
986 200 : }
987 :
988 : template <class T, class U>
989 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static void
990 1200 : HorizPredictorDecode2Components(void *bufferIn, size_t nPixelCount)
991 : {
992 1200 : T *buffer = static_cast<T *>(bufferIn);
993 1200 : constexpr T mask = std::numeric_limits<T>::max();
994 1200 : U acc0 = buffer[0];
995 1200 : U acc1 = buffer[1];
996 194400 : for (size_t i = 1; i < nPixelCount; ++i)
997 : {
998 193200 : acc0 += buffer[i * 2 + 0];
999 193200 : acc1 += buffer[i * 2 + 1];
1000 193200 : buffer[i * 2 + 0] = static_cast<T>(acc0 & mask);
1001 193200 : buffer[i * 2 + 1] = static_cast<T>(acc1 & mask);
1002 : }
1003 1200 : }
1004 :
1005 : template <class T, class U>
1006 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static void
1007 2450 : HorizPredictorDecode3Components(void *bufferIn, size_t nPixelCount)
1008 : {
1009 2450 : T *buffer = static_cast<T *>(bufferIn);
1010 2450 : constexpr T mask = std::numeric_limits<T>::max();
1011 2450 : U acc0 = buffer[0];
1012 2450 : U acc1 = buffer[1];
1013 2450 : U acc2 = buffer[2];
1014 122500 : for (size_t i = 1; i < nPixelCount; ++i)
1015 : {
1016 120050 : acc0 += buffer[i * 3 + 0];
1017 120050 : acc1 += buffer[i * 3 + 1];
1018 120050 : acc2 += buffer[i * 3 + 2];
1019 120050 : buffer[i * 3 + 0] = static_cast<T>(acc0 & mask);
1020 120050 : buffer[i * 3 + 1] = static_cast<T>(acc1 & mask);
1021 120050 : buffer[i * 3 + 2] = static_cast<T>(acc2 & mask);
1022 : }
1023 2450 : }
1024 :
1025 : template <class T, class U>
1026 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static void
1027 632 : HorizPredictorDecode4Components(void *bufferIn, size_t nPixelCount)
1028 : {
1029 632 : T *buffer = static_cast<T *>(bufferIn);
1030 632 : constexpr T mask = std::numeric_limits<T>::max();
1031 632 : U acc0 = buffer[0];
1032 632 : U acc1 = buffer[1];
1033 632 : U acc2 = buffer[2];
1034 632 : U acc3 = buffer[3];
1035 98224 : for (size_t i = 1; i < nPixelCount; ++i)
1036 : {
1037 97592 : acc0 += buffer[i * 4 + 0];
1038 97592 : acc1 += buffer[i * 4 + 1];
1039 97592 : acc2 += buffer[i * 4 + 2];
1040 97592 : acc3 += buffer[i * 4 + 3];
1041 97592 : buffer[i * 4 + 0] = static_cast<T>(acc0 & mask);
1042 97592 : buffer[i * 4 + 1] = static_cast<T>(acc1 & mask);
1043 97592 : buffer[i * 4 + 2] = static_cast<T>(acc2 & mask);
1044 97592 : buffer[i * 4 + 3] = static_cast<T>(acc3 & mask);
1045 : }
1046 632 : }
1047 :
1048 : template <class T>
1049 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static void
1050 4502 : HorizPredictorDecode(void *bufferIn, size_t nPixelCount,
1051 : int nComponentsPerPixel)
1052 : {
1053 : static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> ||
1054 : std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>);
1055 :
1056 4502 : if (nComponentsPerPixel == 1)
1057 : {
1058 : // cppcheck-suppress duplicateBranch
1059 : if constexpr (sizeof(T) < sizeof(uint64_t))
1060 : {
1061 160 : HorizPredictorDecode1Component<T, uint32_t>(bufferIn, nPixelCount);
1062 : }
1063 : else
1064 : {
1065 40 : HorizPredictorDecode1Component<T, T>(bufferIn, nPixelCount);
1066 : }
1067 : }
1068 4302 : else if (nComponentsPerPixel == 2)
1069 : {
1070 : // cppcheck-suppress duplicateBranch
1071 : if constexpr (sizeof(T) < sizeof(uint64_t))
1072 : {
1073 900 : HorizPredictorDecode2Components<T, uint32_t>(bufferIn, nPixelCount);
1074 : }
1075 : else
1076 : {
1077 300 : HorizPredictorDecode2Components<T, T>(bufferIn, nPixelCount);
1078 : }
1079 : }
1080 3102 : else if (nComponentsPerPixel == 3)
1081 : {
1082 : // cppcheck-suppress duplicateBranch
1083 : if constexpr (sizeof(T) < sizeof(uint64_t))
1084 : {
1085 1850 : HorizPredictorDecode3Components<T, uint32_t>(bufferIn, nPixelCount);
1086 : }
1087 : else
1088 : {
1089 600 : HorizPredictorDecode3Components<T, T>(bufferIn, nPixelCount);
1090 : }
1091 : }
1092 652 : else if (nComponentsPerPixel == 4)
1093 : {
1094 : // cppcheck-suppress duplicateBranch
1095 : if constexpr (sizeof(T) < sizeof(uint64_t))
1096 : {
1097 632 : HorizPredictorDecode4Components<T, uint32_t>(bufferIn, nPixelCount);
1098 : }
1099 : else
1100 : {
1101 0 : HorizPredictorDecode4Components<T, T>(bufferIn, nPixelCount);
1102 : }
1103 : }
1104 : else
1105 : {
1106 20 : T *buffer = static_cast<T *>(bufferIn);
1107 20 : constexpr T mask = std::numeric_limits<T>::max();
1108 400 : for (size_t i = 1; i < nPixelCount; ++i)
1109 : {
1110 2280 : for (int j = 0; j < nComponentsPerPixel; j++)
1111 : {
1112 1900 : buffer[i * nComponentsPerPixel + j] =
1113 1900 : static_cast<T>((buffer[i * nComponentsPerPixel + j] +
1114 1900 : buffer[(i - 1) * nComponentsPerPixel + j]) &
1115 : mask);
1116 : }
1117 : }
1118 : }
1119 4502 : }
1120 :
1121 : /************************************************************************/
1122 : /* FloatingPointHorizPredictorDecode() */
1123 : /************************************************************************/
1124 :
1125 : template <class T>
1126 : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW static bool
1127 60 : FloatingPointHorizPredictorDecode(std::vector<uint8_t> &tmpBuffer,
1128 : void *bufferIn, size_t nPixelCount,
1129 : int nComponentsPerPixel)
1130 : {
1131 60 : uint8_t *buffer = static_cast<uint8_t *>(bufferIn);
1132 60 : HorizPredictorDecode<uint8_t>(buffer, nPixelCount * sizeof(T),
1133 : nComponentsPerPixel);
1134 :
1135 60 : const size_t tmpBufferSize = nPixelCount * nComponentsPerPixel * sizeof(T);
1136 60 : if (tmpBuffer.size() < tmpBufferSize)
1137 : {
1138 : try
1139 : {
1140 3 : tmpBuffer.resize(tmpBufferSize);
1141 : }
1142 0 : catch (const std::exception &)
1143 : {
1144 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1145 : "Out of memory in FloatingPointHorizPredictorDecode()");
1146 0 : return false;
1147 : }
1148 : }
1149 60 : memcpy(tmpBuffer.data(), buffer, tmpBufferSize);
1150 60 : constexpr uint32_t bytesPerWords = static_cast<uint32_t>(sizeof(T));
1151 60 : const size_t wordCount = nPixelCount * nComponentsPerPixel;
1152 :
1153 60 : size_t iWord = 0;
1154 :
1155 : #if defined(__x86_64__) || defined(_M_X64)
1156 : if constexpr (bytesPerWords == 4)
1157 : {
1158 : /* Optimization of general case */
1159 80 : for (; iWord + 15 < wordCount; iWord += 16)
1160 : {
1161 : /* Interlace 4*16 byte values */
1162 :
1163 40 : __m128i xmm0 = _mm_loadu_si128(reinterpret_cast<__m128i const *>(
1164 40 : tmpBuffer.data() + iWord + 3 * wordCount));
1165 40 : __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<__m128i const *>(
1166 40 : tmpBuffer.data() + iWord + 2 * wordCount));
1167 40 : __m128i xmm2 = _mm_loadu_si128(reinterpret_cast<__m128i const *>(
1168 40 : tmpBuffer.data() + iWord + 1 * wordCount));
1169 40 : __m128i xmm3 = _mm_loadu_si128(reinterpret_cast<__m128i const *>(
1170 40 : tmpBuffer.data() + iWord + 0 * wordCount));
1171 : /* (xmm0_0, xmm1_0, xmm0_1, xmm1_1, xmm0_2, xmm1_2, ...) */
1172 40 : __m128i tmp0 = _mm_unpacklo_epi8(xmm0, xmm1);
1173 : /* (xmm0_8, xmm1_8, xmm0_9, xmm1_9, xmm0_10, xmm1_10, ...) */
1174 40 : __m128i tmp1 = _mm_unpackhi_epi8(xmm0, xmm1);
1175 : /* (xmm2_0, xmm3_0, xmm2_1, xmm3_1, xmm2_2, xmm3_2, ...) */
1176 40 : __m128i tmp2 = _mm_unpacklo_epi8(xmm2, xmm3);
1177 : /* (xmm2_8, xmm3_8, xmm2_9, xmm3_9, xmm2_10, xmm3_10, ...) */
1178 40 : __m128i tmp3 = _mm_unpackhi_epi8(xmm2, xmm3);
1179 : /* (xmm0_0, xmm1_0, xmm2_0, xmm3_0, xmm0_1, xmm1_1, xmm2_1, xmm3_1, ...) */
1180 40 : __m128i tmp2_0 = _mm_unpacklo_epi16(tmp0, tmp2);
1181 40 : __m128i tmp2_1 = _mm_unpackhi_epi16(tmp0, tmp2);
1182 40 : __m128i tmp2_2 = _mm_unpacklo_epi16(tmp1, tmp3);
1183 40 : __m128i tmp2_3 = _mm_unpackhi_epi16(tmp1, tmp3);
1184 : _mm_storeu_si128(
1185 40 : reinterpret_cast<__m128i *>(buffer + 4 * iWord + 0 * 16),
1186 : tmp2_0);
1187 : _mm_storeu_si128(
1188 40 : reinterpret_cast<__m128i *>(buffer + 4 * iWord + 1 * 16),
1189 : tmp2_1);
1190 : _mm_storeu_si128(
1191 40 : reinterpret_cast<__m128i *>(buffer + 4 * iWord + 2 * 16),
1192 : tmp2_2);
1193 : _mm_storeu_si128(
1194 40 : reinterpret_cast<__m128i *>(buffer + 4 * iWord + 3 * 16),
1195 : tmp2_3);
1196 : }
1197 : }
1198 : #endif
1199 :
1200 620 : for (; iWord < wordCount; iWord++)
1201 : {
1202 4400 : for (uint32_t iByte = 0; iByte < bytesPerWords; iByte++)
1203 : {
1204 : #ifdef CPL_MSB
1205 : buffer[bytesPerWords * iWord + iByte] =
1206 : tmpBuffer[iByte * wordCount + iWord];
1207 : #else
1208 3840 : buffer[bytesPerWords * iWord + iByte] =
1209 3840 : tmpBuffer[(bytesPerWords - iByte - 1) * wordCount + iWord];
1210 : #endif
1211 : }
1212 : }
1213 60 : return true;
1214 : }
1215 :
1216 : /************************************************************************/
1217 : /* ReadBlock() */
1218 : /************************************************************************/
1219 :
1220 8039 : bool LIBERTIFFDataset::ReadBlock(GByte *pabyBlockData, int nBlockXOff,
1221 : int nBlockYOff, int nBandCount,
1222 : BANDMAP_TYPE panBandMap, GDALDataType eBufType,
1223 : GSpacing nPixelSpace, GSpacing nLineSpace,
1224 : GSpacing nBandSpace) const
1225 : {
1226 8039 : uint64_t offset = 0;
1227 8039 : size_t size = 0;
1228 8039 : const bool bSeparate = m_image->planarConfiguration() ==
1229 8027 : LIBERTIFF_NS::PlanarConfiguration::Separate;
1230 :
1231 8027 : ThreadLocalState &tlsState = GetTLSState();
1232 :
1233 8016 : const int iBandTIFFFirst = bSeparate ? panBandMap[0] - 1 : 0;
1234 : uint64_t curStrileIdx;
1235 8016 : if (m_image->isTiled())
1236 : {
1237 6402 : bool ok = true;
1238 6402 : curStrileIdx = m_image->tileCoordinateToIdx(nBlockXOff, nBlockYOff,
1239 : iBandTIFFFirst, ok);
1240 : }
1241 : else
1242 : {
1243 1593 : if (bSeparate)
1244 177 : curStrileIdx =
1245 177 : nBlockYOff + DIV_ROUND_UP(m_image->height(),
1246 177 : m_image->rowsPerStripSanitized()) *
1247 177 : iBandTIFFFirst;
1248 : else
1249 1416 : curStrileIdx = nBlockYOff;
1250 : }
1251 8025 : if (curStrileIdx != tlsState.m_curStrileIdx)
1252 : {
1253 7903 : bool ok = true;
1254 7903 : offset = curStrileIdx < m_tileOffsets.size()
1255 15712 : ? m_tileOffsets[static_cast<size_t>(curStrileIdx)]
1256 7890 : : curStrileIdx < m_tileOffsets64.size()
1257 7884 : ? m_tileOffsets64[static_cast<size_t>(curStrileIdx)]
1258 7884 : : m_image->strileOffset(curStrileIdx, ok);
1259 7822 : if (!ok)
1260 : {
1261 42 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read strile offset");
1262 88 : return false;
1263 : }
1264 : const uint64_t size64 =
1265 7780 : curStrileIdx < m_tileByteCounts.size()
1266 7791 : ? m_tileByteCounts[static_cast<size_t>(curStrileIdx)]
1267 7778 : : m_image->strileByteCount(curStrileIdx, ok);
1268 7785 : if (!ok)
1269 : {
1270 18 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot read strile size");
1271 18 : return false;
1272 : }
1273 :
1274 : if constexpr (sizeof(size_t) < sizeof(uint64_t))
1275 : {
1276 : if (size64 > std::numeric_limits<size_t>::max() - 1)
1277 : {
1278 : CPLError(CE_Failure, CPLE_NotSupported, "Too large strile");
1279 : return false;
1280 : }
1281 : }
1282 7767 : size = static_cast<size_t>(size64);
1283 : // Avoid doing non-sensical memory allocations
1284 7767 : constexpr size_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1024 * 1024;
1285 7848 : if (size > THRESHOLD_CHECK_FILE_SIZE &&
1286 28 : size > m_image->readContext()->size())
1287 : {
1288 28 : CPLError(CE_Failure, CPLE_NotSupported,
1289 : "Strile size larger than file size");
1290 28 : return false;
1291 : }
1292 : }
1293 :
1294 7914 : const GDALDataType eNativeDT = papoBands[0]->GetRasterDataType();
1295 : int nBlockXSize, nBlockYSize;
1296 7828 : papoBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
1297 : const int nBlockActualXSize =
1298 7844 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
1299 : const int nBlockActualYSize =
1300 7879 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
1301 :
1302 : // Sparse block?
1303 7867 : if ((curStrileIdx != tlsState.m_curStrileIdx && size == 0) ||
1304 7850 : (curStrileIdx == tlsState.m_curStrileIdx &&
1305 115 : tlsState.m_curStrileMissing))
1306 : {
1307 20 : if (pabyBlockData)
1308 : {
1309 : const double dfNoData =
1310 8 : cpl::down_cast<LIBERTIFFBand *>(papoBands[0])->m_dfNoData;
1311 16 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1312 : {
1313 92 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1314 : {
1315 84 : GDALCopyWords64(&dfNoData, GDT_Float64, 0,
1316 84 : pabyBlockData + iBand * nBandSpace +
1317 84 : iY * nLineSpace,
1318 : eBufType, static_cast<int>(nPixelSpace),
1319 : nBlockActualXSize);
1320 : }
1321 : }
1322 : }
1323 :
1324 20 : tlsState.m_curStrileIdx = curStrileIdx;
1325 20 : tlsState.m_curStrileMissing = true;
1326 20 : return true;
1327 : }
1328 :
1329 7847 : std::vector<GByte> &abyDecompressedStrile = tlsState.m_decompressedBuffer;
1330 : const size_t nNativeDTSize =
1331 7847 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eNativeDT));
1332 :
1333 7890 : if (curStrileIdx != tlsState.m_curStrileIdx)
1334 : {
1335 7743 : std::vector<GByte> &bufferForOneBitExpansion =
1336 : tlsState.m_bufferForOneBitExpansion;
1337 :
1338 : // Overflow in multiplication checked in Open() method
1339 7743 : const int nComponentsPerPixel = bSeparate ? 1 : nBands;
1340 : const size_t nActualPixelCount =
1341 7743 : static_cast<size_t>(m_image->isTiled() ? nBlockYSize
1342 : : nBlockActualYSize) *
1343 7732 : nBlockXSize;
1344 : const int nLineSizeBytes =
1345 7732 : m_image->bitsPerSample() == 1 ? (nBlockXSize + 7) / 8 : nBlockXSize;
1346 : const size_t nActualUncompressedSize =
1347 7758 : nNativeDTSize *
1348 7726 : static_cast<size_t>(m_image->isTiled() ? nBlockYSize
1349 7758 : : nBlockActualYSize) *
1350 7758 : nLineSizeBytes * nComponentsPerPixel;
1351 :
1352 : // Allocate buffer for decompressed strile
1353 7758 : if (abyDecompressedStrile.empty())
1354 : {
1355 877 : const size_t nMaxUncompressedSize =
1356 877 : nNativeDTSize * nBlockXSize * nBlockYSize * nComponentsPerPixel;
1357 : try
1358 : {
1359 877 : abyDecompressedStrile.resize(nMaxUncompressedSize);
1360 877 : if (m_image->bitsPerSample() == 1)
1361 379 : bufferForOneBitExpansion.resize(nMaxUncompressedSize);
1362 : }
1363 0 : catch (const std::exception &)
1364 : {
1365 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1366 : "Out of memory allocating temporary buffer");
1367 0 : return false;
1368 : }
1369 : }
1370 :
1371 7739 : if (m_image->compression() != LIBERTIFF_NS::Compression::None)
1372 : {
1373 6923 : std::vector<GByte> &abyCompressedStrile =
1374 : tlsState.m_compressedBuffer;
1375 6923 : if (size > 128 && size / 16 > nActualUncompressedSize)
1376 : {
1377 0 : CPLError(CE_Failure, CPLE_AppDefined,
1378 : "Compressed strile size is much larger than "
1379 : "uncompressed size");
1380 16 : return false;
1381 : }
1382 6923 : if (abyCompressedStrile.size() < size + m_jpegTables.size())
1383 : {
1384 : try
1385 : {
1386 619 : abyCompressedStrile.resize(size + m_jpegTables.size());
1387 : }
1388 0 : catch (const std::exception &)
1389 : {
1390 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1391 : "Out of memory allocating temporary buffer");
1392 0 : return false;
1393 : }
1394 : }
1395 :
1396 6949 : bool ok = true;
1397 13855 : m_image->readContext()->read(offset, size,
1398 6932 : abyCompressedStrile.data(), ok);
1399 6994 : if (!ok)
1400 : {
1401 0 : CPLError(CE_Failure, CPLE_FileIO,
1402 : "Cannot read strile from disk");
1403 0 : return false;
1404 : }
1405 :
1406 6994 : if (!tlsState.m_tiff.tif_decodestrip)
1407 : {
1408 5960 : if (m_image->compression() == LIBERTIFF_NS::Compression::LZW)
1409 : {
1410 75 : TIFFInitLZW(&tlsState.m_tiff, m_image->compression());
1411 : }
1412 5878 : else if (m_image->compression() ==
1413 : LIBERTIFF_NS::Compression::PackBits)
1414 : {
1415 7 : TIFFInitPackBits(&tlsState.m_tiff, m_image->compression());
1416 : }
1417 : #ifdef LERC_SUPPORT
1418 5880 : else if (m_image->compression() ==
1419 : LIBERTIFF_NS::Compression::LERC)
1420 : {
1421 67 : TIFFInitLERC(&tlsState.m_tiff, m_image->compression());
1422 67 : LERCState *sp =
1423 : reinterpret_cast<LERCState *>(tlsState.m_tiff.tif_data);
1424 67 : sp->lerc_version = m_lercVersion;
1425 67 : sp->additional_compression = m_lercAdditionalCompression;
1426 : }
1427 : #endif
1428 :
1429 5935 : if (tlsState.m_tiff.tif_decodestrip)
1430 : {
1431 149 : tlsState.m_tiff.tif_name =
1432 149 : const_cast<char *>(GetDescription());
1433 148 : tlsState.m_tiff.tif_dir.td_sampleformat =
1434 149 : static_cast<uint16_t>(m_image->sampleFormat());
1435 148 : tlsState.m_tiff.tif_dir.td_bitspersample =
1436 148 : static_cast<uint16_t>(m_image->bitsPerSample());
1437 148 : if (m_image->isTiled())
1438 : {
1439 64 : tlsState.m_tiff.tif_flags = TIFF_ISTILED;
1440 64 : tlsState.m_tiff.tif_dir.td_tilewidth =
1441 64 : m_image->tileWidth();
1442 65 : tlsState.m_tiff.tif_dir.td_tilelength =
1443 64 : m_image->tileHeight();
1444 : }
1445 : else
1446 : {
1447 84 : tlsState.m_tiff.tif_dir.td_imagewidth =
1448 84 : m_image->width();
1449 84 : tlsState.m_tiff.tif_dir.td_imagelength =
1450 84 : m_image->height();
1451 84 : tlsState.m_tiff.tif_dir.td_rowsperstrip =
1452 84 : m_image->rowsPerStripSanitized();
1453 : }
1454 149 : tlsState.m_tiff.tif_dir.td_samplesperpixel =
1455 149 : static_cast<uint16_t>(m_image->samplesPerPixel());
1456 149 : tlsState.m_tiff.tif_dir.td_planarconfig =
1457 149 : static_cast<uint16_t>(m_image->planarConfiguration());
1458 149 : if (m_extraSamples.size() < 65536)
1459 : {
1460 149 : tlsState.m_tiff.tif_dir.td_extrasamples =
1461 149 : static_cast<uint16_t>(m_extraSamples.size());
1462 149 : tlsState.m_tiff.tif_dir.td_sampleinfo =
1463 149 : const_cast<uint16_t *>(m_extraSamples.data());
1464 : }
1465 : }
1466 : }
1467 :
1468 6969 : if (tlsState.m_tiff.tif_decodestrip)
1469 : {
1470 1128 : tlsState.m_tiff.tif_row = nBlockYOff * nBlockYSize;
1471 1128 : tlsState.m_tiff.tif_rawcc = size;
1472 1128 : tlsState.m_tiff.tif_rawdata = abyCompressedStrile.data();
1473 1128 : tlsState.m_tiff.tif_rawcp = tlsState.m_tiff.tif_rawdata;
1474 3373 : if ((tlsState.m_tiff.tif_predecode &&
1475 2256 : tlsState.m_tiff.tif_predecode(&tlsState.m_tiff, 0) == 0) ||
1476 1129 : tlsState.m_tiff.tif_decodestrip(
1477 : &tlsState.m_tiff, abyDecompressedStrile.data(),
1478 : nActualUncompressedSize, 0) == 0)
1479 : {
1480 14 : CPLError(CE_Failure, CPLE_AppDefined,
1481 : "Decompression failed");
1482 14 : return false;
1483 : }
1484 : }
1485 5841 : else if (m_image->compression() ==
1486 5544 : LIBERTIFF_NS::Compression::JPEG ||
1487 5534 : m_image->compression() ==
1488 5477 : LIBERTIFF_NS::Compression::WEBP ||
1489 16836 : m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
1490 5313 : m_image->compression() ==
1491 : LIBERTIFF_NS::Compression::JXL_DNG_1_7)
1492 : {
1493 510 : size_t blobSize = size;
1494 : const char *drvName =
1495 510 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG
1496 746 : ? "JPEG"
1497 236 : : m_image->compression() == LIBERTIFF_NS::Compression::WEBP
1498 236 : ? "WEBP"
1499 510 : : "JPEGXL";
1500 784 : if (m_image->compression() == LIBERTIFF_NS::Compression::JPEG &&
1501 784 : size > 2 && !m_jpegTables.empty())
1502 : {
1503 : // Insert JPEG tables into JPEG blob
1504 816 : memmove(abyCompressedStrile.data() + 2 +
1505 272 : m_jpegTables.size(),
1506 272 : abyCompressedStrile.data() + 2, size - 2);
1507 272 : memcpy(abyCompressedStrile.data() + 2, m_jpegTables.data(),
1508 : m_jpegTables.size());
1509 272 : blobSize += m_jpegTables.size();
1510 : }
1511 : const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
1512 1020 : std::string("tmp.").append(drvName).c_str());
1513 510 : VSIFCloseL(VSIFileFromMemBuffer(
1514 : osTmpFilename.c_str(), abyCompressedStrile.data(), blobSize,
1515 : /* bTakeOwnership = */ false));
1516 510 : const char *const apszAllowedDrivers[] = {drvName, nullptr};
1517 :
1518 : CPLConfigOptionSetter oJPEGtoRGBSetter(
1519 : "GDAL_JPEG_TO_RGB",
1520 784 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG &&
1521 319 : m_image->samplesPerPixel() == 4 &&
1522 45 : m_image->planarConfiguration() ==
1523 : LIBERTIFF_NS::PlanarConfiguration::Contiguous
1524 : ? "NO"
1525 : : "YES",
1526 784 : false);
1527 :
1528 : auto poTmpDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1529 : osTmpFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
1530 510 : apszAllowedDrivers, nullptr, nullptr));
1531 510 : VSIUnlink(osTmpFilename.c_str());
1532 510 : if (!poTmpDS)
1533 : {
1534 0 : CPLError(CE_Failure, CPLE_AppDefined, "Not a %s blob",
1535 : drvName);
1536 0 : return false;
1537 : }
1538 510 : if (poTmpDS->GetRasterCount() != nComponentsPerPixel ||
1539 1020 : poTmpDS->GetRasterXSize() != nBlockXSize ||
1540 510 : poTmpDS->GetRasterYSize() !=
1541 510 : (m_image->isTiled() ? nBlockYSize : nBlockActualYSize))
1542 : {
1543 0 : CPLError(CE_Failure, CPLE_AppDefined,
1544 : "%s blob has no expected dimensions (%dx%d "
1545 : "whereas %dx%d expected) or band count (%d "
1546 : "whereas %d expected)",
1547 : drvName, poTmpDS->GetRasterXSize(),
1548 : poTmpDS->GetRasterYSize(), nBlockXSize,
1549 0 : m_image->isTiled() ? nBlockYSize
1550 : : nBlockActualYSize,
1551 : poTmpDS->GetRasterCount(), nComponentsPerPixel);
1552 0 : return false;
1553 : }
1554 : GDALRasterIOExtraArg sExtraArg;
1555 510 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1556 1530 : if (poTmpDS->RasterIO(
1557 : GF_Read, 0, 0, poTmpDS->GetRasterXSize(),
1558 510 : poTmpDS->GetRasterYSize(), abyDecompressedStrile.data(),
1559 : poTmpDS->GetRasterXSize(), poTmpDS->GetRasterYSize(),
1560 : eNativeDT, poTmpDS->GetRasterCount(), nullptr,
1561 510 : nNativeDTSize * nComponentsPerPixel,
1562 510 : nNativeDTSize * nComponentsPerPixel * nBlockXSize,
1563 510 : nNativeDTSize, &sExtraArg) != CE_None)
1564 : {
1565 0 : CPLError(CE_Failure, CPLE_AppDefined,
1566 : "Decompression failed");
1567 0 : return false;
1568 : }
1569 : }
1570 : else
1571 : {
1572 5321 : CPLAssert(m_decompressor);
1573 5321 : void *output_data = abyDecompressedStrile.data();
1574 5324 : size_t output_size = nActualUncompressedSize;
1575 5319 : if (!m_decompressor->pfnFunc(
1576 5324 : abyCompressedStrile.data(), size, &output_data,
1577 16046 : &output_size, nullptr, m_decompressor->user_data) ||
1578 5362 : output_size != nActualUncompressedSize)
1579 : {
1580 2 : CPLError(CE_Failure, CPLE_AppDefined,
1581 : "Decompression failed");
1582 2 : return false;
1583 : }
1584 5358 : CPLAssert(output_data == abyDecompressedStrile.data());
1585 : }
1586 : }
1587 : else
1588 : {
1589 818 : if (size != nActualUncompressedSize)
1590 : {
1591 17 : CPLError(CE_Failure, CPLE_NotSupported,
1592 : "Strile size != expected size");
1593 34 : return false;
1594 : }
1595 :
1596 801 : bool ok = true;
1597 1602 : m_image->readContext()->read(offset, size,
1598 801 : abyDecompressedStrile.data(), ok);
1599 801 : if (!ok)
1600 : {
1601 17 : CPLError(CE_Failure, CPLE_FileIO,
1602 : "Cannot read strile from disk");
1603 17 : return false;
1604 : }
1605 : }
1606 :
1607 7760 : if (m_image->bitsPerSample() == 1)
1608 : {
1609 352 : const GByte *CPL_RESTRICT pabySrc = abyDecompressedStrile.data();
1610 352 : GByte *CPL_RESTRICT pabyDst = bufferForOneBitExpansion.data();
1611 1460 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1612 : {
1613 1108 : if (m_bExpand1To255)
1614 : {
1615 662 : GDALExpandPackedBitsToByteAt0Or255(pabySrc, pabyDst,
1616 : nBlockXSize);
1617 : }
1618 : else
1619 : {
1620 446 : GDALExpandPackedBitsToByteAt0Or1(pabySrc, pabyDst,
1621 : nBlockXSize);
1622 : }
1623 1108 : pabySrc += (nBlockXSize + 7) / 8;
1624 1108 : pabyDst += nBlockXSize;
1625 : }
1626 :
1627 352 : std::swap(abyDecompressedStrile, bufferForOneBitExpansion);
1628 : }
1629 14379 : else if (m_image->compression() == LIBERTIFF_NS::Compression::None ||
1630 14381 : m_image->compression() == LIBERTIFF_NS::Compression::LZW ||
1631 6351 : m_decompressor)
1632 : {
1633 6450 : if (m_image->readContext()->mustByteSwap() &&
1634 51 : m_image->predictor() != 3)
1635 : {
1636 50 : if (GDALDataTypeIsComplex(eNativeDT))
1637 : {
1638 1 : GDALSwapWordsEx(abyDecompressedStrile.data(),
1639 : static_cast<int>(nNativeDTSize) / 2,
1640 1 : nActualPixelCount * nComponentsPerPixel * 2,
1641 : static_cast<int>(nNativeDTSize) / 2);
1642 : }
1643 : else
1644 : {
1645 49 : GDALSwapWordsEx(abyDecompressedStrile.data(),
1646 : static_cast<int>(nNativeDTSize),
1647 49 : nActualPixelCount * nComponentsPerPixel,
1648 : static_cast<int>(nNativeDTSize));
1649 : }
1650 : }
1651 :
1652 6396 : if (m_image->predictor() == 2)
1653 : {
1654 4893 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1655 : {
1656 : auto ptr =
1657 4442 : abyDecompressedStrile.data() +
1658 4442 : nNativeDTSize * iY * nBlockXSize * nComponentsPerPixel;
1659 4442 : if (nNativeDTSize == sizeof(uint8_t))
1660 : {
1661 1192 : HorizPredictorDecode<uint8_t>(ptr, nBlockXSize,
1662 : nComponentsPerPixel);
1663 : }
1664 3250 : else if (nNativeDTSize == sizeof(uint16_t))
1665 : {
1666 1370 : HorizPredictorDecode<uint16_t>(ptr, nBlockXSize,
1667 : nComponentsPerPixel);
1668 : }
1669 1880 : else if (nNativeDTSize == sizeof(uint32_t))
1670 : {
1671 940 : HorizPredictorDecode<uint32_t>(ptr, nBlockXSize,
1672 : nComponentsPerPixel);
1673 : }
1674 940 : else if (nNativeDTSize == sizeof(uint64_t))
1675 : {
1676 940 : HorizPredictorDecode<uint64_t>(ptr, nBlockXSize,
1677 : nComponentsPerPixel);
1678 : }
1679 : else
1680 : {
1681 0 : CPLAssert(false);
1682 : }
1683 : }
1684 : }
1685 5954 : else if (m_image->predictor() == 3)
1686 : {
1687 63 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1688 : {
1689 : auto ptr =
1690 60 : abyDecompressedStrile.data() +
1691 60 : nNativeDTSize * iY * nBlockXSize * nComponentsPerPixel;
1692 60 : bool ok = true;
1693 60 : if (nNativeDTSize == sizeof(uint32_t))
1694 : {
1695 40 : ok = FloatingPointHorizPredictorDecode<uint32_t>(
1696 : tlsState
1697 40 : .m_floatingPointHorizPredictorDecodeTmpBuffer,
1698 : ptr, nBlockXSize, nComponentsPerPixel);
1699 : }
1700 20 : else if (nNativeDTSize == sizeof(uint64_t))
1701 : {
1702 20 : ok = FloatingPointHorizPredictorDecode<uint64_t>(
1703 : tlsState
1704 20 : .m_floatingPointHorizPredictorDecodeTmpBuffer,
1705 : ptr, nBlockXSize, nComponentsPerPixel);
1706 : }
1707 : else
1708 : {
1709 0 : CPLAssert(false);
1710 : }
1711 60 : if (!ok)
1712 0 : return false;
1713 : }
1714 : }
1715 : }
1716 : }
1717 :
1718 : // Copy decompress strile into user buffer
1719 7870 : if (pabyBlockData)
1720 : {
1721 598 : const auto IsContiguousBandMap = [nBandCount, panBandMap]()
1722 : {
1723 286 : for (int i = 0; i < nBandCount; ++i)
1724 : {
1725 234 : if (panBandMap[i] != i + 1)
1726 26 : return false;
1727 : }
1728 52 : return true;
1729 7293 : };
1730 :
1731 7293 : const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1732 1632 : if (!bSeparate && nBands > 1 && nBands == nBandCount &&
1733 8932 : nBufTypeSize == nPixelSpace && IsContiguousBandMap())
1734 : {
1735 : // Optimization: reading a pixel-interleaved buffer into a band-interleaved buffer
1736 26 : std::vector<void *> &apabyDest = tlsState.m_apabyDest;
1737 26 : apabyDest.resize(nBands);
1738 130 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1739 : {
1740 104 : apabyDest[iBand] = pabyBlockData + iBand * nBandSpace;
1741 : }
1742 326 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1743 : {
1744 300 : if (iY > 0)
1745 : {
1746 1370 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1747 : {
1748 1096 : apabyDest[iBand] =
1749 1096 : static_cast<GByte *>(apabyDest[iBand]) + nLineSpace;
1750 : }
1751 : }
1752 300 : GDALDeinterleave(abyDecompressedStrile.data() +
1753 300 : nNativeDTSize * iY * nBlockXSize * nBands,
1754 300 : eNativeDT, nBands, apabyDest.data(), eBufType,
1755 : nBlockActualXSize);
1756 : }
1757 : }
1758 1606 : else if (!bSeparate && nBands == nBandCount &&
1759 153 : nBufTypeSize == nBandSpace &&
1760 8906 : nPixelSpace == nBandSpace * nBandCount &&
1761 26 : IsContiguousBandMap())
1762 : {
1763 : // Optimization reading a pixel-interleaved buffer into a pixel-interleaved buffer
1764 326 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1765 : {
1766 600 : GDALCopyWords64(
1767 600 : abyDecompressedStrile.data() +
1768 300 : nNativeDTSize * iY * nBlockXSize * nBands,
1769 : eNativeDT, static_cast<int>(nNativeDTSize),
1770 300 : pabyBlockData + iY * nLineSpace, eBufType, nBufTypeSize,
1771 : static_cast<GPtrDiff_t>(
1772 300 : static_cast<GIntBig>(nBlockActualXSize) * nBands));
1773 : }
1774 : }
1775 : else
1776 : {
1777 : // General case
1778 7248 : const int nSrcPixels = bSeparate ? 1 : nBands;
1779 14633 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1780 : {
1781 7380 : const int iSrcBand = bSeparate ? 0 : panBandMap[iBand] - 1;
1782 187232 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1783 : {
1784 359168 : GDALCopyWords64(
1785 359168 : abyDecompressedStrile.data() +
1786 179321 : nNativeDTSize *
1787 179321 : (iY * nBlockXSize * nSrcPixels + iSrcBand),
1788 179847 : eNativeDT, static_cast<int>(nSrcPixels * nNativeDTSize),
1789 179847 : pabyBlockData + iBand * nBandSpace + iY * nLineSpace,
1790 : eBufType, nBufTypeSize, nBlockActualXSize);
1791 : }
1792 : }
1793 : }
1794 : }
1795 :
1796 7882 : tlsState.m_curStrileIdx = curStrileIdx;
1797 7882 : tlsState.m_curStrileMissing = false;
1798 :
1799 7882 : return true;
1800 : }
1801 :
1802 : /************************************************************************/
1803 : /* Identify() */
1804 : /************************************************************************/
1805 :
1806 65553 : /* static */ int LIBERTIFFDataset::Identify(GDALOpenInfo *poOpenInfo)
1807 : {
1808 130404 : return poOpenInfo->eAccess != GA_Update &&
1809 64851 : (STARTS_WITH_CI(poOpenInfo->pszFilename, "GTIFF_DIR:") ||
1810 64825 : (poOpenInfo->fpL && poOpenInfo->nHeaderBytes >= 8 &&
1811 10639 : (((poOpenInfo->pabyHeader[0] == 'I' &&
1812 2363 : poOpenInfo->pabyHeader[1] == 'I') &&
1813 2339 : ((poOpenInfo->pabyHeader[2] == 0x2A &&
1814 2302 : poOpenInfo->pabyHeader[3] == 0) ||
1815 41 : (poOpenInfo->pabyHeader[2] == 0x2B &&
1816 32 : poOpenInfo->pabyHeader[3] == 0))) ||
1817 8309 : ((poOpenInfo->pabyHeader[0] == 'M' &&
1818 116 : poOpenInfo->pabyHeader[1] == 'M') &&
1819 70 : ((poOpenInfo->pabyHeader[2] == 0 &&
1820 70 : poOpenInfo->pabyHeader[3] == 0x2A) ||
1821 26 : (poOpenInfo->pabyHeader[2] == 0 &&
1822 65579 : poOpenInfo->pabyHeader[3] == 0x2B))))));
1823 : }
1824 :
1825 : /************************************************************************/
1826 : /* ComputeGDALDataType() */
1827 : /************************************************************************/
1828 :
1829 1011 : GDALDataType LIBERTIFFDataset::ComputeGDALDataType() const
1830 : {
1831 :
1832 1011 : GDALDataType eDT = GDT_Unknown;
1833 :
1834 1011 : switch (m_image->sampleFormat())
1835 : {
1836 937 : case LIBERTIFF_NS::SampleFormat::UnsignedInt:
1837 : {
1838 1421 : if (m_image->bitsPerSample() == 1 &&
1839 484 : (m_image->samplesPerPixel() == 1 ||
1840 10 : m_image->planarConfiguration() ==
1841 : LIBERTIFF_NS::PlanarConfiguration::Separate))
1842 : {
1843 464 : eDT = GDT_Byte;
1844 : }
1845 473 : else if (m_image->bitsPerSample() == 8)
1846 392 : eDT = GDT_Byte;
1847 81 : else if (m_image->bitsPerSample() == 16)
1848 10 : eDT = GDT_UInt16;
1849 71 : else if (m_image->bitsPerSample() == 32)
1850 7 : eDT = GDT_UInt32;
1851 64 : else if (m_image->bitsPerSample() == 64)
1852 7 : eDT = GDT_UInt64;
1853 937 : break;
1854 : }
1855 :
1856 12 : case LIBERTIFF_NS::SampleFormat::SignedInt:
1857 : {
1858 12 : if (m_image->bitsPerSample() == 8)
1859 1 : eDT = GDT_Int8;
1860 11 : else if (m_image->bitsPerSample() == 16)
1861 6 : eDT = GDT_Int16;
1862 5 : else if (m_image->bitsPerSample() == 32)
1863 2 : eDT = GDT_Int32;
1864 3 : else if (m_image->bitsPerSample() == 64)
1865 2 : eDT = GDT_Int64;
1866 12 : break;
1867 : }
1868 :
1869 26 : case LIBERTIFF_NS::SampleFormat::IEEEFP:
1870 : {
1871 26 : if (m_image->bitsPerSample() == 32)
1872 18 : eDT = GDT_Float32;
1873 8 : else if (m_image->bitsPerSample() == 64)
1874 6 : eDT = GDT_Float64;
1875 26 : break;
1876 : }
1877 :
1878 7 : case LIBERTIFF_NS::SampleFormat::ComplexInt:
1879 : {
1880 7 : if (m_image->bitsPerSample() == 32)
1881 3 : eDT = GDT_CInt16;
1882 4 : else if (m_image->bitsPerSample() == 64)
1883 4 : eDT = GDT_CInt32;
1884 7 : break;
1885 : }
1886 :
1887 6 : case LIBERTIFF_NS::SampleFormat::ComplexIEEEFP:
1888 : {
1889 6 : if (m_image->bitsPerSample() == 64)
1890 4 : eDT = GDT_CFloat32;
1891 2 : else if (m_image->bitsPerSample() == 128)
1892 2 : eDT = GDT_CFloat64;
1893 6 : break;
1894 : }
1895 :
1896 23 : default:
1897 23 : break;
1898 : }
1899 :
1900 1015 : if (m_image->bitsPerSample() == 12 &&
1901 4 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG)
1902 : {
1903 2 : auto poJPEGDrv = GetGDALDriverManager()->GetDriverByName("JPEG");
1904 2 : if (poJPEGDrv)
1905 : {
1906 : const char *pszJPEGDataTypes =
1907 2 : poJPEGDrv->GetMetadataItem(GDAL_DMD_CREATIONDATATYPES);
1908 2 : if (pszJPEGDataTypes && strstr(pszJPEGDataTypes, "UInt16"))
1909 2 : eDT = GDT_UInt16;
1910 : }
1911 : }
1912 :
1913 1011 : return eDT;
1914 : }
1915 :
1916 : /************************************************************************/
1917 : /* ProcessCompressionMethod() */
1918 : /************************************************************************/
1919 :
1920 1067 : bool LIBERTIFFDataset::ProcessCompressionMethod()
1921 : {
1922 1067 : if (m_image->compression() == LIBERTIFF_NS::Compression::PackBits)
1923 : {
1924 7 : GDALDataset::SetMetadataItem("COMPRESSION", "PACKBITS",
1925 : "IMAGE_STRUCTURE");
1926 : }
1927 2092 : else if (m_image->compression() == LIBERTIFF_NS::Compression::Deflate ||
1928 1032 : m_image->compression() == LIBERTIFF_NS::Compression::LegacyDeflate)
1929 : {
1930 43 : m_decompressor = CPLGetDecompressor("zlib");
1931 43 : GDALDataset::SetMetadataItem("COMPRESSION", "DEFLATE",
1932 : "IMAGE_STRUCTURE");
1933 : }
1934 1017 : else if (m_image->compression() == LIBERTIFF_NS::Compression::ZSTD)
1935 : {
1936 17 : m_decompressor = CPLGetDecompressor("zstd");
1937 17 : if (!m_decompressor)
1938 : {
1939 0 : ReportError(CE_Failure, CPLE_NotSupported,
1940 : "Compression = ZSTD unhandled because GDAL "
1941 : "has not been built against libzstd");
1942 0 : return false;
1943 : }
1944 17 : GDALDataset::SetMetadataItem("COMPRESSION", "ZSTD", "IMAGE_STRUCTURE");
1945 : }
1946 1000 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LZMA)
1947 : {
1948 14 : m_decompressor = CPLGetDecompressor("lzma");
1949 14 : if (!m_decompressor)
1950 : {
1951 0 : ReportError(CE_Failure, CPLE_NotSupported,
1952 : "Compression = LZMA unhandled because GDAL "
1953 : "has not been built against liblzma");
1954 0 : return false;
1955 : }
1956 14 : GDALDataset::SetMetadataItem("COMPRESSION", "LZMA", "IMAGE_STRUCTURE");
1957 : }
1958 986 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LZW)
1959 : {
1960 71 : GDALDataset::SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
1961 : }
1962 915 : else if (m_image->compression() == LIBERTIFF_NS::Compression::JPEG)
1963 : {
1964 40 : if (!GDALGetDriverByName("JPEG"))
1965 : {
1966 0 : ReportError(
1967 : CE_Failure, CPLE_NotSupported,
1968 : "Compression = JPEG not supported because JPEG driver missing");
1969 0 : return false;
1970 : }
1971 40 : if (m_image->photometricInterpretation() ==
1972 49 : LIBERTIFF_NS::PhotometricInterpretation::YCbCr &&
1973 9 : m_image->samplesPerPixel() == 3)
1974 : {
1975 9 : GDALDataset::SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
1976 : "IMAGE_STRUCTURE");
1977 9 : GDALDataset::SetMetadataItem("COMPRESSION", "YCbCr JPEG",
1978 : "IMAGE_STRUCTURE");
1979 : }
1980 : else
1981 : {
1982 31 : GDALDataset::SetMetadataItem("COMPRESSION", "JPEG",
1983 : "IMAGE_STRUCTURE");
1984 : }
1985 64 : if (m_image->samplesPerPixel() != 1 &&
1986 27 : m_image->samplesPerPixel() != 3 &&
1987 67 : m_image->samplesPerPixel() != 4 &&
1988 0 : m_image->planarConfiguration() ==
1989 : LIBERTIFF_NS::PlanarConfiguration::Contiguous)
1990 : {
1991 0 : ReportError(CE_Failure, CPLE_NotSupported,
1992 : "Compression = JPEG not supported when samplesPerPixel "
1993 : "!= 1, 3 or 4 and planarConfiguration = Contiguous");
1994 0 : return false;
1995 : }
1996 :
1997 : const auto psJPEGTablesTag =
1998 40 : m_image->tag(LIBERTIFF_NS::TagCode::JPEGTables);
1999 40 : if (psJPEGTablesTag &&
2000 37 : psJPEGTablesTag->type == LIBERTIFF_NS::TagType::Undefined &&
2001 37 : psJPEGTablesTag->count > 4 &&
2002 37 : !psJPEGTablesTag->invalid_value_offset &&
2003 37 : psJPEGTablesTag->count < 65536)
2004 : {
2005 37 : bool ok = true;
2006 : m_jpegTablesOri =
2007 37 : m_image->readTagAsVector<uint8_t>(*psJPEGTablesTag, ok);
2008 74 : if (m_jpegTablesOri.size() >= 4 && m_jpegTablesOri[0] == 0xff &&
2009 37 : m_jpegTablesOri[1] == 0xd8 &&
2010 111 : m_jpegTablesOri[m_jpegTablesOri.size() - 2] == 0xff &&
2011 37 : m_jpegTablesOri.back() == 0xd9)
2012 : {
2013 : m_jpegTables.insert(
2014 0 : m_jpegTables.end(), m_jpegTablesOri.data() + 2,
2015 37 : m_jpegTablesOri.data() + m_jpegTablesOri.size() - 2);
2016 : }
2017 : }
2018 :
2019 43 : if (m_image->samplesPerPixel() == 4 &&
2020 3 : m_image->planarConfiguration() ==
2021 : LIBERTIFF_NS::PlanarConfiguration::Contiguous)
2022 : {
2023 2 : const GByte abyAdobeAPP14RGB[] = {
2024 : 0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62,
2025 : 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00};
2026 0 : m_jpegTables.insert(m_jpegTables.end(), abyAdobeAPP14RGB,
2027 2 : abyAdobeAPP14RGB + sizeof(abyAdobeAPP14RGB));
2028 : }
2029 : }
2030 875 : else if (m_image->compression() == LIBERTIFF_NS::Compression::WEBP)
2031 : {
2032 9 : if (!GDALGetDriverByName("WEBP"))
2033 : {
2034 0 : ReportError(
2035 : CE_Failure, CPLE_NotSupported,
2036 : "Compression = WEBP not supported because WEBP driver missing");
2037 0 : return false;
2038 : }
2039 9 : GDALDataset::SetMetadataItem("COMPRESSION", "WEBP", "IMAGE_STRUCTURE");
2040 : }
2041 1718 : else if (m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
2042 852 : m_image->compression() == LIBERTIFF_NS::Compression::JXL_DNG_1_7)
2043 : {
2044 14 : if (!GDALGetDriverByName("JPEGXL"))
2045 : {
2046 0 : ReportError(
2047 : CE_Failure, CPLE_NotSupported,
2048 : "Compression = JXL not supported because JXL driver missing");
2049 0 : return false;
2050 : }
2051 14 : GDALDataset::SetMetadataItem("COMPRESSION", "JXL", "IMAGE_STRUCTURE");
2052 : }
2053 852 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LERC)
2054 : {
2055 : #ifndef LERC_SUPPORT
2056 : ReportError(CE_Failure, CPLE_NotSupported,
2057 : "Compression = LERC not supported because GDAL "
2058 : "has not been built against liblerc");
2059 : return false;
2060 : #else
2061 : const auto *psLercParametersTag =
2062 43 : m_image->tag(LIBERTIFF_NS::TagCode::LERCParameters);
2063 43 : if (psLercParametersTag &&
2064 43 : psLercParametersTag->type == LIBERTIFF_NS::TagType::Long &&
2065 43 : psLercParametersTag->count == 2)
2066 : {
2067 43 : bool ok = true;
2068 : const auto lercParameters =
2069 43 : m_image->readTagAsVector<uint32_t>(*psLercParametersTag, ok);
2070 43 : if (!ok || lercParameters.size() != 2)
2071 : {
2072 0 : ReportError(CE_Failure, CPLE_NotSupported,
2073 : "Tag LERCParameters is invalid");
2074 0 : return false;
2075 : }
2076 43 : m_lercVersion = lercParameters[0];
2077 43 : m_lercAdditionalCompression = lercParameters[1];
2078 : #ifndef ZSTD_SUPPORT
2079 : if (m_lercAdditionalCompression == LERC_ADD_COMPRESSION_ZSTD)
2080 : {
2081 : ReportError(
2082 : CE_Failure, CPLE_NotSupported,
2083 : "Compression = LERC_ZSTD not supported because GDAL "
2084 : "has not been built against libzstd");
2085 : return false;
2086 : }
2087 : #endif
2088 : }
2089 :
2090 43 : GDALDataset::SetMetadataItem(
2091 : "COMPRESSION",
2092 43 : m_lercAdditionalCompression == LERC_ADD_COMPRESSION_DEFLATE
2093 : ? "LERC_DEFLATE"
2094 29 : : m_lercAdditionalCompression == LERC_ADD_COMPRESSION_ZSTD
2095 29 : ? "LERC_ZSTD"
2096 : : "LERC",
2097 : "IMAGE_STRUCTURE");
2098 :
2099 43 : if (m_lercVersion == LERC_VERSION_2_4)
2100 : {
2101 43 : GDALDataset::SetMetadataItem("LERC_VERSION", "2.4",
2102 : "IMAGE_STRUCTURE");
2103 : }
2104 : #endif
2105 : }
2106 809 : else if (m_image->compression() != LIBERTIFF_NS::Compression::None)
2107 : {
2108 18 : CPLDebug("LIBERTIFF", "Compression = %s unhandled",
2109 : LIBERTIFF_NS::compressionName(m_image->compression()));
2110 18 : return false;
2111 : }
2112 :
2113 1049 : return true;
2114 : }
2115 :
2116 : /************************************************************************/
2117 : /* Open() */
2118 : /************************************************************************/
2119 :
2120 1188 : bool LIBERTIFFDataset::Open(std::unique_ptr<const LIBERTIFF_NS::Image> image)
2121 : {
2122 1188 : m_image = std::move(image);
2123 :
2124 : // Basic sanity checks
2125 1188 : if (m_image->width() == 0 ||
2126 2303 : m_image->width() > static_cast<uint32_t>(INT_MAX) ||
2127 1151 : m_image->height() == 0 ||
2128 2232 : m_image->height() > static_cast<uint32_t>(INT_MAX) ||
2129 3456 : m_image->samplesPerPixel() == 0 ||
2130 1067 : m_image->samplesPerPixel() > static_cast<uint32_t>(INT_MAX))
2131 : {
2132 121 : CPLDebug("LIBERTIFF", "Invalid width, height, or samplesPerPixel");
2133 121 : return false;
2134 : }
2135 :
2136 1067 : nRasterXSize = static_cast<int>(m_image->width());
2137 1067 : nRasterYSize = static_cast<int>(m_image->height());
2138 1067 : const int l_nBands = static_cast<int>(m_image->samplesPerPixel());
2139 1067 : if (!GDALCheckBandCount(l_nBands, false))
2140 0 : return false;
2141 :
2142 1067 : if (!ProcessCompressionMethod())
2143 18 : return false;
2144 :
2145 : // Compute block size
2146 : int nBlockXSize;
2147 : int nBlockYSize;
2148 1049 : if (m_image->isTiled())
2149 : {
2150 120 : if (m_image->tileWidth() == 0 ||
2151 238 : m_image->tileWidth() > static_cast<uint32_t>(INT_MAX) ||
2152 358 : m_image->tileHeight() == 0 ||
2153 118 : m_image->tileHeight() > static_cast<uint32_t>(INT_MAX))
2154 : {
2155 2 : CPLDebug("LIBERTIFF", "Invalid tileWidth or tileHeight");
2156 2 : return false;
2157 : }
2158 118 : nBlockXSize = static_cast<int>(m_image->tileWidth());
2159 118 : nBlockYSize = static_cast<int>(m_image->tileHeight());
2160 : }
2161 : else
2162 : {
2163 929 : if (m_image->rowsPerStripSanitized() == 0)
2164 : {
2165 36 : CPLDebug("LIBERTIFF", "Invalid rowsPerStrip");
2166 36 : return false;
2167 : }
2168 893 : nBlockXSize = nRasterXSize;
2169 893 : nBlockYSize = static_cast<int>(m_image->rowsPerStripSanitized());
2170 : }
2171 :
2172 1011 : const GDALDataType eDT = ComputeGDALDataType();
2173 1011 : if (eDT == GDT_Unknown)
2174 : {
2175 81 : CPLDebug("LIBERTIFF",
2176 : "BitsPerSample = %u and SampleFormat=%u unhandled",
2177 : m_image->bitsPerSample(), m_image->sampleFormat());
2178 81 : return false;
2179 : }
2180 :
2181 : // Deal with Predictor tag
2182 930 : if (m_image->predictor() == 2)
2183 : {
2184 33 : GDALDataset::SetMetadataItem("PREDICTOR", "2", "IMAGE_STRUCTURE");
2185 : }
2186 897 : else if (m_image->predictor() == 3)
2187 : {
2188 3 : if (eDT != GDT_Float32 && eDT != GDT_Float64)
2189 : {
2190 0 : CPLDebug("LIBERTIFF", "Unhandled predictor=3 with non-float data");
2191 0 : return false;
2192 : }
2193 3 : GDALDataset::SetMetadataItem("PREDICTOR", "3", "IMAGE_STRUCTURE");
2194 : }
2195 894 : else if (m_image->predictor() > 3)
2196 : {
2197 1 : CPLDebug("LIBERTIFF", "Predictor = %u unhandled", m_image->predictor());
2198 1 : return false;
2199 : }
2200 :
2201 : // Deal with PlanarConfiguration tag
2202 929 : if (m_image->planarConfiguration() ==
2203 1779 : LIBERTIFF_NS::PlanarConfiguration::Separate ||
2204 850 : m_image->samplesPerPixel() == 1)
2205 780 : GDALDataset::SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
2206 : else
2207 149 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2208 :
2209 929 : const int nNativeDTSize = GDALGetDataTypeSizeBytes(eDT);
2210 929 : const bool bSeparate = m_image->planarConfiguration() ==
2211 929 : LIBERTIFF_NS::PlanarConfiguration::Separate;
2212 : // Sanity check that a strile can its on SIZE_MAX, to avoid further
2213 : // issues in ReadBlock()
2214 929 : if (static_cast<uint64_t>(nNativeDTSize) * (bSeparate ? 1 : l_nBands) >
2215 929 : std::numeric_limits<size_t>::max() /
2216 929 : (static_cast<uint64_t>(nBlockXSize) * nBlockYSize))
2217 : {
2218 0 : CPLDebug("LIBERTIFF", "Too large block");
2219 0 : return false;
2220 : }
2221 :
2222 : // Process GDAL_NODATA tag
2223 929 : bool bHasNoData = false;
2224 929 : double dfNoData = 0;
2225 929 : const auto *tagNoData = m_image->tag(LIBERTIFF_NS::TagCode::GDAL_NODATA);
2226 929 : if (tagNoData && tagNoData->type == LIBERTIFF_NS::TagType::ASCII &&
2227 26 : !(tagNoData->count > 4 && tagNoData->invalid_value_offset) &&
2228 26 : tagNoData->count < 256)
2229 : {
2230 26 : bool ok = true;
2231 52 : const std::string noData = m_image->readTagAsString(*tagNoData, ok);
2232 26 : if (ok && !noData.empty())
2233 : {
2234 25 : bHasNoData = true;
2235 25 : dfNoData = CPLAtof(noData.c_str());
2236 : }
2237 : }
2238 :
2239 : // Process ExtraSamples tag
2240 929 : int nRegularChannels = 0;
2241 929 : if (m_image->photometricInterpretation() ==
2242 : LIBERTIFF_NS::PhotometricInterpretation::MinIsBlack)
2243 : {
2244 269 : nRegularChannels = 1;
2245 : }
2246 660 : else if (m_image->photometricInterpretation() ==
2247 : LIBERTIFF_NS::PhotometricInterpretation::RGB)
2248 : {
2249 194 : nRegularChannels = 3;
2250 : }
2251 : const auto *psExtraSamplesTag =
2252 929 : m_image->tag(LIBERTIFF_NS::TagCode::ExtraSamples);
2253 929 : if (nRegularChannels > 0 && l_nBands > nRegularChannels &&
2254 22 : psExtraSamplesTag &&
2255 22 : psExtraSamplesTag->type == LIBERTIFF_NS::TagType::Short &&
2256 22 : psExtraSamplesTag->count ==
2257 22 : static_cast<unsigned>(l_nBands - nRegularChannels))
2258 : {
2259 21 : bool ok = true;
2260 : m_extraSamples =
2261 21 : m_image->readTagAsVector<uint16_t>(*psExtraSamplesTag, ok);
2262 : }
2263 :
2264 : // Preload TileOffsets and TileByteCounts if not too big
2265 929 : if (m_image->isTiled())
2266 : {
2267 : const auto *psTileOffsets =
2268 118 : m_image->tag(LIBERTIFF_NS::TagCode::TileOffsets);
2269 : const auto *psTileByteCounts =
2270 118 : m_image->tag(LIBERTIFF_NS::TagCode::TileByteCounts);
2271 118 : if (psTileOffsets &&
2272 118 : (psTileOffsets->type == LIBERTIFF_NS::TagType::Long ||
2273 6 : psTileOffsets->type == LIBERTIFF_NS::TagType::Long8) &&
2274 116 : !psTileOffsets->invalid_value_offset &&
2275 94 : psTileOffsets->count <= 4096 && psTileByteCounts &&
2276 94 : psTileByteCounts->type == LIBERTIFF_NS::TagType::Long &&
2277 15 : !psTileByteCounts->invalid_value_offset &&
2278 15 : psTileByteCounts->count <= 4096)
2279 : {
2280 15 : bool ok = true;
2281 15 : if (psTileOffsets->type == LIBERTIFF_NS::TagType::Long)
2282 : m_tileOffsets =
2283 15 : m_image->readTagAsVector<uint32_t>(*psTileOffsets, ok);
2284 : else
2285 : m_tileOffsets64 =
2286 0 : m_image->readTagAsVector<uint64_t>(*psTileOffsets, ok);
2287 : m_tileByteCounts =
2288 15 : m_image->readTagAsVector<uint32_t>(*psTileByteCounts, ok);
2289 15 : if (!ok)
2290 : {
2291 0 : m_tileOffsets.clear();
2292 0 : m_tileOffsets64.clear();
2293 0 : m_tileByteCounts.clear();
2294 : }
2295 : }
2296 : }
2297 :
2298 : // Create raster bands
2299 67853 : for (int i = 0; i < l_nBands; ++i)
2300 : {
2301 0 : auto poBand = std::make_unique<LIBERTIFFBand>(this, i + 1, eDT,
2302 66924 : nBlockXSize, nBlockYSize);
2303 66924 : poBand->m_bHasNoData = bHasNoData;
2304 66924 : poBand->m_dfNoData = dfNoData;
2305 66924 : if (m_image->photometricInterpretation() ==
2306 : LIBERTIFF_NS::PhotometricInterpretation::MinIsBlack)
2307 : {
2308 65818 : if (i == 0)
2309 269 : poBand->m_eColorInterp = GCI_GrayIndex;
2310 : }
2311 1106 : else if (m_image->photometricInterpretation() ==
2312 1676 : LIBERTIFF_NS::PhotometricInterpretation::RGB ||
2313 510 : (m_image->photometricInterpretation() ==
2314 60 : LIBERTIFF_NS::PhotometricInterpretation::YCbCr &&
2315 60 : m_image->samplesPerPixel() == 3))
2316 : {
2317 656 : if (i < 3)
2318 642 : poBand->m_eColorInterp =
2319 642 : static_cast<GDALColorInterp>(GCI_RedBand + i);
2320 : }
2321 66924 : if (i >= nRegularChannels && !m_extraSamples.empty())
2322 : {
2323 25 : if (m_extraSamples[i - nRegularChannels] ==
2324 : LIBERTIFF_NS::ExtraSamples::UnAssociatedAlpha)
2325 : {
2326 13 : poBand->m_eColorInterp = GCI_AlphaBand;
2327 13 : if (!m_poAlphaBand)
2328 13 : m_poAlphaBand = poBand.get();
2329 : }
2330 12 : else if (m_extraSamples[i - nRegularChannels] ==
2331 : LIBERTIFF_NS::ExtraSamples::AssociatedAlpha)
2332 : {
2333 2 : poBand->m_eColorInterp = GCI_AlphaBand;
2334 2 : poBand->GDALRasterBand::SetMetadataItem(
2335 : "ALPHA", "PREMULTIPLIED", "IMAGE_STRUCTURE");
2336 2 : if (!m_poAlphaBand)
2337 2 : m_poAlphaBand = poBand.get();
2338 : }
2339 : }
2340 :
2341 68049 : if (m_image->bitsPerSample() != 8 && m_image->bitsPerSample() != 16 &&
2342 68521 : m_image->bitsPerSample() != 32 && m_image->bitsPerSample() != 64 &&
2343 472 : m_image->bitsPerSample() != 128)
2344 : {
2345 470 : poBand->GDALRasterBand::SetMetadataItem(
2346 : "NBITS", CPLSPrintf("%u", m_image->bitsPerSample()),
2347 : "IMAGE_STRUCTURE");
2348 : }
2349 :
2350 66924 : if (l_nBands == 1 && eDT == GDT_Byte)
2351 : {
2352 650 : poBand->ReadColorMap();
2353 : }
2354 :
2355 66924 : if (m_image->photometricInterpretation() ==
2356 : LIBERTIFF_NS::PhotometricInterpretation::MinIsWhite)
2357 : {
2358 420 : GDALDataset::SetMetadataItem("MINISWHITE", "YES",
2359 : "IMAGE_STRUCTURE");
2360 : }
2361 :
2362 66924 : if (m_image->bitsPerSample() == 1 && !poBand->m_poColorTable)
2363 : {
2364 462 : poBand->m_poColorTable = std::make_unique<GDALColorTable>();
2365 462 : const GDALColorEntry oEntryBlack = {0, 0, 0, 255};
2366 462 : const GDALColorEntry oEntryWhite = {255, 255, 255, 255};
2367 462 : if (m_image->photometricInterpretation() ==
2368 : LIBERTIFF_NS::PhotometricInterpretation::MinIsWhite)
2369 : {
2370 419 : poBand->m_poColorTable->SetColorEntry(0, &oEntryWhite);
2371 419 : poBand->m_poColorTable->SetColorEntry(1, &oEntryBlack);
2372 : }
2373 : else
2374 : {
2375 43 : poBand->m_poColorTable->SetColorEntry(0, &oEntryBlack);
2376 43 : poBand->m_poColorTable->SetColorEntry(1, &oEntryWhite);
2377 : }
2378 462 : poBand->m_eColorInterp = GCI_PaletteIndex;
2379 : }
2380 :
2381 66924 : SetBand(i + 1, std::move(poBand));
2382 : }
2383 :
2384 929 : nOpenFlags = GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE;
2385 :
2386 929 : return true;
2387 : }
2388 :
2389 : /************************************************************************/
2390 : /* Open() */
2391 : /************************************************************************/
2392 :
2393 1213 : bool LIBERTIFFDataset::Open(GDALOpenInfo *poOpenInfo)
2394 : {
2395 1213 : SetDescription(poOpenInfo->pszFilename);
2396 :
2397 1213 : int iSelectedSubDS = -1;
2398 1213 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GTIFF_DIR:"))
2399 : {
2400 13 : iSelectedSubDS = atoi(poOpenInfo->pszFilename + strlen("GTIFF_DIR:"));
2401 13 : if (iSelectedSubDS <= 0)
2402 : {
2403 9 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset syntax");
2404 9 : return false;
2405 : }
2406 : const char *pszNextColon =
2407 4 : strchr(poOpenInfo->pszFilename + strlen("GTIFF_DIR:"), ':');
2408 4 : if (!pszNextColon)
2409 : {
2410 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset syntax");
2411 0 : return false;
2412 : }
2413 4 : m_poFile.reset(VSIFOpenL(pszNextColon + 1, "rb"));
2414 4 : if (!m_poFile)
2415 : {
2416 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2417 : pszNextColon + 1);
2418 1 : return false;
2419 : }
2420 : m_fileReader =
2421 3 : std::make_shared<const LIBERTIFFDatasetFileReader>(m_poFile.get());
2422 : }
2423 : else
2424 : {
2425 : m_fileReader =
2426 1200 : std::make_shared<const LIBERTIFFDatasetFileReader>(poOpenInfo->fpL);
2427 : }
2428 :
2429 2406 : auto mainImage = LIBERTIFF_NS::open(m_fileReader);
2430 1203 : if (!mainImage)
2431 : {
2432 40 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open TIFF image");
2433 40 : return false;
2434 : }
2435 :
2436 2326 : if (mainImage->subFileType() != LIBERTIFF_NS::SubFileTypeFlags::Page &&
2437 1163 : mainImage->subFileType() != 0)
2438 : {
2439 0 : CPLDebug("LIBERTIFF", "Invalid subFileType value for first image");
2440 0 : return false;
2441 : }
2442 :
2443 : // Check structural metadata (for COG)
2444 1163 : const int nOffsetOfStructuralMetadata =
2445 1160 : poOpenInfo->nHeaderBytes && ((poOpenInfo->pabyHeader[2] == 0x2B ||
2446 1144 : poOpenInfo->pabyHeader[3] == 0x2B))
2447 2323 : ? 16
2448 : : 8;
2449 1163 : if (poOpenInfo->nHeaderBytes >
2450 1163 : nOffsetOfStructuralMetadata +
2451 1160 : static_cast<int>(strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) &&
2452 1160 : memcmp(poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata,
2453 : "GDAL_STRUCTURAL_METADATA_SIZE=",
2454 : strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) == 0)
2455 : {
2456 2 : const char *pszStructuralMD = reinterpret_cast<const char *>(
2457 2 : poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata);
2458 2 : const bool bLayoutIFDSBeforeData =
2459 2 : strstr(pszStructuralMD, "LAYOUT=IFDS_BEFORE_DATA") != nullptr;
2460 2 : const bool bBlockOrderRowMajor =
2461 2 : strstr(pszStructuralMD, "BLOCK_ORDER=ROW_MAJOR") != nullptr;
2462 2 : const bool bLeaderSizeAsUInt4 =
2463 2 : strstr(pszStructuralMD, "BLOCK_LEADER=SIZE_AS_UINT4") != nullptr;
2464 2 : const bool bTrailerRepeatedLast4BytesRepeated =
2465 2 : strstr(pszStructuralMD, "BLOCK_TRAILER=LAST_4_BYTES_REPEATED") !=
2466 : nullptr;
2467 2 : const bool bKnownIncompatibleEdition =
2468 2 : strstr(pszStructuralMD, "KNOWN_INCOMPATIBLE_EDITION=YES") !=
2469 : nullptr;
2470 2 : if (bKnownIncompatibleEdition)
2471 : {
2472 0 : ReportError(CE_Warning, CPLE_AppDefined,
2473 : "This file used to have optimizations in its layout, "
2474 : "but those have been, at least partly, invalidated by "
2475 : "later changes");
2476 : }
2477 2 : else if (bLayoutIFDSBeforeData && bBlockOrderRowMajor &&
2478 2 : bLeaderSizeAsUInt4 && bTrailerRepeatedLast4BytesRepeated)
2479 : {
2480 2 : GDALDataset::SetMetadataItem("LAYOUT", "COG", "IMAGE_STRUCTURE");
2481 : }
2482 : }
2483 :
2484 1163 : if (!Open(std::move(mainImage)))
2485 258 : return false;
2486 :
2487 : // Iterate over overviews
2488 905 : LIBERTIFFDataset *poLastNonMaskDS = this;
2489 1810 : auto imageNext = m_image->next();
2490 28 : if (imageNext &&
2491 28 : (m_image->subFileType() == 0 ||
2492 952 : m_image->subFileType() == LIBERTIFF_NS::SubFileTypeFlags::Page) &&
2493 47 : (imageNext->subFileType() == 0 ||
2494 19 : imageNext->subFileType() == LIBERTIFF_NS::SubFileTypeFlags::Page))
2495 : {
2496 9 : int iSubDS = 1;
2497 9 : CPLStringList aosList;
2498 9 : auto curImage = std::move(m_image);
2499 10 : do
2500 : {
2501 19 : if (iSelectedSubDS > 0 && iSubDS == iSelectedSubDS)
2502 : {
2503 2 : m_image = std::move(curImage);
2504 2 : break;
2505 : }
2506 17 : if (iSelectedSubDS < 0)
2507 : {
2508 : aosList.AddNameValue(
2509 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
2510 14 : CPLSPrintf("GTIFF_DIR:%d:%s", iSubDS, GetDescription()));
2511 : aosList.AddNameValue(CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
2512 : CPLSPrintf("Page %d (%uP x %uL x %uB)",
2513 : iSubDS, curImage->width(),
2514 : curImage->height(),
2515 14 : curImage->samplesPerPixel()));
2516 : }
2517 17 : ++iSubDS;
2518 17 : if (iSubDS == 65536)
2519 : {
2520 0 : ReportError(CE_Warning, CPLE_AppDefined,
2521 : "Stopping IFD scanning at 65536th one");
2522 0 : break;
2523 : }
2524 17 : curImage = curImage->next();
2525 17 : } while (curImage);
2526 9 : if (iSelectedSubDS < 0)
2527 : {
2528 13 : for (int i = 0; i < nBands; ++i)
2529 7 : delete papoBands[i];
2530 6 : CPLFree(papoBands);
2531 6 : papoBands = nullptr;
2532 6 : nRasterXSize = 0;
2533 6 : nRasterYSize = 0;
2534 6 : GDALDataset::SetMetadata(aosList.List(), "SUBDATASETS");
2535 6 : return true;
2536 : }
2537 3 : else if (!m_image)
2538 : {
2539 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %dth image",
2540 : iSelectedSubDS);
2541 1 : return false;
2542 : }
2543 : }
2544 896 : else if (iSelectedSubDS < 0)
2545 : {
2546 896 : auto curImage = std::move(imageNext);
2547 896 : int iters = 0;
2548 921 : while (curImage)
2549 : {
2550 25 : auto nextImage = curImage->next();
2551 25 : if (curImage->subFileType() ==
2552 : LIBERTIFF_NS::SubFileTypeFlags::ReducedImage)
2553 : {
2554 : // Overview IFD
2555 24 : auto poOvrDS = std::make_unique<LIBERTIFFDataset>();
2556 24 : if (poOvrDS->Open(std::move(curImage)) &&
2557 12 : poOvrDS->GetRasterCount() == nBands &&
2558 12 : poOvrDS->GetRasterXSize() <= nRasterXSize &&
2559 48 : poOvrDS->GetRasterYSize() <= nRasterYSize &&
2560 12 : poOvrDS->GetRasterBand(1)->GetRasterDataType() ==
2561 12 : GetRasterBand(1)->GetRasterDataType())
2562 : {
2563 12 : m_apoOvrDSOwned.push_back(std::move(poOvrDS));
2564 12 : auto poOvrDSRaw = m_apoOvrDSOwned.back().get();
2565 12 : m_apoOvrDS.push_back(poOvrDSRaw);
2566 12 : poLastNonMaskDS = poOvrDSRaw;
2567 : }
2568 : }
2569 13 : else if ((curImage->subFileType() &
2570 13 : LIBERTIFF_NS::SubFileTypeFlags::Mask) != 0)
2571 : {
2572 : // Mask IFD
2573 13 : if (!poLastNonMaskDS->m_poMaskDS)
2574 : {
2575 26 : auto poMaskDS = std::make_unique<LIBERTIFFDataset>();
2576 26 : if (poMaskDS->Open(std::move(curImage)) &&
2577 12 : poMaskDS->GetRasterCount() == 1 &&
2578 11 : poMaskDS->GetRasterXSize() ==
2579 11 : poLastNonMaskDS->nRasterXSize &&
2580 9 : poMaskDS->GetRasterYSize() ==
2581 47 : poLastNonMaskDS->nRasterYSize &&
2582 9 : poMaskDS->GetRasterBand(1)->GetRasterDataType() ==
2583 : GDT_Byte)
2584 : {
2585 9 : poMaskDS->m_bExpand1To255 = true;
2586 9 : poLastNonMaskDS->m_poMaskDS = std::move(poMaskDS);
2587 9 : if (poLastNonMaskDS != this && m_poMaskDS)
2588 : {
2589 : // Also register the mask as the overview of the main
2590 : // mask
2591 0 : m_poMaskDS->m_apoOvrDS.push_back(
2592 0 : poLastNonMaskDS->m_poMaskDS.get());
2593 : }
2594 : }
2595 : }
2596 : }
2597 : else
2598 : {
2599 0 : CPLDebug("LIBERTIFF",
2600 : "Unhandled subFileType value for auxiliary image");
2601 0 : return false;
2602 : }
2603 25 : curImage = std::move(nextImage);
2604 :
2605 25 : ++iters;
2606 25 : if (iters == 64)
2607 : {
2608 0 : ReportError(CE_Warning, CPLE_AppDefined,
2609 : "Stopping IFD scanning at 64th one");
2610 0 : break;
2611 : }
2612 : }
2613 : }
2614 :
2615 : static const struct
2616 : {
2617 : LIBERTIFF_NS::TagCodeType code;
2618 : const char *mditem;
2619 : } strTags[] = {
2620 : {LIBERTIFF_NS::TagCode::DocumentName, "TIFFTAG_DOCUMENTNAME"},
2621 : {LIBERTIFF_NS::TagCode::ImageDescription, "TIFFTAG_IMAGEDESCRIPTION"},
2622 : {LIBERTIFF_NS::TagCode::Software, "TIFFTAG_SOFTWARE"},
2623 : {LIBERTIFF_NS::TagCode::DateTime, "TIFFTAG_DATETIME"},
2624 : {LIBERTIFF_NS::TagCode::Copyright, "TIFFTAG_COPYRIGHT"},
2625 : };
2626 :
2627 5388 : for (const auto &strTag : strTags)
2628 : {
2629 4490 : const auto *tag = m_image->tag(strTag.code);
2630 4490 : constexpr size_t ARBITRARY_MAX_SIZE = 65536;
2631 4490 : if (tag && tag->type == LIBERTIFF_NS::TagType::ASCII &&
2632 11 : !(tag->count > 4 && tag->invalid_value_offset) &&
2633 11 : tag->count < ARBITRARY_MAX_SIZE)
2634 : {
2635 11 : bool ok = true;
2636 22 : const std::string str = m_image->readTagAsString(*tag, ok);
2637 11 : if (ok)
2638 : {
2639 11 : GDALDataset::SetMetadataItem(strTag.mditem, str.c_str());
2640 : }
2641 : }
2642 : }
2643 :
2644 898 : ReadSRS();
2645 898 : ReadGeoTransform();
2646 898 : ReadRPCTag();
2647 :
2648 : const auto *psGDALMetadataTag =
2649 898 : m_image->tag(LIBERTIFF_NS::TagCode::GDAL_METADATA);
2650 898 : constexpr size_t ARBITRARY_MAX_SIZE_GDAL_METADATA = 10 * 1024 * 1024;
2651 898 : if (psGDALMetadataTag &&
2652 76 : psGDALMetadataTag->type == LIBERTIFF_NS::TagType::ASCII &&
2653 76 : !(psGDALMetadataTag->count > 4 &&
2654 76 : psGDALMetadataTag->invalid_value_offset) &&
2655 76 : psGDALMetadataTag->count < ARBITRARY_MAX_SIZE_GDAL_METADATA)
2656 : {
2657 76 : bool ok = true;
2658 : const std::string str =
2659 152 : m_image->readTagAsString(*psGDALMetadataTag, ok);
2660 76 : if (ok)
2661 : {
2662 152 : auto oRoot = CPLXMLTreeCloser(CPLParseXMLString(str.c_str()));
2663 76 : if (oRoot.get())
2664 : {
2665 : const CPLXMLNode *psItem =
2666 76 : oRoot.get() ? CPLGetXMLNode(oRoot.get(), "=GDALMetadata")
2667 76 : : nullptr;
2668 76 : if (psItem)
2669 76 : psItem = psItem->psChild;
2670 268 : for (; psItem != nullptr; psItem = psItem->psNext)
2671 : {
2672 192 : if (psItem->eType != CXT_Element ||
2673 192 : !EQUAL(psItem->pszValue, "Item"))
2674 0 : continue;
2675 :
2676 : const char *pszKey =
2677 192 : CPLGetXMLValue(psItem, "name", nullptr);
2678 : const char *pszValue =
2679 192 : CPLGetXMLValue(psItem, nullptr, nullptr);
2680 192 : int nBand = atoi(CPLGetXMLValue(psItem, "sample", "-1"));
2681 192 : if (nBand < -1 || nBand > 65535)
2682 0 : continue;
2683 192 : nBand++;
2684 192 : const char *pszRole = CPLGetXMLValue(psItem, "role", "");
2685 : const char *pszDomain =
2686 192 : CPLGetXMLValue(psItem, "domain", "");
2687 :
2688 192 : if (pszKey == nullptr || pszValue == nullptr)
2689 6 : continue;
2690 186 : if (EQUAL(pszDomain, "IMAGE_STRUCTURE"))
2691 : {
2692 78 : if (m_image->compression() ==
2693 84 : LIBERTIFF_NS::Compression::WEBP &&
2694 6 : EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
2695 : {
2696 : // go on
2697 : }
2698 72 : else if (m_image->compression() ==
2699 72 : LIBERTIFF_NS::Compression::WEBP &&
2700 0 : EQUAL(pszKey, "WEBP_LEVEL"))
2701 : {
2702 0 : const int nLevel = atoi(pszValue);
2703 0 : if (nLevel >= 1 && nLevel <= 100)
2704 : {
2705 0 : GDALDataset::SetMetadataItem(
2706 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2707 : "IMAGE_STRUCTURE");
2708 : }
2709 : }
2710 72 : else if (m_image->compression() ==
2711 114 : LIBERTIFF_NS::Compression::LERC &&
2712 42 : EQUAL(pszKey, "MAX_Z_ERROR"))
2713 : {
2714 : // go on
2715 : }
2716 72 : else if (m_image->compression() ==
2717 114 : LIBERTIFF_NS::Compression::LERC &&
2718 42 : EQUAL(pszKey, "MAX_Z_ERROR_OVERVIEW"))
2719 : {
2720 : // go on
2721 : }
2722 72 : else if (m_image->compression() ==
2723 100 : LIBERTIFF_NS::Compression::JXL &&
2724 28 : EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
2725 : {
2726 : // go on
2727 : }
2728 58 : else if (m_image->compression() ==
2729 72 : LIBERTIFF_NS::Compression::JXL &&
2730 14 : EQUAL(pszKey, "JXL_DISTANCE"))
2731 : {
2732 0 : const double dfVal = CPLAtof(pszValue);
2733 0 : if (dfVal > 0 && dfVal <= 15)
2734 : {
2735 0 : GDALDataset::SetMetadataItem(
2736 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2737 : "IMAGE_STRUCTURE");
2738 : }
2739 : }
2740 58 : else if (m_image->compression() ==
2741 72 : LIBERTIFF_NS::Compression::JXL &&
2742 14 : EQUAL(pszKey, "JXL_ALPHA_DISTANCE"))
2743 : {
2744 0 : const double dfVal = CPLAtof(pszValue);
2745 0 : if (dfVal > 0 && dfVal <= 15)
2746 : {
2747 0 : GDALDataset::SetMetadataItem(
2748 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2749 : "IMAGE_STRUCTURE");
2750 : }
2751 : }
2752 58 : else if (m_image->compression() ==
2753 72 : LIBERTIFF_NS::Compression::JXL &&
2754 14 : EQUAL(pszKey, "JXL_EFFORT"))
2755 : {
2756 : // go on
2757 : }
2758 : else
2759 : {
2760 44 : continue;
2761 : }
2762 : }
2763 :
2764 142 : bool bIsXML = false;
2765 :
2766 142 : if (STARTS_WITH_CI(pszDomain, "xml:"))
2767 0 : bIsXML = TRUE;
2768 :
2769 : // Note: this un-escaping should not normally be done, as the
2770 : // deserialization of the tree from XML also does it, so we end up
2771 : // width double XML escaping, but keep it for backward
2772 : // compatibility.
2773 : char *pszUnescapedValue =
2774 142 : CPLUnescapeString(pszValue, nullptr, CPLES_XML);
2775 142 : if (nBand == 0)
2776 : {
2777 118 : if (bIsXML)
2778 : {
2779 0 : char *apszMD[2] = {pszUnescapedValue, nullptr};
2780 0 : GDALDataset::SetMetadata(apszMD, pszDomain);
2781 : }
2782 : else
2783 : {
2784 118 : GDALDataset::SetMetadataItem(
2785 : pszKey, pszUnescapedValue, pszDomain);
2786 : }
2787 : }
2788 : else
2789 : {
2790 24 : auto poBand = cpl::down_cast<LIBERTIFFBand *>(
2791 : GetRasterBand(nBand));
2792 24 : if (poBand != nullptr)
2793 : {
2794 24 : if (EQUAL(pszRole, "scale"))
2795 : {
2796 0 : poBand->m_bHaveOffsetScale = true;
2797 0 : poBand->m_dfScale = CPLAtofM(pszUnescapedValue);
2798 : }
2799 24 : else if (EQUAL(pszRole, "offset"))
2800 : {
2801 0 : poBand->m_bHaveOffsetScale = true;
2802 0 : poBand->m_dfOffset =
2803 0 : CPLAtofM(pszUnescapedValue);
2804 : }
2805 24 : else if (EQUAL(pszRole, "unittype"))
2806 : {
2807 0 : poBand->m_osUnitType = pszUnescapedValue;
2808 : }
2809 24 : else if (EQUAL(pszRole, "description"))
2810 : {
2811 0 : poBand->m_osDescription = pszUnescapedValue;
2812 : }
2813 24 : else if (EQUAL(pszRole, "colorinterp"))
2814 : {
2815 9 : if (EQUAL(pszUnescapedValue, "undefined"))
2816 0 : poBand->m_eColorInterp = GCI_Undefined;
2817 : else
2818 : {
2819 9 : poBand->m_eColorInterp =
2820 9 : GDALGetColorInterpretationByName(
2821 : pszUnescapedValue);
2822 9 : if (poBand->m_eColorInterp == GCI_Undefined)
2823 : {
2824 0 : poBand->GDALRasterBand::SetMetadataItem(
2825 : "COLOR_INTERPRETATION",
2826 : pszUnescapedValue);
2827 : }
2828 : }
2829 : }
2830 : else
2831 : {
2832 15 : if (bIsXML)
2833 : {
2834 0 : char *apszMD[2] = {pszUnescapedValue,
2835 0 : nullptr};
2836 0 : poBand->GDALRasterBand::SetMetadata(
2837 : apszMD, pszDomain);
2838 : }
2839 : else
2840 : {
2841 15 : poBand->GDALRasterBand::SetMetadataItem(
2842 : pszKey, pszUnescapedValue, pszDomain);
2843 : }
2844 : }
2845 : }
2846 : }
2847 142 : CPLFree(pszUnescapedValue);
2848 : }
2849 : }
2850 : }
2851 : }
2852 :
2853 1787 : if ((m_image->compression() == LIBERTIFF_NS::Compression::WEBP ||
2854 1764 : m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
2855 2671 : m_image->compression() == LIBERTIFF_NS::Compression::JXL_DNG_1_7) &&
2856 23 : GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE") ==
2857 : nullptr)
2858 : {
2859 : const char *pszDriverName =
2860 3 : m_image->compression() == LIBERTIFF_NS::Compression::WEBP
2861 3 : ? "WEBP"
2862 3 : : "JPEGXL";
2863 3 : auto poTileDriver = GDALGetDriverByName(pszDriverName);
2864 3 : if (poTileDriver)
2865 : {
2866 3 : bool ok = true;
2867 3 : const uint64_t offset = m_image->strileOffset(0, ok);
2868 3 : const uint64_t bytecount = m_image->strileByteCount(0, ok);
2869 3 : if (ok && bytecount > 0)
2870 : {
2871 : const std::string osSubfile(
2872 : CPLSPrintf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
2873 : static_cast<GUIntBig>(offset),
2874 3 : static_cast<int>(std::min(
2875 3 : static_cast<uint64_t>(1024), bytecount)),
2876 6 : GetDescription()));
2877 3 : const char *const apszDrivers[] = {pszDriverName, nullptr};
2878 : auto poTileDataset =
2879 : std::unique_ptr<GDALDataset>(GDALDataset::Open(
2880 6 : osSubfile.c_str(), GDAL_OF_RASTER, apszDrivers));
2881 3 : if (poTileDataset)
2882 : {
2883 : const char *pszReversibility =
2884 3 : poTileDataset->GetMetadataItem(
2885 3 : "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
2886 3 : if (pszReversibility)
2887 3 : GDALDataset::SetMetadataItem(
2888 : "COMPRESSION_REVERSIBILITY", pszReversibility,
2889 : "IMAGE_STRUCTURE");
2890 : }
2891 : }
2892 : }
2893 : }
2894 :
2895 : // Init mask bands
2896 67786 : for (int i = 0; i < nBands; ++i)
2897 : {
2898 66888 : cpl::down_cast<LIBERTIFFBand *>(papoBands[i])->InitMaskBand();
2899 : }
2900 910 : for (auto &poOvrDS : m_apoOvrDS)
2901 : {
2902 26 : for (int i = 0; i < nBands; ++i)
2903 : {
2904 14 : cpl::down_cast<LIBERTIFFBand *>(poOvrDS->papoBands[i])
2905 14 : ->InitMaskBand();
2906 : }
2907 : }
2908 :
2909 898 : m_fileReader->setPReadAllowed();
2910 :
2911 898 : if (poOpenInfo->fpL)
2912 : {
2913 896 : m_poFile.reset(poOpenInfo->fpL);
2914 896 : poOpenInfo->fpL = nullptr;
2915 : }
2916 :
2917 : const char *pszValue =
2918 898 : CSLFetchNameValue(poOpenInfo->papszOpenOptions, "NUM_THREADS");
2919 898 : if (pszValue == nullptr)
2920 801 : pszValue = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
2921 898 : if (pszValue)
2922 : {
2923 : int nThreads =
2924 97 : EQUAL(pszValue, "ALL_CPUS") ? CPLGetNumCPUs() : atoi(pszValue);
2925 97 : if (nThreads > 1024)
2926 1 : nThreads = 1024; // to please Coverity
2927 97 : if (nThreads > 1)
2928 : {
2929 97 : m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
2930 : }
2931 : }
2932 :
2933 898 : return true;
2934 : }
2935 :
2936 : /************************************************************************/
2937 : /* ReadSRS() */
2938 : /************************************************************************/
2939 :
2940 : // Simplified GeoTIFF SRS reader, assuming the SRS is encoded as a EPSG code
2941 898 : void LIBERTIFFDataset::ReadSRS()
2942 : {
2943 : const auto psGeoKeysTag =
2944 898 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoKeyDirectory);
2945 898 : constexpr int VALUES_PER_GEOKEY = 4;
2946 898 : if (psGeoKeysTag && psGeoKeysTag->type == LIBERTIFF_NS::TagType::Short &&
2947 312 : !psGeoKeysTag->invalid_value_offset &&
2948 312 : psGeoKeysTag->count >= VALUES_PER_GEOKEY &&
2949 312 : (psGeoKeysTag->count % VALUES_PER_GEOKEY) == 0 &&
2950 : // Sanity check
2951 312 : psGeoKeysTag->count < 1000)
2952 : {
2953 312 : bool ok = true;
2954 : const auto values =
2955 312 : m_image->readTagAsVector<uint16_t>(*psGeoKeysTag, ok);
2956 312 : if (values.size() >= 4)
2957 : {
2958 309 : const uint16_t geokeysCount = values[3];
2959 309 : constexpr uint16_t GEOTIFF_KEY_DIRECTORY_VERSION_V1 = 1;
2960 309 : constexpr uint16_t GEOTIFF_KEY_VERSION_MAJOR_V1 = 1;
2961 309 : if (values[0] == GEOTIFF_KEY_DIRECTORY_VERSION_V1 &&
2962 : // GeoTIFF 1.x
2963 618 : values[1] == GEOTIFF_KEY_VERSION_MAJOR_V1 &&
2964 : // No equality for autotest/gcore/data/ycbcr_with_mask.tif
2965 309 : geokeysCount <= psGeoKeysTag->count / VALUES_PER_GEOKEY - 1)
2966 : {
2967 308 : constexpr uint16_t GeoTIFFTypeShort = 0;
2968 308 : constexpr uint16_t GeoTIFFTypeDouble =
2969 : LIBERTIFF_NS::TagCode::GeoTIFFDoubleParams;
2970 :
2971 308 : constexpr uint16_t GTModelTypeGeoKey = 1024;
2972 308 : constexpr uint16_t ModelTypeProjected = 1;
2973 308 : constexpr uint16_t ModelTypeGeographic = 2;
2974 :
2975 308 : constexpr uint16_t GTRasterTypeGeoKey = 1025;
2976 308 : constexpr uint16_t RasterPixelIsArea = 1;
2977 308 : constexpr uint16_t RasterPixelIsPoint = 2;
2978 :
2979 308 : constexpr uint16_t GeodeticCRSGeoKey = 2048;
2980 308 : constexpr uint16_t ProjectedCRSGeoKey = 3072;
2981 :
2982 308 : constexpr uint16_t VerticalGeoKey = 4096;
2983 :
2984 308 : constexpr uint16_t CoordinateEpochGeoKey = 5120;
2985 :
2986 308 : uint16_t nModelType = 0;
2987 308 : uint16_t nEPSGCode = 0;
2988 308 : uint16_t nEPSGCodeVertical = 0;
2989 308 : double dfCoordEpoch = 0;
2990 308 : bool bHasCoordEpoch = false;
2991 2424 : for (uint32_t i = 1; i <= geokeysCount; ++i)
2992 : {
2993 2116 : const auto geokey = values[VALUES_PER_GEOKEY * i];
2994 2116 : const auto geokeyType = values[VALUES_PER_GEOKEY * i + 1];
2995 2116 : const auto geokeyCount = values[VALUES_PER_GEOKEY * i + 2];
2996 2116 : const auto geokeyValue = values[VALUES_PER_GEOKEY * i + 3];
2997 2116 : if (geokey == GTModelTypeGeoKey)
2998 : {
2999 304 : nModelType = geokeyValue;
3000 : }
3001 1812 : else if (geokey == GeodeticCRSGeoKey &&
3002 175 : nModelType == ModelTypeGeographic &&
3003 175 : geokeyType == GeoTIFFTypeShort &&
3004 175 : geokeyCount == 1 && geokeyValue > 0)
3005 : {
3006 175 : nEPSGCode = geokeyValue;
3007 : }
3008 1637 : else if (geokey == ProjectedCRSGeoKey &&
3009 125 : nModelType == ModelTypeProjected &&
3010 125 : geokeyType == GeoTIFFTypeShort &&
3011 125 : geokeyCount == 1 && geokeyValue > 0)
3012 : {
3013 125 : nEPSGCode = geokeyValue;
3014 : }
3015 1512 : else if (geokey == GTRasterTypeGeoKey &&
3016 306 : geokeyType == GeoTIFFTypeShort && geokeyCount == 1)
3017 : {
3018 306 : if (geokeyValue == RasterPixelIsArea)
3019 : {
3020 300 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT,
3021 : GDALMD_AOP_AREA);
3022 : }
3023 6 : else if (geokeyValue == RasterPixelIsPoint)
3024 : {
3025 6 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT,
3026 : GDALMD_AOP_POINT);
3027 : }
3028 : }
3029 1217 : else if (values[2] == 1 /* GeoTIFF 1.1 */ &&
3030 7 : geokey == VerticalGeoKey &&
3031 1217 : geokeyType == GeoTIFFTypeShort && geokeyCount == 1)
3032 : {
3033 7 : nEPSGCodeVertical = geokeyValue;
3034 : }
3035 1199 : else if (geokey == CoordinateEpochGeoKey &&
3036 1 : geokeyType == GeoTIFFTypeDouble &&
3037 : geokeyCount == 1)
3038 : {
3039 1 : const auto psGeoDoubleParamsTag = m_image->tag(
3040 : LIBERTIFF_NS::TagCode::GeoTIFFDoubleParams);
3041 1 : if (psGeoDoubleParamsTag &&
3042 1 : psGeoDoubleParamsTag->type ==
3043 1 : LIBERTIFF_NS::TagType::Double &&
3044 1 : psGeoDoubleParamsTag->count > geokeyValue)
3045 : {
3046 1 : ok = true;
3047 : const auto doubleValues =
3048 : m_image->readTagAsVector<double>(
3049 2 : *psGeoDoubleParamsTag, ok);
3050 1 : if (ok && doubleValues.size() > geokeyValue)
3051 : {
3052 1 : bHasCoordEpoch = true;
3053 1 : dfCoordEpoch = doubleValues[geokeyValue];
3054 : }
3055 : }
3056 : }
3057 : }
3058 :
3059 308 : if (nEPSGCode > 0 && nEPSGCode != 32767 &&
3060 : nEPSGCodeVertical != 32767)
3061 : {
3062 290 : m_oSRS.importFromEPSG(nEPSGCode);
3063 :
3064 290 : if (nEPSGCodeVertical > 0)
3065 : {
3066 14 : OGRSpatialReference oSRSVertical;
3067 7 : oSRSVertical.importFromEPSG(nEPSGCodeVertical);
3068 10 : if (oSRSVertical.IsGeographic() &&
3069 3 : oSRSVertical.GetAxesCount() == 3)
3070 : {
3071 3 : m_oSRS = std::move(oSRSVertical);
3072 : }
3073 : else
3074 : {
3075 4 : m_oSRS.SetFromUserInput(CPLSPrintf(
3076 : "EPSG:%d+%d", nEPSGCode, nEPSGCodeVertical));
3077 : }
3078 : }
3079 :
3080 290 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3081 290 : if (bHasCoordEpoch)
3082 1 : m_oSRS.SetCoordinateEpoch(dfCoordEpoch);
3083 290 : return;
3084 : }
3085 :
3086 18 : const char *const apszAllowedDrivers[] = {"GTiff", nullptr};
3087 : auto poTmpDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3088 18 : GetDescription(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
3089 36 : apszAllowedDrivers, nullptr, nullptr));
3090 18 : if (poTmpDS)
3091 : {
3092 18 : const OGRSpatialReference *poSRS = poTmpDS->GetSpatialRef();
3093 18 : if (!poSRS)
3094 2 : poSRS = poTmpDS->GetGCPSpatialRef();
3095 18 : if (poSRS)
3096 18 : m_oSRS = *poSRS;
3097 : }
3098 : }
3099 : }
3100 : }
3101 : }
3102 :
3103 : /************************************************************************/
3104 : /* ReadGeoTransform() */
3105 : /************************************************************************/
3106 :
3107 898 : void LIBERTIFFDataset::ReadGeoTransform()
3108 : {
3109 : // Number of values per GCP in the GeoTIFFTiePoints tag
3110 898 : constexpr int VALUES_PER_GCP = 6;
3111 :
3112 898 : constexpr int GCP_PIXEL = 0;
3113 898 : constexpr int GCP_LINE = 1;
3114 : // constexpr int GCP_DEPTH = 2;
3115 898 : constexpr int GCP_X = 3;
3116 898 : constexpr int GCP_Y = 4;
3117 898 : constexpr int GCP_Z = 5;
3118 :
3119 : const auto *psTagTiePoints =
3120 898 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFTiePoints);
3121 : const auto *psTagPixelScale =
3122 898 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFPixelScale);
3123 : const auto *psTagGeoTransMatrix =
3124 898 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoTransMatrix);
3125 898 : if (psTagTiePoints &&
3126 312 : psTagTiePoints->type == LIBERTIFF_NS::TagType::Double &&
3127 312 : !psTagTiePoints->invalid_value_offset &&
3128 312 : psTagTiePoints->count == VALUES_PER_GCP && psTagPixelScale &&
3129 308 : psTagPixelScale->type == LIBERTIFF_NS::TagType::Double &&
3130 308 : !psTagPixelScale->invalid_value_offset && psTagPixelScale->count == 3)
3131 : {
3132 308 : bool ok = true;
3133 : const auto tiepoints =
3134 308 : m_image->readTagAsVector<double>(*psTagTiePoints, ok);
3135 : const auto pixelScale =
3136 308 : m_image->readTagAsVector<double>(*psTagPixelScale, ok);
3137 308 : if (!ok)
3138 3 : return;
3139 :
3140 305 : m_geotransformValid = true;
3141 305 : m_geotransform[1] = pixelScale[GCP_PIXEL];
3142 305 : m_geotransform[5] = -pixelScale[GCP_LINE];
3143 610 : m_geotransform[0] =
3144 305 : tiepoints[GCP_X] - tiepoints[GCP_PIXEL] * m_geotransform[1];
3145 305 : m_geotransform[3] =
3146 610 : tiepoints[GCP_Y] - tiepoints[GCP_LINE] * m_geotransform[5];
3147 : }
3148 590 : else if (psTagGeoTransMatrix &&
3149 5 : psTagGeoTransMatrix->type == LIBERTIFF_NS::TagType::Double &&
3150 5 : !psTagGeoTransMatrix->invalid_value_offset &&
3151 5 : psTagGeoTransMatrix->count == 16)
3152 : {
3153 5 : bool ok = true;
3154 : const auto matrix =
3155 10 : m_image->readTagAsVector<double>(*psTagGeoTransMatrix, ok);
3156 5 : if (ok)
3157 : {
3158 5 : m_geotransformValid = true;
3159 5 : m_geotransform[0] = matrix[3];
3160 5 : m_geotransform[1] = matrix[0];
3161 5 : m_geotransform[2] = matrix[1];
3162 5 : m_geotransform[3] = matrix[7];
3163 5 : m_geotransform[4] = matrix[4];
3164 5 : m_geotransform[5] = matrix[5];
3165 5 : }
3166 : }
3167 585 : else if (psTagTiePoints &&
3168 4 : psTagTiePoints->type == LIBERTIFF_NS::TagType::Double &&
3169 4 : !psTagTiePoints->invalid_value_offset &&
3170 4 : psTagTiePoints->count > VALUES_PER_GCP &&
3171 4 : (psTagTiePoints->count % VALUES_PER_GCP) == 0 &&
3172 4 : psTagTiePoints->count <= 10000 * VALUES_PER_GCP)
3173 : {
3174 4 : bool ok = true;
3175 : const auto tiepoints =
3176 8 : m_image->readTagAsVector<double>(*psTagTiePoints, ok);
3177 4 : if (ok)
3178 : {
3179 4 : bool pixelIsPoint = false;
3180 4 : if (const char *pszAreaOrPoint =
3181 4 : GetMetadataItem(GDALMD_AREA_OR_POINT))
3182 : {
3183 4 : pixelIsPoint = EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
3184 : }
3185 4 : const int gcpCount =
3186 4 : static_cast<int>(psTagTiePoints->count / VALUES_PER_GCP);
3187 18 : for (int iGCP = 0; iGCP < gcpCount; ++iGCP)
3188 : {
3189 : m_aoGCPs.emplace_back(
3190 14 : CPLSPrintf("%d", iGCP + 1), "",
3191 14 : /* pixel = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_PIXEL],
3192 14 : /* line = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_LINE],
3193 14 : /* X = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_X],
3194 14 : /* Y = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_Y],
3195 28 : /* Z = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_Z]);
3196 :
3197 14 : if (pixelIsPoint)
3198 : {
3199 8 : m_aoGCPs.back().Pixel() += 0.5;
3200 8 : m_aoGCPs.back().Line() += 0.5;
3201 : }
3202 : }
3203 : }
3204 : }
3205 :
3206 895 : if (m_geotransformValid)
3207 : {
3208 310 : if (const char *pszAreaOrPoint = GetMetadataItem(GDALMD_AREA_OR_POINT))
3209 : {
3210 302 : if (EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
3211 : {
3212 8 : m_geotransform[0] -=
3213 4 : (m_geotransform[1] * 0.5 + m_geotransform[2] * 0.5);
3214 4 : m_geotransform[3] -=
3215 4 : (m_geotransform[4] * 0.5 + m_geotransform[5] * 0.5);
3216 : }
3217 : }
3218 : }
3219 : }
3220 :
3221 : /************************************************************************/
3222 : /* ReadRPCTag() */
3223 : /* */
3224 : /* Format a TAG according to: */
3225 : /* */
3226 : /* http://geotiff.maptools.org/rpc_prop.html */
3227 : /************************************************************************/
3228 :
3229 898 : void LIBERTIFFDataset::ReadRPCTag()
3230 :
3231 : {
3232 : const auto *psTagRPCCoefficients =
3233 898 : m_image->tag(LIBERTIFF_NS::TagCode::RPCCoefficients);
3234 898 : if (psTagRPCCoefficients &&
3235 4 : psTagRPCCoefficients->type == LIBERTIFF_NS::TagType::Double &&
3236 4 : !psTagRPCCoefficients->invalid_value_offset &&
3237 4 : psTagRPCCoefficients->count == 92)
3238 : {
3239 4 : bool ok = true;
3240 : const auto adfRPC =
3241 8 : m_image->readTagAsVector<double>(*psTagRPCCoefficients, ok);
3242 4 : if (ok && adfRPC.size() == 92)
3243 : {
3244 4 : GDALDataset::SetMetadata(
3245 8 : gdal::tiff_common::TIFFRPCTagToRPCMetadata(adfRPC.data())
3246 : .List(),
3247 : "RPC");
3248 : }
3249 : }
3250 898 : }
3251 :
3252 : /************************************************************************/
3253 : /* OpenStatic() */
3254 : /************************************************************************/
3255 :
3256 1213 : /* static */ GDALDataset *LIBERTIFFDataset::OpenStatic(GDALOpenInfo *poOpenInfo)
3257 : {
3258 1213 : if (!Identify(poOpenInfo))
3259 0 : return nullptr;
3260 :
3261 2426 : auto poDS = std::make_unique<LIBERTIFFDataset>();
3262 1213 : if (!poDS->Open(poOpenInfo))
3263 309 : return nullptr;
3264 904 : return poDS.release();
3265 : }
3266 :
3267 : /************************************************************************/
3268 : /* GDALRegister_LIBERTIFF() */
3269 : /************************************************************************/
3270 :
3271 1889 : void GDALRegister_LIBERTIFF()
3272 :
3273 : {
3274 1889 : if (GDALGetDriverByName("LIBERTIFF") != nullptr)
3275 282 : return;
3276 :
3277 3214 : auto poDriver = std::make_unique<GDALDriver>();
3278 1607 : poDriver->SetDescription("LIBERTIFF");
3279 1607 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3280 1607 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
3281 1607 : "GeoTIFF (using LIBERTIFF library)");
3282 1607 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
3283 1607 : "drivers/raster/libertiff.html");
3284 1607 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/tiff");
3285 1607 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "tif tiff");
3286 1607 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3287 1607 : poDriver->SetMetadataItem(GDAL_DCAP_COORDINATE_EPOCH, "YES");
3288 :
3289 1607 : poDriver->pfnIdentify = LIBERTIFFDataset::Identify;
3290 1607 : poDriver->pfnOpen = LIBERTIFFDataset::OpenStatic;
3291 :
3292 1607 : poDriver->SetMetadataItem(
3293 : GDAL_DMD_OPENOPTIONLIST,
3294 : "<OpenOptionList>"
3295 : " <Option name='NUM_THREADS' type='string' description='Number of "
3296 : "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3297 1607 : "</OpenOptionList>");
3298 :
3299 1607 : if (CPLGetDecompressor("lzma"))
3300 : {
3301 1607 : poDriver->SetMetadataItem("LZMA_SUPPORT", "YES", "LIBERTIFF");
3302 : }
3303 : #ifdef ZSTD_SUPPORT
3304 1607 : poDriver->SetMetadataItem("ZSTD_SUPPORT", "YES", "LIBERTIFF");
3305 : #endif
3306 : #if defined(LERC_SUPPORT)
3307 1607 : poDriver->SetMetadataItem("LERC_SUPPORT", "YES", "LIBERTIFF");
3308 : #if defined(LERC_VERSION_MAJOR)
3309 : poDriver->SetMetadataItem("LERC_VERSION_MAJOR",
3310 : XSTRINGIFY(LERC_VERSION_MAJOR), "LERC");
3311 : poDriver->SetMetadataItem("LERC_VERSION_MINOR",
3312 : XSTRINGIFY(LERC_VERSION_MINOR), "LERC");
3313 : poDriver->SetMetadataItem("LERC_VERSION_PATCH",
3314 : XSTRINGIFY(LERC_VERSION_PATCH), "LERC");
3315 : #endif
3316 : #endif
3317 :
3318 1607 : GetGDALDriverManager()->RegisterDriver(poDriver.release());
3319 : }
|