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