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 1220 : explicit LIBERTIFFDatasetFileReader(VSILFILE *fp)
60 1220 : : m_fp(fp), m_bHasPread(m_fp->HasPRead())
61 : {
62 1220 : }
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 915 : void setPReadAllowed() const
78 : {
79 915 : m_bPReadAllowed = true;
80 915 : }
81 :
82 : CPL_DISALLOW_COPY_ASSIGN(LIBERTIFFDatasetFileReader)
83 : };
84 :
85 94303 : size_t LIBERTIFFDatasetFileReader::read(uint64_t offset, size_t count,
86 : void *buffer) const
87 : {
88 94303 : if (m_bHasPread && m_bPReadAllowed)
89 : {
90 22570 : return m_fp->PRead(buffer, count, offset);
91 : }
92 : else
93 : {
94 71733 : std::lock_guard oLock(m_oMutex);
95 71733 : return m_fp->Seek(offset, SEEK_SET) == 0 ? m_fp->Read(buffer, 1, count)
96 71733 : : 0;
97 : }
98 : }
99 :
100 : /************************************************************************/
101 : /* LIBERTIFFDataset */
102 : /************************************************************************/
103 :
104 : class LIBERTIFFDataset final : public GDALPamDataset
105 : {
106 : public:
107 2510 : 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 1255 : 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 982 : explicit ThreadLocalState(const LIBERTIFFDataset *ds)
193 982 : : m_validityTest(ds->m_validityPtr)
194 : {
195 982 : memset(&m_tiff, 0, sizeof(m_tiff));
196 982 : }
197 :
198 978 : ~ThreadLocalState()
199 978 : {
200 978 : if (m_tiff.tif_cleanup)
201 145 : m_tiff.tif_cleanup(&m_tiff);
202 978 : }
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 66961 : LIBERTIFFBand(LIBERTIFFDataset *poDSIn, int nBandIn, GDALDataType eDT,
253 : int nBlockXSizeIn, int nBlockYSizeIn)
254 66961 : {
255 66961 : poDS = poDSIn;
256 66961 : nBand = nBandIn;
257 66961 : eDataType = eDT;
258 66961 : nBlockXSize = nBlockXSizeIn;
259 66961 : nBlockYSize = nBlockYSizeIn;
260 66961 : }
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 654 : void LIBERTIFFBand::ReadColorMap()
452 : {
453 654 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
454 :
455 : const auto psTagColorMap =
456 654 : 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 670 : 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 654 : }
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 66939 : void LIBERTIFFBand::InitMaskBand()
536 : {
537 66939 : auto l_poDS = cpl::down_cast<LIBERTIFFDataset *>(poDS);
538 66939 : if (m_bHasNoData)
539 : {
540 36 : nMaskFlags = GMF_NODATA;
541 36 : poMask.reset(std::make_unique<GDALNoDataMaskBand>(this));
542 : }
543 66903 : 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 66883 : 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 66839 : nMaskFlags = GMF_ALL_VALID;
556 66839 : poMask.reset(std::make_unique<GDALAllValidMaskBand>(this));
557 : }
558 66939 : }
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 8788 : LIBERTIFFDataset::ThreadLocalState &LIBERTIFFDataset::GetTLSState() const
667 : {
668 : static thread_local lru11::Cache<const LIBERTIFFDataset *,
669 : std::shared_ptr<ThreadLocalState>>
670 8788 : tlsState;
671 :
672 8788 : std::shared_ptr<ThreadLocalState> value;
673 8788 : if (tlsState.tryGet(this, value))
674 : {
675 8769 : if (value->isValid())
676 7806 : return *value.get();
677 : }
678 982 : value = std::make_shared<ThreadLocalState>(this);
679 982 : tlsState.insert(this, value);
680 982 : return *value.get();
681 : }
682 :
683 : /************************************************************************/
684 : /* IRasterIO() */
685 : /************************************************************************/
686 :
687 3135 : 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 3135 : if (eRWFlag != GF_Read)
694 0 : return CE_Failure;
695 :
696 : // Try to pass the request to the most appropriate overview dataset.
697 3135 : 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 3134 : const GDALDataType eNativeDT = papoBands[0]->GetRasterDataType();
709 : const size_t nNativeDTSize =
710 3134 : static_cast<size_t>(GDALGetDataTypeSizeBytes(eNativeDT));
711 : int nBlockXSize, nBlockYSize;
712 3134 : papoBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
713 :
714 3134 : const int iXBlockMin = nXOff / nBlockXSize;
715 3134 : const int iYBlockMin = nYOff / nBlockYSize;
716 3134 : 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 2446 : if (nXSize != nBufXSize || nYSize != nBufYSize ||
789 2440 : (nXOff % nBlockXSize) != 0 || (nYOff % nBlockYSize) != 0 ||
790 2437 : !(nXOff + nXSize == nRasterXSize || (nBufXSize % nBlockXSize) == 0) ||
791 2437 : !(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 2437 : const int iYBlockMax = DIV_ROUND_UP(nYOff + nBufYSize, nBlockYSize);
881 2437 : const int iXBlockMax = DIV_ROUND_UP(nXOff + nBufXSize, nBlockXSize);
882 :
883 0 : CPLJobQueuePtr poQueue;
884 2437 : const bool bIsSeparate = m_image->planarConfiguration() ==
885 2437 : LIBERTIFF_NS::PlanarConfiguration::Separate;
886 2437 : 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 2437 : std::atomic<bool> bSuccess(true);
893 :
894 4979 : for (int iYBlock = iYBlockMin, iY = 0; iYBlock < iYBlockMax && bSuccess;
895 : ++iYBlock, ++iY)
896 : {
897 9885 : for (int iXBlock = iXBlockMin, iX = 0; iXBlock < iXBlockMax && bSuccess;
898 : ++iXBlock, ++iX)
899 : {
900 7343 : 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 1683 : const auto lambda = [this, &bSuccess, nBandCount, panBandMap,
933 : pData, iY, nLineSpace, nBlockYSize, iX,
934 : nPixelSpace, nBlockXSize, nBandSpace,
935 1702 : iXBlock, iYBlock, eBufType]()
936 : {
937 1683 : if (!ReadBlock(static_cast<GByte *>(pData) +
938 1683 : iY * nLineSpace * nBlockYSize +
939 1683 : iX * nPixelSpace * nBlockXSize,
940 : iXBlock, iYBlock, nBandCount, panBandMap,
941 : eBufType, nPixelSpace, nLineSpace,
942 : nBandSpace))
943 : {
944 19 : bSuccess = false;
945 : }
946 3366 : };
947 1683 : if (poQueue)
948 : {
949 544 : poQueue->SubmitJob(lambda);
950 : }
951 : else
952 : {
953 1139 : lambda();
954 : }
955 : }
956 : }
957 : }
958 :
959 2437 : if (poQueue)
960 260 : poQueue->WaitCompletion();
961 :
962 2437 : 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 8091 : 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 8091 : uint64_t offset = 0;
1234 8091 : size_t size = 0;
1235 8091 : const bool bSeparate = m_image->planarConfiguration() ==
1236 8091 : LIBERTIFF_NS::PlanarConfiguration::Separate;
1237 :
1238 8091 : ThreadLocalState &tlsState = GetTLSState();
1239 :
1240 8091 : const int iBandTIFFFirst = bSeparate ? panBandMap[0] - 1 : 0;
1241 : uint64_t curStrileIdx;
1242 8091 : if (m_image->isTiled())
1243 : {
1244 6454 : bool ok = true;
1245 6454 : 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 8091 : if (curStrileIdx != tlsState.m_curStrileIdx)
1259 : {
1260 7967 : bool ok = true;
1261 7967 : offset = curStrileIdx < m_tileOffsets.size()
1262 15921 : ? m_tileOffsets[static_cast<size_t>(curStrileIdx)]
1263 7954 : : curStrileIdx < m_tileOffsets64.size()
1264 7954 : ? m_tileOffsets64[static_cast<size_t>(curStrileIdx)]
1265 7954 : : m_image->strileOffset(curStrileIdx, ok);
1266 7967 : 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 7925 : curStrileIdx < m_tileByteCounts.size()
1273 7925 : ? m_tileByteCounts[static_cast<size_t>(curStrileIdx)]
1274 7912 : : m_image->strileByteCount(curStrileIdx, ok);
1275 7925 : 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 7907 : size = static_cast<size_t>(size64);
1290 : // Avoid doing non-sensical memory allocations
1291 7907 : constexpr size_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1024 * 1024;
1292 7935 : 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 8003 : const GDALDataType eNativeDT = papoBands[0]->GetRasterDataType();
1302 : int nBlockXSize, nBlockYSize;
1303 8003 : papoBands[0]->GetBlockSize(&nBlockXSize, &nBlockYSize);
1304 : const int nBlockActualXSize =
1305 8003 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
1306 : const int nBlockActualYSize =
1307 8003 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
1308 :
1309 : // Sparse block?
1310 8003 : if ((curStrileIdx != tlsState.m_curStrileIdx && size == 0) ||
1311 7985 : (curStrileIdx == tlsState.m_curStrileIdx &&
1312 124 : 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 7983 : GByte *pabyDecompressedStrile = nullptr;
1337 : const size_t nNativeDTSize =
1338 7983 : 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 7983 : };
1349 :
1350 7983 : if (curStrileIdx != tlsState.m_curStrileIdx)
1351 : {
1352 7861 : std::vector<GByte> &bufferForOneBitExpansion =
1353 : tlsState.m_bufferForOneBitExpansion;
1354 :
1355 : // Overflow in multiplication checked in Open() method
1356 7861 : const int nComponentsPerPixel = bSeparate ? 1 : nBands;
1357 : const int nActualLineCount =
1358 7861 : m_image->isTiled() ? nBlockYSize : nBlockActualYSize;
1359 7861 : const size_t nActualPixelCount =
1360 7861 : static_cast<size_t>(nActualLineCount) * nBlockXSize;
1361 7861 : const int nLineSizeBytes = m_image->bitsPerSample() == 1
1362 7861 : ? cpl::div_round_up(nBlockXSize, 8)
1363 7861 : : nBlockXSize;
1364 7861 : const size_t nActualUncompressedSize =
1365 7861 : nNativeDTSize * static_cast<size_t>(nActualLineCount) *
1366 7861 : nLineSizeBytes * nComponentsPerPixel;
1367 :
1368 : const bool bCanDecompressDirectlyIntoOutputBuffer =
1369 7232 : pabyBlockData != nullptr && m_image->bitsPerSample() != 1 &&
1370 148 : eBufType == eNativeDT &&
1371 : nPixelSpace ==
1372 148 : static_cast<GSpacing>(nNativeDTSize) * nComponentsPerPixel &&
1373 3 : ((nActualLineCount == 1 && !m_image->isTiled()) ||
1374 59 : nLineSpace == nPixelSpace * nBlockXSize) &&
1375 15149 : (!m_image->isTiled() || nBlockYOff + nBlockYSize <= nRasterYSize) &&
1376 24 : ((bSeparate && nBandCount == 1) ||
1377 32 : (!bSeparate && nBandCount == nBands &&
1378 32 : (nBands == 1 ||
1379 48 : (nBandSpace == static_cast<GSpacing>(nNativeDTSize) &&
1380 24 : IsContiguousBandMap()))));
1381 :
1382 7861 : if (bCanDecompressDirectlyIntoOutputBuffer)
1383 : {
1384 : // Use directly output buffer to decompress strile
1385 56 : pabyDecompressedStrile = pabyBlockData;
1386 : }
1387 : else
1388 : {
1389 : // Allocate buffer for decompressed strile
1390 7805 : std::vector<GByte> &abyDecompressedStrile =
1391 : tlsState.m_decompressedBuffer;
1392 7805 : if (abyDecompressedStrile.empty())
1393 : {
1394 884 : const size_t nMaxUncompressedSize = nNativeDTSize *
1395 884 : nBlockXSize * nBlockYSize *
1396 884 : nComponentsPerPixel;
1397 : try
1398 : {
1399 884 : abyDecompressedStrile.resize(nMaxUncompressedSize);
1400 884 : if (m_image->bitsPerSample() == 1)
1401 379 : bufferForOneBitExpansion.resize(nMaxUncompressedSize);
1402 : }
1403 0 : catch (const std::exception &)
1404 : {
1405 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1406 : "Out of memory allocating temporary buffer");
1407 0 : return false;
1408 : }
1409 : }
1410 7805 : pabyDecompressedStrile = abyDecompressedStrile.data();
1411 : }
1412 :
1413 7861 : if (m_image->compression() != LIBERTIFF_NS::Compression::None)
1414 : {
1415 7025 : std::vector<GByte> &abyCompressedStrile =
1416 : tlsState.m_compressedBuffer;
1417 7025 : if (size > 128 && size / 16 > nActualUncompressedSize)
1418 : {
1419 0 : CPLError(CE_Failure, CPLE_AppDefined,
1420 : "Compressed strile size is much larger than "
1421 : "uncompressed size");
1422 16 : return false;
1423 : }
1424 7025 : if (abyCompressedStrile.size() < size + m_jpegTables.size())
1425 : {
1426 : try
1427 : {
1428 655 : abyCompressedStrile.resize(size + m_jpegTables.size());
1429 : }
1430 0 : catch (const std::exception &)
1431 : {
1432 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
1433 : "Out of memory allocating temporary buffer");
1434 0 : return false;
1435 : }
1436 : }
1437 :
1438 7025 : bool ok = true;
1439 14050 : m_image->readContext()->read(offset, size,
1440 7025 : abyCompressedStrile.data(), ok);
1441 7025 : if (!ok)
1442 : {
1443 0 : CPLError(CE_Failure, CPLE_FileIO,
1444 : "Cannot read strile from disk");
1445 0 : return false;
1446 : }
1447 :
1448 7025 : if (!tlsState.m_tiff.tif_decodestrip)
1449 : {
1450 6048 : if (m_image->compression() == LIBERTIFF_NS::Compression::LZW)
1451 : {
1452 74 : TIFFInitLZW(&tlsState.m_tiff, m_image->compression());
1453 : }
1454 5974 : else if (m_image->compression() ==
1455 : LIBERTIFF_NS::Compression::PackBits)
1456 : {
1457 7 : TIFFInitPackBits(&tlsState.m_tiff, m_image->compression());
1458 : }
1459 : #ifdef LERC_SUPPORT
1460 5967 : else if (m_image->compression() ==
1461 : LIBERTIFF_NS::Compression::LERC)
1462 : {
1463 71 : TIFFInitLERC(&tlsState.m_tiff, m_image->compression());
1464 71 : LERCState *sp =
1465 : reinterpret_cast<LERCState *>(tlsState.m_tiff.tif_data);
1466 71 : sp->lerc_version = m_lercVersion;
1467 71 : sp->additional_compression = m_lercAdditionalCompression;
1468 : }
1469 : #endif
1470 :
1471 6048 : if (tlsState.m_tiff.tif_decodestrip)
1472 : {
1473 152 : tlsState.m_tiff.tif_name =
1474 152 : const_cast<char *>(GetDescription());
1475 152 : tlsState.m_tiff.tif_dir.td_sampleformat =
1476 152 : static_cast<uint16_t>(m_image->sampleFormat());
1477 152 : tlsState.m_tiff.tif_dir.td_bitspersample =
1478 152 : static_cast<uint16_t>(m_image->bitsPerSample());
1479 152 : if (m_image->isTiled())
1480 : {
1481 68 : tlsState.m_tiff.tif_flags = TIFF_ISTILED;
1482 68 : tlsState.m_tiff.tif_dir.td_tilewidth =
1483 68 : m_image->tileWidth();
1484 68 : tlsState.m_tiff.tif_dir.td_tilelength =
1485 68 : m_image->tileHeight();
1486 : }
1487 : else
1488 : {
1489 84 : tlsState.m_tiff.tif_dir.td_imagewidth =
1490 84 : m_image->width();
1491 84 : tlsState.m_tiff.tif_dir.td_imagelength =
1492 84 : m_image->height();
1493 84 : tlsState.m_tiff.tif_dir.td_rowsperstrip =
1494 84 : m_image->rowsPerStripSanitized();
1495 : }
1496 152 : tlsState.m_tiff.tif_dir.td_samplesperpixel =
1497 152 : static_cast<uint16_t>(m_image->samplesPerPixel());
1498 152 : tlsState.m_tiff.tif_dir.td_planarconfig =
1499 152 : static_cast<uint16_t>(m_image->planarConfiguration());
1500 152 : if (m_extraSamples.size() < 65536)
1501 : {
1502 152 : tlsState.m_tiff.tif_dir.td_extrasamples =
1503 152 : static_cast<uint16_t>(m_extraSamples.size());
1504 152 : tlsState.m_tiff.tif_dir.td_sampleinfo =
1505 152 : const_cast<uint16_t *>(m_extraSamples.data());
1506 : }
1507 : }
1508 : }
1509 :
1510 7025 : if (tlsState.m_tiff.tif_decodestrip)
1511 : {
1512 1129 : tlsState.m_tiff.tif_row = nBlockYOff * nBlockYSize;
1513 1129 : tlsState.m_tiff.tif_rawcc = size;
1514 1129 : tlsState.m_tiff.tif_rawdata = abyCompressedStrile.data();
1515 1129 : tlsState.m_tiff.tif_rawcp = tlsState.m_tiff.tif_rawdata;
1516 3376 : if ((tlsState.m_tiff.tif_predecode &&
1517 2258 : tlsState.m_tiff.tif_predecode(&tlsState.m_tiff, 0) == 0) ||
1518 1129 : tlsState.m_tiff.tif_decodestrip(
1519 : &tlsState.m_tiff, pabyDecompressedStrile,
1520 : nActualUncompressedSize, 0) == 0)
1521 : {
1522 14 : CPLError(CE_Failure, CPLE_AppDefined,
1523 : "Decompression failed");
1524 14 : return false;
1525 : }
1526 : }
1527 5896 : else if (m_image->compression() ==
1528 5623 : LIBERTIFF_NS::Compression::JPEG ||
1529 5623 : m_image->compression() ==
1530 5529 : LIBERTIFF_NS::Compression::WEBP ||
1531 17048 : m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
1532 5370 : m_image->compression() ==
1533 : LIBERTIFF_NS::Compression::JXL_DNG_1_7)
1534 : {
1535 526 : size_t blobSize = size;
1536 : const char *drvName =
1537 526 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG
1538 779 : ? "JPEG"
1539 253 : : m_image->compression() == LIBERTIFF_NS::Compression::WEBP
1540 253 : ? "WEBP"
1541 526 : : "JPEGXL";
1542 799 : if (m_image->compression() == LIBERTIFF_NS::Compression::JPEG &&
1543 799 : size > 2 && !m_jpegTables.empty())
1544 : {
1545 : // Insert JPEG tables into JPEG blob
1546 813 : memmove(abyCompressedStrile.data() + 2 +
1547 271 : m_jpegTables.size(),
1548 271 : abyCompressedStrile.data() + 2, size - 2);
1549 271 : memcpy(abyCompressedStrile.data() + 2, m_jpegTables.data(),
1550 : m_jpegTables.size());
1551 271 : blobSize += m_jpegTables.size();
1552 : }
1553 : const std::string osTmpFilename = VSIMemGenerateHiddenFilename(
1554 1052 : std::string("tmp.").append(drvName).c_str());
1555 526 : VSIFCloseL(VSIFileFromMemBuffer(
1556 : osTmpFilename.c_str(), abyCompressedStrile.data(), blobSize,
1557 : /* bTakeOwnership = */ false));
1558 526 : const char *const apszAllowedDrivers[] = {drvName, nullptr};
1559 :
1560 : CPLConfigOptionSetter oJPEGtoRGBSetter(
1561 : "GDAL_JPEG_TO_RGB",
1562 799 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG &&
1563 318 : m_image->samplesPerPixel() == 4 &&
1564 45 : m_image->planarConfiguration() ==
1565 : LIBERTIFF_NS::PlanarConfiguration::Contiguous
1566 : ? "NO"
1567 : : "YES",
1568 799 : false);
1569 :
1570 526 : const char *const apszOpenOptions[] = {
1571 620 : m_image->compression() == LIBERTIFF_NS::Compression::WEBP &&
1572 : nComponentsPerPixel == 4
1573 620 : ? "@FORCE_4BANDS=YES"
1574 : : nullptr,
1575 526 : nullptr};
1576 :
1577 : auto poTmpDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1578 : osTmpFilename.c_str(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
1579 526 : apszAllowedDrivers, apszOpenOptions, nullptr));
1580 526 : VSIUnlink(osTmpFilename.c_str());
1581 526 : if (!poTmpDS)
1582 : {
1583 0 : CPLError(CE_Failure, CPLE_AppDefined, "Not a %s blob",
1584 : drvName);
1585 0 : return false;
1586 : }
1587 526 : if (poTmpDS->GetRasterCount() != nComponentsPerPixel ||
1588 1052 : poTmpDS->GetRasterXSize() != nBlockXSize ||
1589 526 : poTmpDS->GetRasterYSize() != nActualLineCount)
1590 : {
1591 0 : CPLError(CE_Failure, CPLE_AppDefined,
1592 : "%s blob has no expected dimensions (%dx%d "
1593 : "whereas %dx%d expected) or band count (%d "
1594 : "whereas %d expected)",
1595 : drvName, poTmpDS->GetRasterXSize(),
1596 : poTmpDS->GetRasterYSize(), nBlockXSize,
1597 : nActualLineCount, poTmpDS->GetRasterCount(),
1598 : nComponentsPerPixel);
1599 0 : return false;
1600 : }
1601 : GDALRasterIOExtraArg sExtraArg;
1602 526 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1603 1052 : if (poTmpDS->RasterIO(
1604 : GF_Read, 0, 0, poTmpDS->GetRasterXSize(),
1605 : poTmpDS->GetRasterYSize(), pabyDecompressedStrile,
1606 : poTmpDS->GetRasterXSize(), poTmpDS->GetRasterYSize(),
1607 : eNativeDT, poTmpDS->GetRasterCount(), nullptr,
1608 526 : nNativeDTSize * nComponentsPerPixel,
1609 526 : nNativeDTSize * nComponentsPerPixel * nBlockXSize,
1610 526 : nNativeDTSize, &sExtraArg) != CE_None)
1611 : {
1612 0 : CPLError(CE_Failure, CPLE_AppDefined,
1613 : "Decompression failed");
1614 0 : return false;
1615 : }
1616 : }
1617 : else
1618 : {
1619 5370 : CPLAssert(m_decompressor);
1620 5370 : void *output_data = pabyDecompressedStrile;
1621 5370 : size_t output_size = nActualUncompressedSize;
1622 5370 : if (!m_decompressor->pfnFunc(
1623 5370 : abyCompressedStrile.data(), size, &output_data,
1624 16108 : &output_size, nullptr, m_decompressor->user_data) ||
1625 5368 : output_size != nActualUncompressedSize)
1626 : {
1627 2 : CPLError(CE_Failure, CPLE_AppDefined,
1628 : "Decompression failed");
1629 2 : return false;
1630 : }
1631 5368 : CPLAssert(output_data == pabyDecompressedStrile);
1632 : }
1633 : }
1634 : else
1635 : {
1636 836 : if (size != nActualUncompressedSize)
1637 : {
1638 17 : CPLError(CE_Failure, CPLE_NotSupported,
1639 : "Strile size != expected size");
1640 34 : return false;
1641 : }
1642 :
1643 819 : bool ok = true;
1644 819 : m_image->readContext()->read(offset, size, pabyDecompressedStrile,
1645 : ok);
1646 819 : if (!ok)
1647 : {
1648 17 : CPLError(CE_Failure, CPLE_FileIO,
1649 : "Cannot read strile from disk");
1650 17 : return false;
1651 : }
1652 : }
1653 :
1654 7811 : if (m_image->bitsPerSample() == 1)
1655 : {
1656 352 : std::vector<GByte> &abyDecompressedStrile =
1657 : tlsState.m_decompressedBuffer;
1658 352 : const GByte *CPL_RESTRICT pabySrc = abyDecompressedStrile.data();
1659 352 : GByte *CPL_RESTRICT pabyDst = bufferForOneBitExpansion.data();
1660 352 : const int nSrcInc = cpl::div_round_up(nBlockXSize, 8);
1661 1460 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1662 : {
1663 1108 : if (m_bExpand1To255)
1664 : {
1665 662 : GDALExpandPackedBitsToByteAt0Or255(pabySrc, pabyDst,
1666 : nBlockXSize);
1667 : }
1668 : else
1669 : {
1670 446 : GDALExpandPackedBitsToByteAt0Or1(pabySrc, pabyDst,
1671 : nBlockXSize);
1672 : }
1673 1108 : pabySrc += nSrcInc;
1674 1108 : pabyDst += nBlockXSize;
1675 : }
1676 :
1677 352 : std::swap(abyDecompressedStrile, bufferForOneBitExpansion);
1678 352 : pabyDecompressedStrile = abyDecompressedStrile.data();
1679 : }
1680 14464 : else if (m_image->compression() == LIBERTIFF_NS::Compression::None ||
1681 14464 : m_image->compression() == LIBERTIFF_NS::Compression::LZW ||
1682 6381 : m_decompressor)
1683 : {
1684 6493 : if (m_image->readContext()->mustByteSwap() &&
1685 51 : m_image->predictor() != 3)
1686 : {
1687 50 : if (GDALDataTypeIsComplex(eNativeDT))
1688 : {
1689 1 : GDALSwapWordsEx(pabyDecompressedStrile,
1690 : static_cast<int>(nNativeDTSize) / 2,
1691 1 : nActualPixelCount * nComponentsPerPixel * 2,
1692 : static_cast<int>(nNativeDTSize) / 2);
1693 : }
1694 : else
1695 : {
1696 49 : GDALSwapWordsEx(pabyDecompressedStrile,
1697 : static_cast<int>(nNativeDTSize),
1698 49 : nActualPixelCount * nComponentsPerPixel,
1699 : static_cast<int>(nNativeDTSize));
1700 : }
1701 : }
1702 :
1703 6442 : if (m_image->predictor() == 2)
1704 : {
1705 4893 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1706 : {
1707 4442 : auto ptr = pabyDecompressedStrile + nNativeDTSize * iY *
1708 4442 : nBlockXSize *
1709 4442 : nComponentsPerPixel;
1710 4442 : if (nNativeDTSize == sizeof(uint8_t))
1711 : {
1712 1192 : HorizPredictorDecode<uint8_t>(ptr, nBlockXSize,
1713 : nComponentsPerPixel);
1714 : }
1715 3250 : else if (nNativeDTSize == sizeof(uint16_t))
1716 : {
1717 1370 : HorizPredictorDecode<uint16_t>(ptr, nBlockXSize,
1718 : nComponentsPerPixel);
1719 : }
1720 1880 : else if (nNativeDTSize == sizeof(uint32_t))
1721 : {
1722 940 : HorizPredictorDecode<uint32_t>(ptr, nBlockXSize,
1723 : nComponentsPerPixel);
1724 : }
1725 940 : else if (nNativeDTSize == sizeof(uint64_t))
1726 : {
1727 940 : HorizPredictorDecode<uint64_t>(ptr, nBlockXSize,
1728 : nComponentsPerPixel);
1729 : }
1730 : else
1731 : {
1732 0 : CPLAssert(false);
1733 : }
1734 : }
1735 : }
1736 5991 : else if (m_image->predictor() == 3)
1737 : {
1738 63 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1739 : {
1740 60 : auto ptr = pabyDecompressedStrile + nNativeDTSize * iY *
1741 60 : nBlockXSize *
1742 60 : nComponentsPerPixel;
1743 60 : bool ok = true;
1744 60 : if (nNativeDTSize == sizeof(uint32_t))
1745 : {
1746 40 : ok = FloatingPointHorizPredictorDecode<uint32_t>(
1747 : tlsState
1748 40 : .m_floatingPointHorizPredictorDecodeTmpBuffer,
1749 : ptr, nBlockXSize, nComponentsPerPixel);
1750 : }
1751 20 : else if (nNativeDTSize == sizeof(uint64_t))
1752 : {
1753 20 : ok = FloatingPointHorizPredictorDecode<uint64_t>(
1754 : tlsState
1755 20 : .m_floatingPointHorizPredictorDecodeTmpBuffer,
1756 : ptr, nBlockXSize, nComponentsPerPixel);
1757 : }
1758 : else
1759 : {
1760 0 : CPLAssert(false);
1761 : }
1762 60 : if (!ok)
1763 0 : return false;
1764 : }
1765 : }
1766 : }
1767 :
1768 7811 : if (bCanDecompressDirectlyIntoOutputBuffer)
1769 55 : return true;
1770 : }
1771 : else
1772 : {
1773 122 : pabyDecompressedStrile = tlsState.m_decompressedBuffer.data();
1774 : }
1775 :
1776 : // Copy decompress strile into user buffer
1777 7878 : if (pabyBlockData)
1778 : {
1779 7289 : const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1780 1629 : if (!bSeparate && nBands > 1 && nBands == nBandCount &&
1781 8918 : nBufTypeSize == nPixelSpace && IsContiguousBandMap())
1782 : {
1783 : // Optimization: reading a pixel-interleaved buffer into a band-interleaved buffer
1784 26 : std::vector<void *> &apabyDest = tlsState.m_apabyDest;
1785 26 : apabyDest.resize(nBands);
1786 130 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1787 : {
1788 104 : apabyDest[iBand] = pabyBlockData + iBand * nBandSpace;
1789 : }
1790 326 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1791 : {
1792 300 : if (iY > 0)
1793 : {
1794 1370 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1795 : {
1796 1096 : apabyDest[iBand] =
1797 1096 : static_cast<GByte *>(apabyDest[iBand]) + nLineSpace;
1798 : }
1799 : }
1800 300 : GDALDeinterleave(pabyDecompressedStrile +
1801 300 : nNativeDTSize * iY * nBlockXSize * nBands,
1802 300 : eNativeDT, nBands, apabyDest.data(), eBufType,
1803 : nBlockActualXSize);
1804 : }
1805 : }
1806 1603 : else if (!bSeparate && nBands == nBandCount &&
1807 132 : nBufTypeSize == nBandSpace &&
1808 8868 : nPixelSpace == nBandSpace * nBandCount &&
1809 2 : IsContiguousBandMap())
1810 : {
1811 : // Optimization reading a pixel-interleaved buffer into a pixel-interleaved buffer
1812 14 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1813 : {
1814 12 : GDALCopyWords64(
1815 12 : pabyDecompressedStrile +
1816 12 : nNativeDTSize * iY * nBlockXSize * nBands,
1817 : eNativeDT, static_cast<int>(nNativeDTSize),
1818 12 : pabyBlockData + iY * nLineSpace, eBufType, nBufTypeSize,
1819 : static_cast<GPtrDiff_t>(
1820 12 : static_cast<GIntBig>(nBlockActualXSize) * nBands));
1821 : }
1822 : }
1823 1601 : else if (!bSeparate && nBands == nBandCount &&
1824 130 : nBufTypeSize == nBandSpace &&
1825 4 : eBufType == papoBands[0]->GetRasterDataType() &&
1826 4 : nPixelSpace > nBandSpace * nBandCount &&
1827 8866 : nLineSpace >= nPixelSpace * nBlockXSize &&
1828 4 : IsContiguousBandMap())
1829 : {
1830 : // Optimization for typically reading a pixel-interleaved RGB buffer
1831 : // into a pixel-interleaved RGBA buffer
1832 204 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1833 : {
1834 200 : GByte *const pabyDst = pabyBlockData + iY * nLineSpace;
1835 200 : const GByte *const pabySrc =
1836 : pabyDecompressedStrile +
1837 200 : nNativeDTSize * iY * nBlockXSize * nBands;
1838 200 : if (nBands == 3 && nPixelSpace == 4 && nBufTypeSize == 1)
1839 : {
1840 100 : for (size_t iX = 0;
1841 5100 : iX < static_cast<size_t>(nBlockActualXSize); ++iX)
1842 : {
1843 5000 : memcpy(pabyDst + iX * 4, pabySrc + iX * 3, 3);
1844 100 : }
1845 : }
1846 : else
1847 : {
1848 100 : for (size_t iX = 0;
1849 5100 : iX < static_cast<size_t>(nBlockActualXSize); ++iX)
1850 : {
1851 5000 : memcpy(pabyDst + iX * nPixelSpace,
1852 5000 : pabySrc + iX * nBands, nBands * nBufTypeSize);
1853 : }
1854 : }
1855 : }
1856 : }
1857 : else
1858 : {
1859 : // General case
1860 7257 : const int nSrcPixels = bSeparate ? 1 : nBands;
1861 14645 : for (int iBand = 0; iBand < nBandCount; ++iBand)
1862 : {
1863 7388 : const int iSrcBand = bSeparate ? 0 : panBandMap[iBand] - 1;
1864 187665 : for (int iY = 0; iY < nBlockActualYSize; ++iY)
1865 : {
1866 180277 : GDALCopyWords64(
1867 180277 : pabyDecompressedStrile +
1868 180277 : nNativeDTSize * (static_cast<size_t>(iY) *
1869 180277 : nBlockXSize * nSrcPixels +
1870 180277 : iSrcBand),
1871 180277 : eNativeDT, static_cast<int>(nSrcPixels * nNativeDTSize),
1872 180277 : pabyBlockData + iBand * nBandSpace + iY * nLineSpace,
1873 : eBufType, static_cast<int>(nPixelSpace),
1874 : nBlockActualXSize);
1875 : }
1876 : }
1877 : }
1878 : }
1879 :
1880 7878 : tlsState.m_curStrileIdx = curStrileIdx;
1881 7878 : tlsState.m_curStrileMissing = false;
1882 :
1883 7878 : return true;
1884 : }
1885 :
1886 : /************************************************************************/
1887 : /* Identify() */
1888 : /************************************************************************/
1889 :
1890 73355 : /* static */ int LIBERTIFFDataset::Identify(GDALOpenInfo *poOpenInfo)
1891 : {
1892 145790 : return poOpenInfo->eAccess != GA_Update &&
1893 72435 : (STARTS_WITH_CI(poOpenInfo->pszFilename, "GTIFF_DIR:") ||
1894 72409 : (poOpenInfo->fpL && poOpenInfo->nHeaderBytes >= 8 &&
1895 12655 : (((poOpenInfo->pabyHeader[0] == 'I' &&
1896 2398 : poOpenInfo->pabyHeader[1] == 'I') &&
1897 2373 : ((poOpenInfo->pabyHeader[2] == 0x2A &&
1898 2336 : poOpenInfo->pabyHeader[3] == 0) ||
1899 41 : (poOpenInfo->pabyHeader[2] == 0x2B &&
1900 32 : poOpenInfo->pabyHeader[3] == 0))) ||
1901 10291 : ((poOpenInfo->pabyHeader[0] == 'M' &&
1902 116 : poOpenInfo->pabyHeader[1] == 'M') &&
1903 70 : ((poOpenInfo->pabyHeader[2] == 0 &&
1904 70 : poOpenInfo->pabyHeader[3] == 0x2A) ||
1905 26 : (poOpenInfo->pabyHeader[2] == 0 &&
1906 73381 : poOpenInfo->pabyHeader[3] == 0x2B))))));
1907 : }
1908 :
1909 : /************************************************************************/
1910 : /* ComputeGDALDataType() */
1911 : /************************************************************************/
1912 :
1913 1028 : GDALDataType LIBERTIFFDataset::ComputeGDALDataType() const
1914 : {
1915 :
1916 1028 : GDALDataType eDT = GDT_Unknown;
1917 :
1918 1028 : switch (m_image->sampleFormat())
1919 : {
1920 946 : case LIBERTIFF_NS::SampleFormat::UnsignedInt:
1921 : {
1922 1430 : if (m_image->bitsPerSample() == 1 &&
1923 484 : (m_image->samplesPerPixel() == 1 ||
1924 10 : m_image->planarConfiguration() ==
1925 : LIBERTIFF_NS::PlanarConfiguration::Separate))
1926 : {
1927 464 : eDT = GDT_UInt8;
1928 : }
1929 482 : else if (m_image->bitsPerSample() == 8)
1930 401 : eDT = GDT_UInt8;
1931 81 : else if (m_image->bitsPerSample() == 16)
1932 10 : eDT = GDT_UInt16;
1933 71 : else if (m_image->bitsPerSample() == 32)
1934 7 : eDT = GDT_UInt32;
1935 64 : else if (m_image->bitsPerSample() == 64)
1936 7 : eDT = GDT_UInt64;
1937 946 : break;
1938 : }
1939 :
1940 19 : case LIBERTIFF_NS::SampleFormat::SignedInt:
1941 : {
1942 19 : if (m_image->bitsPerSample() == 8)
1943 1 : eDT = GDT_Int8;
1944 18 : else if (m_image->bitsPerSample() == 16)
1945 13 : eDT = GDT_Int16;
1946 5 : else if (m_image->bitsPerSample() == 32)
1947 2 : eDT = GDT_Int32;
1948 3 : else if (m_image->bitsPerSample() == 64)
1949 2 : eDT = GDT_Int64;
1950 19 : break;
1951 : }
1952 :
1953 27 : case LIBERTIFF_NS::SampleFormat::IEEEFP:
1954 : {
1955 27 : if (m_image->bitsPerSample() == 32)
1956 19 : eDT = GDT_Float32;
1957 8 : else if (m_image->bitsPerSample() == 64)
1958 6 : eDT = GDT_Float64;
1959 27 : break;
1960 : }
1961 :
1962 7 : case LIBERTIFF_NS::SampleFormat::ComplexInt:
1963 : {
1964 7 : if (m_image->bitsPerSample() == 32)
1965 3 : eDT = GDT_CInt16;
1966 4 : else if (m_image->bitsPerSample() == 64)
1967 4 : eDT = GDT_CInt32;
1968 7 : break;
1969 : }
1970 :
1971 6 : case LIBERTIFF_NS::SampleFormat::ComplexIEEEFP:
1972 : {
1973 6 : if (m_image->bitsPerSample() == 64)
1974 4 : eDT = GDT_CFloat32;
1975 2 : else if (m_image->bitsPerSample() == 128)
1976 2 : eDT = GDT_CFloat64;
1977 6 : break;
1978 : }
1979 :
1980 23 : default:
1981 23 : break;
1982 : }
1983 :
1984 1032 : if (m_image->bitsPerSample() == 12 &&
1985 4 : m_image->compression() == LIBERTIFF_NS::Compression::JPEG)
1986 : {
1987 2 : auto poJPEGDrv = GetGDALDriverManager()->GetDriverByName("JPEG");
1988 2 : if (poJPEGDrv)
1989 : {
1990 : const char *pszJPEGDataTypes =
1991 2 : poJPEGDrv->GetMetadataItem(GDAL_DMD_CREATIONDATATYPES);
1992 2 : if (pszJPEGDataTypes && strstr(pszJPEGDataTypes, "UInt16"))
1993 2 : eDT = GDT_UInt16;
1994 : }
1995 : }
1996 :
1997 1028 : return eDT;
1998 : }
1999 :
2000 : /************************************************************************/
2001 : /* ProcessCompressionMethod() */
2002 : /************************************************************************/
2003 :
2004 1084 : bool LIBERTIFFDataset::ProcessCompressionMethod()
2005 : {
2006 1084 : if (m_image->compression() == LIBERTIFF_NS::Compression::PackBits)
2007 : {
2008 7 : GDALDataset::SetMetadataItem("COMPRESSION", "PACKBITS",
2009 : "IMAGE_STRUCTURE");
2010 : }
2011 2125 : else if (m_image->compression() == LIBERTIFF_NS::Compression::Deflate ||
2012 1048 : m_image->compression() == LIBERTIFF_NS::Compression::LegacyDeflate)
2013 : {
2014 44 : m_decompressor = CPLGetDecompressor("zlib");
2015 44 : GDALDataset::SetMetadataItem("COMPRESSION", "DEFLATE",
2016 : "IMAGE_STRUCTURE");
2017 : }
2018 1033 : else if (m_image->compression() == LIBERTIFF_NS::Compression::ZSTD)
2019 : {
2020 17 : m_decompressor = CPLGetDecompressor("zstd");
2021 17 : if (!m_decompressor)
2022 : {
2023 0 : ReportError(CE_Failure, CPLE_NotSupported,
2024 : "Compression = ZSTD unhandled because GDAL "
2025 : "has not been built against libzstd");
2026 0 : return false;
2027 : }
2028 17 : GDALDataset::SetMetadataItem("COMPRESSION", "ZSTD", "IMAGE_STRUCTURE");
2029 : }
2030 1016 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LZMA)
2031 : {
2032 14 : m_decompressor = CPLGetDecompressor("lzma");
2033 14 : if (!m_decompressor)
2034 : {
2035 0 : ReportError(CE_Failure, CPLE_NotSupported,
2036 : "Compression = LZMA unhandled because GDAL "
2037 : "has not been built against liblzma");
2038 0 : return false;
2039 : }
2040 14 : GDALDataset::SetMetadataItem("COMPRESSION", "LZMA", "IMAGE_STRUCTURE");
2041 : }
2042 1002 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LZW)
2043 : {
2044 71 : GDALDataset::SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
2045 : }
2046 931 : else if (m_image->compression() == LIBERTIFF_NS::Compression::JPEG)
2047 : {
2048 40 : if (!GDALGetDriverByName("JPEG"))
2049 : {
2050 0 : ReportError(
2051 : CE_Failure, CPLE_NotSupported,
2052 : "Compression = JPEG not supported because JPEG driver missing");
2053 0 : return false;
2054 : }
2055 40 : if (m_image->photometricInterpretation() ==
2056 49 : LIBERTIFF_NS::PhotometricInterpretation::YCbCr &&
2057 9 : m_image->samplesPerPixel() == 3)
2058 : {
2059 9 : GDALDataset::SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
2060 : "IMAGE_STRUCTURE");
2061 9 : GDALDataset::SetMetadataItem("COMPRESSION", "YCbCr JPEG",
2062 : "IMAGE_STRUCTURE");
2063 : }
2064 : else
2065 : {
2066 31 : GDALDataset::SetMetadataItem("COMPRESSION", "JPEG",
2067 : "IMAGE_STRUCTURE");
2068 : }
2069 64 : if (m_image->samplesPerPixel() != 1 &&
2070 27 : m_image->samplesPerPixel() != 3 &&
2071 67 : m_image->samplesPerPixel() != 4 &&
2072 0 : m_image->planarConfiguration() ==
2073 : LIBERTIFF_NS::PlanarConfiguration::Contiguous)
2074 : {
2075 0 : ReportError(CE_Failure, CPLE_NotSupported,
2076 : "Compression = JPEG not supported when samplesPerPixel "
2077 : "!= 1, 3 or 4 and planarConfiguration = Contiguous");
2078 0 : return false;
2079 : }
2080 :
2081 : const auto psJPEGTablesTag =
2082 40 : m_image->tag(LIBERTIFF_NS::TagCode::JPEGTables);
2083 40 : if (psJPEGTablesTag &&
2084 37 : psJPEGTablesTag->type == LIBERTIFF_NS::TagType::Undefined &&
2085 37 : psJPEGTablesTag->count > 4 &&
2086 37 : !psJPEGTablesTag->invalid_value_offset &&
2087 37 : psJPEGTablesTag->count < 65536)
2088 : {
2089 37 : bool ok = true;
2090 : m_jpegTablesOri =
2091 37 : m_image->readTagAsVector<uint8_t>(*psJPEGTablesTag, ok);
2092 74 : if (m_jpegTablesOri.size() >= 4 && m_jpegTablesOri[0] == 0xff &&
2093 37 : m_jpegTablesOri[1] == 0xd8 &&
2094 111 : m_jpegTablesOri[m_jpegTablesOri.size() - 2] == 0xff &&
2095 37 : m_jpegTablesOri.back() == 0xd9)
2096 : {
2097 : m_jpegTables.insert(
2098 0 : m_jpegTables.end(), m_jpegTablesOri.data() + 2,
2099 37 : m_jpegTablesOri.data() + m_jpegTablesOri.size() - 2);
2100 : }
2101 : }
2102 :
2103 43 : if (m_image->samplesPerPixel() == 4 &&
2104 3 : m_image->planarConfiguration() ==
2105 : LIBERTIFF_NS::PlanarConfiguration::Contiguous)
2106 : {
2107 2 : const GByte abyAdobeAPP14RGB[] = {
2108 : 0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64, 0x6F, 0x62,
2109 : 0x65, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00};
2110 0 : m_jpegTables.insert(m_jpegTables.end(), abyAdobeAPP14RGB,
2111 2 : abyAdobeAPP14RGB + sizeof(abyAdobeAPP14RGB));
2112 : }
2113 : }
2114 891 : else if (m_image->compression() == LIBERTIFF_NS::Compression::WEBP)
2115 : {
2116 12 : if (!GDALGetDriverByName("WEBP"))
2117 : {
2118 0 : ReportError(
2119 : CE_Failure, CPLE_NotSupported,
2120 : "Compression = WEBP not supported because WEBP driver missing");
2121 0 : return false;
2122 : }
2123 12 : GDALDataset::SetMetadataItem("COMPRESSION", "WEBP", "IMAGE_STRUCTURE");
2124 : }
2125 1744 : else if (m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
2126 865 : m_image->compression() == LIBERTIFF_NS::Compression::JXL_DNG_1_7)
2127 : {
2128 14 : if (!GDALGetDriverByName("JPEGXL"))
2129 : {
2130 0 : ReportError(
2131 : CE_Failure, CPLE_NotSupported,
2132 : "Compression = JXL not supported because JXL driver missing");
2133 0 : return false;
2134 : }
2135 14 : GDALDataset::SetMetadataItem("COMPRESSION", "JXL", "IMAGE_STRUCTURE");
2136 : }
2137 865 : else if (m_image->compression() == LIBERTIFF_NS::Compression::LERC)
2138 : {
2139 : #ifndef LERC_SUPPORT
2140 : ReportError(CE_Failure, CPLE_NotSupported,
2141 : "Compression = LERC not supported because GDAL "
2142 : "has not been built against liblerc");
2143 : return false;
2144 : #else
2145 : const auto *psLercParametersTag =
2146 43 : m_image->tag(LIBERTIFF_NS::TagCode::LERCParameters);
2147 43 : if (psLercParametersTag &&
2148 43 : psLercParametersTag->type == LIBERTIFF_NS::TagType::Long &&
2149 43 : psLercParametersTag->count == 2)
2150 : {
2151 43 : bool ok = true;
2152 : const auto lercParameters =
2153 43 : m_image->readTagAsVector<uint32_t>(*psLercParametersTag, ok);
2154 43 : if (!ok || lercParameters.size() != 2)
2155 : {
2156 0 : ReportError(CE_Failure, CPLE_NotSupported,
2157 : "Tag LERCParameters is invalid");
2158 0 : return false;
2159 : }
2160 43 : m_lercVersion = lercParameters[0];
2161 43 : m_lercAdditionalCompression = lercParameters[1];
2162 : #ifndef ZSTD_SUPPORT
2163 : if (m_lercAdditionalCompression == LERC_ADD_COMPRESSION_ZSTD)
2164 : {
2165 : ReportError(
2166 : CE_Failure, CPLE_NotSupported,
2167 : "Compression = LERC_ZSTD not supported because GDAL "
2168 : "has not been built against libzstd");
2169 : return false;
2170 : }
2171 : #endif
2172 : }
2173 :
2174 43 : GDALDataset::SetMetadataItem(
2175 : "COMPRESSION",
2176 43 : m_lercAdditionalCompression == LERC_ADD_COMPRESSION_DEFLATE
2177 : ? "LERC_DEFLATE"
2178 29 : : m_lercAdditionalCompression == LERC_ADD_COMPRESSION_ZSTD
2179 29 : ? "LERC_ZSTD"
2180 : : "LERC",
2181 : "IMAGE_STRUCTURE");
2182 :
2183 43 : if (m_lercVersion == LERC_VERSION_2_4)
2184 : {
2185 43 : GDALDataset::SetMetadataItem("LERC_VERSION", "2.4",
2186 : "IMAGE_STRUCTURE");
2187 : }
2188 : #endif
2189 : }
2190 822 : else if (m_image->compression() != LIBERTIFF_NS::Compression::None)
2191 : {
2192 18 : CPLDebug("LIBERTIFF", "Compression = %s unhandled",
2193 : LIBERTIFF_NS::compressionName(m_image->compression()));
2194 18 : return false;
2195 : }
2196 :
2197 1066 : return true;
2198 : }
2199 :
2200 : /************************************************************************/
2201 : /* Open() */
2202 : /************************************************************************/
2203 :
2204 1205 : bool LIBERTIFFDataset::Open(std::unique_ptr<const LIBERTIFF_NS::Image> image)
2205 : {
2206 1205 : m_image = std::move(image);
2207 :
2208 : // Basic sanity checks
2209 1205 : if (m_image->width() == 0 ||
2210 2337 : m_image->width() > static_cast<uint32_t>(INT_MAX) ||
2211 1168 : m_image->height() == 0 ||
2212 2266 : m_image->height() > static_cast<uint32_t>(INT_MAX) ||
2213 3507 : m_image->samplesPerPixel() == 0 ||
2214 1084 : m_image->samplesPerPixel() > static_cast<uint32_t>(INT_MAX))
2215 : {
2216 121 : CPLDebug("LIBERTIFF", "Invalid width, height, or samplesPerPixel");
2217 121 : return false;
2218 : }
2219 :
2220 1084 : nRasterXSize = static_cast<int>(m_image->width());
2221 1084 : nRasterYSize = static_cast<int>(m_image->height());
2222 1084 : const int l_nBands = static_cast<int>(m_image->samplesPerPixel());
2223 1084 : if (!GDALCheckBandCount(l_nBands, false))
2224 0 : return false;
2225 :
2226 1084 : if (!ProcessCompressionMethod())
2227 18 : return false;
2228 :
2229 : // Compute block size
2230 : int nBlockXSize;
2231 : int nBlockYSize;
2232 1066 : if (m_image->isTiled())
2233 : {
2234 122 : if (m_image->tileWidth() == 0 ||
2235 242 : m_image->tileWidth() > static_cast<uint32_t>(INT_MAX) ||
2236 364 : m_image->tileHeight() == 0 ||
2237 120 : m_image->tileHeight() > static_cast<uint32_t>(INT_MAX))
2238 : {
2239 2 : CPLDebug("LIBERTIFF", "Invalid tileWidth or tileHeight");
2240 2 : return false;
2241 : }
2242 120 : nBlockXSize = static_cast<int>(m_image->tileWidth());
2243 120 : nBlockYSize = static_cast<int>(m_image->tileHeight());
2244 : }
2245 : else
2246 : {
2247 944 : if (m_image->rowsPerStripSanitized() == 0)
2248 : {
2249 36 : CPLDebug("LIBERTIFF", "Invalid rowsPerStrip");
2250 36 : return false;
2251 : }
2252 908 : nBlockXSize = nRasterXSize;
2253 908 : nBlockYSize = static_cast<int>(m_image->rowsPerStripSanitized());
2254 : }
2255 :
2256 1028 : const GDALDataType eDT = ComputeGDALDataType();
2257 1028 : if (eDT == GDT_Unknown)
2258 : {
2259 81 : CPLDebug("LIBERTIFF",
2260 : "BitsPerSample = %u and SampleFormat=%u unhandled",
2261 : m_image->bitsPerSample(), m_image->sampleFormat());
2262 81 : return false;
2263 : }
2264 :
2265 : // Deal with Predictor tag
2266 947 : if (m_image->predictor() == 2)
2267 : {
2268 33 : GDALDataset::SetMetadataItem("PREDICTOR", "2", "IMAGE_STRUCTURE");
2269 : }
2270 914 : else if (m_image->predictor() == 3)
2271 : {
2272 3 : if (eDT != GDT_Float32 && eDT != GDT_Float64)
2273 : {
2274 0 : CPLDebug("LIBERTIFF", "Unhandled predictor=3 with non-float data");
2275 0 : return false;
2276 : }
2277 3 : GDALDataset::SetMetadataItem("PREDICTOR", "3", "IMAGE_STRUCTURE");
2278 : }
2279 911 : else if (m_image->predictor() > 3)
2280 : {
2281 1 : CPLDebug("LIBERTIFF", "Predictor = %u unhandled", m_image->predictor());
2282 1 : return false;
2283 : }
2284 :
2285 : // Deal with PlanarConfiguration tag
2286 946 : if (m_image->planarConfiguration() ==
2287 1811 : LIBERTIFF_NS::PlanarConfiguration::Separate ||
2288 865 : m_image->samplesPerPixel() == 1)
2289 789 : GDALDataset::SetMetadataItem("INTERLEAVE", "BAND", "IMAGE_STRUCTURE");
2290 : else
2291 157 : GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2292 :
2293 946 : const int nNativeDTSize = GDALGetDataTypeSizeBytes(eDT);
2294 946 : const bool bSeparate = m_image->planarConfiguration() ==
2295 946 : LIBERTIFF_NS::PlanarConfiguration::Separate;
2296 : // Sanity check that a strile can its on SIZE_MAX, to avoid further
2297 : // issues in ReadBlock()
2298 946 : if (static_cast<uint64_t>(nNativeDTSize) * (bSeparate ? 1 : l_nBands) >
2299 946 : std::numeric_limits<size_t>::max() /
2300 946 : (static_cast<uint64_t>(nBlockXSize) * nBlockYSize))
2301 : {
2302 0 : CPLDebug("LIBERTIFF", "Too large block");
2303 0 : return false;
2304 : }
2305 :
2306 : // Process GDAL_NODATA tag
2307 946 : bool bHasNoData = false;
2308 946 : double dfNoData = 0;
2309 946 : const auto *tagNoData = m_image->tag(LIBERTIFF_NS::TagCode::GDAL_NODATA);
2310 946 : if (tagNoData && tagNoData->type == LIBERTIFF_NS::TagType::ASCII &&
2311 27 : !(tagNoData->count > 4 && tagNoData->invalid_value_offset) &&
2312 27 : tagNoData->count < 256)
2313 : {
2314 27 : bool ok = true;
2315 54 : const std::string noData = m_image->readTagAsString(*tagNoData, ok);
2316 27 : if (ok && !noData.empty())
2317 : {
2318 26 : bHasNoData = true;
2319 26 : dfNoData = CPLAtof(noData.c_str());
2320 : }
2321 : }
2322 :
2323 : // Process ExtraSamples tag
2324 946 : int nRegularChannels = 0;
2325 946 : if (m_image->photometricInterpretation() ==
2326 : LIBERTIFF_NS::PhotometricInterpretation::MinIsBlack)
2327 : {
2328 281 : nRegularChannels = 1;
2329 : }
2330 665 : else if (m_image->photometricInterpretation() ==
2331 : LIBERTIFF_NS::PhotometricInterpretation::RGB)
2332 : {
2333 199 : nRegularChannels = 3;
2334 : }
2335 : const auto *psExtraSamplesTag =
2336 946 : m_image->tag(LIBERTIFF_NS::TagCode::ExtraSamples);
2337 946 : if (nRegularChannels > 0 && l_nBands > nRegularChannels &&
2338 30 : psExtraSamplesTag &&
2339 30 : psExtraSamplesTag->type == LIBERTIFF_NS::TagType::Short &&
2340 30 : psExtraSamplesTag->count ==
2341 30 : static_cast<unsigned>(l_nBands - nRegularChannels))
2342 : {
2343 29 : bool ok = true;
2344 : m_extraSamples =
2345 29 : m_image->readTagAsVector<uint16_t>(*psExtraSamplesTag, ok);
2346 : }
2347 :
2348 : // Preload TileOffsets and TileByteCounts if not too big
2349 946 : if (m_image->isTiled())
2350 : {
2351 : const auto *psTileOffsets =
2352 120 : m_image->tag(LIBERTIFF_NS::TagCode::TileOffsets);
2353 : const auto *psTileByteCounts =
2354 120 : m_image->tag(LIBERTIFF_NS::TagCode::TileByteCounts);
2355 120 : if (psTileOffsets &&
2356 120 : (psTileOffsets->type == LIBERTIFF_NS::TagType::Long ||
2357 6 : psTileOffsets->type == LIBERTIFF_NS::TagType::Long8) &&
2358 118 : !psTileOffsets->invalid_value_offset &&
2359 95 : psTileOffsets->count <= 4096 && psTileByteCounts &&
2360 95 : psTileByteCounts->type == LIBERTIFF_NS::TagType::Long &&
2361 15 : !psTileByteCounts->invalid_value_offset &&
2362 15 : psTileByteCounts->count <= 4096)
2363 : {
2364 15 : bool ok = true;
2365 15 : if (psTileOffsets->type == LIBERTIFF_NS::TagType::Long)
2366 : m_tileOffsets =
2367 15 : m_image->readTagAsVector<uint32_t>(*psTileOffsets, ok);
2368 : else
2369 : m_tileOffsets64 =
2370 0 : m_image->readTagAsVector<uint64_t>(*psTileOffsets, ok);
2371 : m_tileByteCounts =
2372 15 : m_image->readTagAsVector<uint32_t>(*psTileByteCounts, ok);
2373 15 : if (!ok)
2374 : {
2375 0 : m_tileOffsets.clear();
2376 0 : m_tileOffsets64.clear();
2377 0 : m_tileByteCounts.clear();
2378 : }
2379 : }
2380 : }
2381 :
2382 : // Create raster bands
2383 67907 : for (int i = 0; i < l_nBands; ++i)
2384 : {
2385 0 : auto poBand = std::make_unique<LIBERTIFFBand>(this, i + 1, eDT,
2386 66961 : nBlockXSize, nBlockYSize);
2387 66961 : poBand->m_bHasNoData = bHasNoData;
2388 66961 : poBand->m_dfNoData = dfNoData;
2389 66961 : if (m_image->photometricInterpretation() ==
2390 : LIBERTIFF_NS::PhotometricInterpretation::MinIsBlack)
2391 : {
2392 65837 : if (i == 0)
2393 281 : poBand->m_eColorInterp = GCI_GrayIndex;
2394 : }
2395 1124 : else if (m_image->photometricInterpretation() ==
2396 1694 : LIBERTIFF_NS::PhotometricInterpretation::RGB ||
2397 510 : (m_image->photometricInterpretation() ==
2398 60 : LIBERTIFF_NS::PhotometricInterpretation::YCbCr &&
2399 60 : m_image->samplesPerPixel() == 3))
2400 : {
2401 674 : if (i < 3)
2402 657 : poBand->m_eColorInterp =
2403 657 : static_cast<GDALColorInterp>(GCI_RedBand + i);
2404 : }
2405 66961 : if (i >= nRegularChannels && !m_extraSamples.empty())
2406 : {
2407 35 : if (m_extraSamples[i - nRegularChannels] ==
2408 : LIBERTIFF_NS::ExtraSamples::UnAssociatedAlpha)
2409 : {
2410 16 : poBand->m_eColorInterp = GCI_AlphaBand;
2411 16 : if (!m_poAlphaBand)
2412 16 : m_poAlphaBand = poBand.get();
2413 : }
2414 19 : else if (m_extraSamples[i - nRegularChannels] ==
2415 : LIBERTIFF_NS::ExtraSamples::AssociatedAlpha)
2416 : {
2417 2 : poBand->m_eColorInterp = GCI_AlphaBand;
2418 2 : poBand->GDALRasterBand::SetMetadataItem(
2419 : "ALPHA", "PREMULTIPLIED", "IMAGE_STRUCTURE");
2420 2 : if (!m_poAlphaBand)
2421 2 : m_poAlphaBand = poBand.get();
2422 : }
2423 : }
2424 :
2425 68105 : if (m_image->bitsPerSample() != 8 && m_image->bitsPerSample() != 16 &&
2426 68577 : m_image->bitsPerSample() != 32 && m_image->bitsPerSample() != 64 &&
2427 472 : m_image->bitsPerSample() != 128)
2428 : {
2429 470 : poBand->GDALRasterBand::SetMetadataItem(
2430 : "NBITS", CPLSPrintf("%u", m_image->bitsPerSample()),
2431 : "IMAGE_STRUCTURE");
2432 : }
2433 :
2434 66961 : if (l_nBands == 1 && eDT == GDT_UInt8)
2435 : {
2436 654 : poBand->ReadColorMap();
2437 : }
2438 :
2439 66961 : if (m_image->photometricInterpretation() ==
2440 : LIBERTIFF_NS::PhotometricInterpretation::MinIsWhite)
2441 : {
2442 420 : GDALDataset::SetMetadataItem("MINISWHITE", "YES",
2443 : "IMAGE_STRUCTURE");
2444 : }
2445 :
2446 66961 : if (m_image->bitsPerSample() == 1 && !poBand->m_poColorTable)
2447 : {
2448 462 : poBand->m_poColorTable = std::make_unique<GDALColorTable>();
2449 462 : const GDALColorEntry oEntryBlack = {0, 0, 0, 255};
2450 462 : const GDALColorEntry oEntryWhite = {255, 255, 255, 255};
2451 462 : if (m_image->photometricInterpretation() ==
2452 : LIBERTIFF_NS::PhotometricInterpretation::MinIsWhite)
2453 : {
2454 419 : poBand->m_poColorTable->SetColorEntry(0, &oEntryWhite);
2455 419 : poBand->m_poColorTable->SetColorEntry(1, &oEntryBlack);
2456 : }
2457 : else
2458 : {
2459 43 : poBand->m_poColorTable->SetColorEntry(0, &oEntryBlack);
2460 43 : poBand->m_poColorTable->SetColorEntry(1, &oEntryWhite);
2461 : }
2462 462 : poBand->m_eColorInterp = GCI_PaletteIndex;
2463 : }
2464 :
2465 66961 : SetBand(i + 1, std::move(poBand));
2466 : }
2467 :
2468 946 : nOpenFlags = GDAL_OF_RASTER | GDAL_OF_THREAD_SAFE;
2469 :
2470 946 : return true;
2471 : }
2472 :
2473 : /************************************************************************/
2474 : /* Open() */
2475 : /************************************************************************/
2476 :
2477 1230 : bool LIBERTIFFDataset::Open(GDALOpenInfo *poOpenInfo)
2478 : {
2479 1230 : SetDescription(poOpenInfo->pszFilename);
2480 :
2481 1230 : int iSelectedSubDS = -1;
2482 1230 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "GTIFF_DIR:"))
2483 : {
2484 13 : iSelectedSubDS = atoi(poOpenInfo->pszFilename + strlen("GTIFF_DIR:"));
2485 13 : if (iSelectedSubDS <= 0)
2486 : {
2487 9 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset syntax");
2488 9 : return false;
2489 : }
2490 : const char *pszNextColon =
2491 4 : strchr(poOpenInfo->pszFilename + strlen("GTIFF_DIR:"), ':');
2492 4 : if (!pszNextColon)
2493 : {
2494 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid subdataset syntax");
2495 0 : return false;
2496 : }
2497 4 : m_poFile.reset(VSIFOpenL(pszNextColon + 1, "rb"));
2498 4 : if (!m_poFile)
2499 : {
2500 1 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
2501 : pszNextColon + 1);
2502 1 : return false;
2503 : }
2504 : m_fileReader =
2505 3 : std::make_shared<const LIBERTIFFDatasetFileReader>(m_poFile.get());
2506 : }
2507 : else
2508 : {
2509 : m_fileReader =
2510 1217 : std::make_shared<const LIBERTIFFDatasetFileReader>(poOpenInfo->fpL);
2511 : }
2512 :
2513 2440 : auto mainImage = LIBERTIFF_NS::open(m_fileReader);
2514 1220 : if (!mainImage)
2515 : {
2516 40 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open TIFF image");
2517 40 : return false;
2518 : }
2519 :
2520 2360 : if (mainImage->subFileType() != LIBERTIFF_NS::SubFileTypeFlags::Page &&
2521 1180 : mainImage->subFileType() != 0)
2522 : {
2523 0 : CPLDebug("LIBERTIFF", "Invalid subFileType value for first image");
2524 0 : return false;
2525 : }
2526 :
2527 : // Check structural metadata (for COG)
2528 1180 : const int nOffsetOfStructuralMetadata =
2529 1177 : poOpenInfo->nHeaderBytes && ((poOpenInfo->pabyHeader[2] == 0x2B ||
2530 1161 : poOpenInfo->pabyHeader[3] == 0x2B))
2531 2357 : ? 16
2532 : : 8;
2533 1180 : if (poOpenInfo->nHeaderBytes >
2534 1180 : nOffsetOfStructuralMetadata +
2535 1177 : static_cast<int>(strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) &&
2536 1177 : memcmp(poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata,
2537 : "GDAL_STRUCTURAL_METADATA_SIZE=",
2538 : strlen("GDAL_STRUCTURAL_METADATA_SIZE=")) == 0)
2539 : {
2540 2 : const char *pszStructuralMD = reinterpret_cast<const char *>(
2541 2 : poOpenInfo->pabyHeader + nOffsetOfStructuralMetadata);
2542 2 : const bool bLayoutIFDSBeforeData =
2543 2 : strstr(pszStructuralMD, "LAYOUT=IFDS_BEFORE_DATA") != nullptr;
2544 2 : const bool bBlockOrderRowMajor =
2545 2 : strstr(pszStructuralMD, "BLOCK_ORDER=ROW_MAJOR") != nullptr;
2546 2 : const bool bLeaderSizeAsUInt4 =
2547 2 : strstr(pszStructuralMD, "BLOCK_LEADER=SIZE_AS_UINT4") != nullptr;
2548 2 : const bool bTrailerRepeatedLast4BytesRepeated =
2549 2 : strstr(pszStructuralMD, "BLOCK_TRAILER=LAST_4_BYTES_REPEATED") !=
2550 : nullptr;
2551 2 : const bool bKnownIncompatibleEdition =
2552 2 : strstr(pszStructuralMD, "KNOWN_INCOMPATIBLE_EDITION=YES") !=
2553 : nullptr;
2554 2 : if (bKnownIncompatibleEdition)
2555 : {
2556 0 : ReportError(CE_Warning, CPLE_AppDefined,
2557 : "This file used to have optimizations in its layout, "
2558 : "but those have been, at least partly, invalidated by "
2559 : "later changes");
2560 : }
2561 2 : else if (bLayoutIFDSBeforeData && bBlockOrderRowMajor &&
2562 2 : bLeaderSizeAsUInt4 && bTrailerRepeatedLast4BytesRepeated)
2563 : {
2564 2 : GDALDataset::SetMetadataItem("LAYOUT", "COG", "IMAGE_STRUCTURE");
2565 : }
2566 : }
2567 :
2568 1180 : if (!Open(std::move(mainImage)))
2569 258 : return false;
2570 :
2571 : // Iterate over overviews
2572 922 : LIBERTIFFDataset *poLastNonMaskDS = this;
2573 1844 : auto imageNext = m_image->next();
2574 28 : if (imageNext &&
2575 28 : (m_image->subFileType() == 0 ||
2576 969 : m_image->subFileType() == LIBERTIFF_NS::SubFileTypeFlags::Page) &&
2577 47 : (imageNext->subFileType() == 0 ||
2578 19 : imageNext->subFileType() == LIBERTIFF_NS::SubFileTypeFlags::Page))
2579 : {
2580 9 : int iSubDS = 1;
2581 9 : CPLStringList aosList;
2582 9 : auto curImage = std::move(m_image);
2583 10 : do
2584 : {
2585 19 : if (iSelectedSubDS > 0 && iSubDS == iSelectedSubDS)
2586 : {
2587 2 : m_image = std::move(curImage);
2588 2 : break;
2589 : }
2590 17 : if (iSelectedSubDS < 0)
2591 : {
2592 : aosList.AddNameValue(
2593 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
2594 14 : CPLSPrintf("GTIFF_DIR:%d:%s", iSubDS, GetDescription()));
2595 : aosList.AddNameValue(CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
2596 : CPLSPrintf("Page %d (%uP x %uL x %uB)",
2597 : iSubDS, curImage->width(),
2598 : curImage->height(),
2599 14 : curImage->samplesPerPixel()));
2600 : }
2601 17 : ++iSubDS;
2602 17 : if (iSubDS == 65536)
2603 : {
2604 0 : ReportError(CE_Warning, CPLE_AppDefined,
2605 : "Stopping IFD scanning at 65536th one");
2606 0 : break;
2607 : }
2608 17 : curImage = curImage->next();
2609 17 : } while (curImage);
2610 9 : if (iSelectedSubDS < 0)
2611 : {
2612 13 : for (int i = 0; i < nBands; ++i)
2613 7 : delete papoBands[i];
2614 6 : CPLFree(papoBands);
2615 6 : papoBands = nullptr;
2616 6 : nRasterXSize = 0;
2617 6 : nRasterYSize = 0;
2618 6 : GDALDataset::SetMetadata(aosList.List(), "SUBDATASETS");
2619 6 : return true;
2620 : }
2621 3 : else if (!m_image)
2622 : {
2623 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %dth image",
2624 : iSelectedSubDS);
2625 1 : return false;
2626 : }
2627 : }
2628 913 : else if (iSelectedSubDS < 0)
2629 : {
2630 913 : auto curImage = std::move(imageNext);
2631 913 : int iters = 0;
2632 938 : while (curImage)
2633 : {
2634 25 : auto nextImage = curImage->next();
2635 25 : if (curImage->subFileType() ==
2636 : LIBERTIFF_NS::SubFileTypeFlags::ReducedImage)
2637 : {
2638 : // Overview IFD
2639 24 : auto poOvrDS = std::make_unique<LIBERTIFFDataset>();
2640 24 : if (poOvrDS->Open(std::move(curImage)) &&
2641 12 : poOvrDS->GetRasterCount() == nBands &&
2642 12 : poOvrDS->GetRasterXSize() <= nRasterXSize &&
2643 48 : poOvrDS->GetRasterYSize() <= nRasterYSize &&
2644 12 : poOvrDS->GetRasterBand(1)->GetRasterDataType() ==
2645 12 : GetRasterBand(1)->GetRasterDataType())
2646 : {
2647 12 : m_apoOvrDSOwned.push_back(std::move(poOvrDS));
2648 12 : auto poOvrDSRaw = m_apoOvrDSOwned.back().get();
2649 12 : m_apoOvrDS.push_back(poOvrDSRaw);
2650 12 : poLastNonMaskDS = poOvrDSRaw;
2651 : }
2652 : }
2653 13 : else if ((curImage->subFileType() &
2654 13 : LIBERTIFF_NS::SubFileTypeFlags::Mask) != 0)
2655 : {
2656 : // Mask IFD
2657 13 : if (!poLastNonMaskDS->m_poMaskDS)
2658 : {
2659 26 : auto poMaskDS = std::make_unique<LIBERTIFFDataset>();
2660 26 : if (poMaskDS->Open(std::move(curImage)) &&
2661 12 : poMaskDS->GetRasterCount() == 1 &&
2662 11 : poMaskDS->GetRasterXSize() ==
2663 11 : poLastNonMaskDS->nRasterXSize &&
2664 9 : poMaskDS->GetRasterYSize() ==
2665 47 : poLastNonMaskDS->nRasterYSize &&
2666 9 : poMaskDS->GetRasterBand(1)->GetRasterDataType() ==
2667 : GDT_UInt8)
2668 : {
2669 9 : poMaskDS->m_bExpand1To255 = true;
2670 9 : poLastNonMaskDS->m_poMaskDS = std::move(poMaskDS);
2671 9 : if (poLastNonMaskDS != this && m_poMaskDS)
2672 : {
2673 : // Also register the mask as the overview of the main
2674 : // mask
2675 0 : m_poMaskDS->m_apoOvrDS.push_back(
2676 0 : poLastNonMaskDS->m_poMaskDS.get());
2677 : }
2678 : }
2679 : }
2680 : }
2681 : else
2682 : {
2683 0 : CPLDebug("LIBERTIFF",
2684 : "Unhandled subFileType value for auxiliary image");
2685 0 : return false;
2686 : }
2687 25 : curImage = std::move(nextImage);
2688 :
2689 25 : ++iters;
2690 25 : if (iters == 64)
2691 : {
2692 0 : ReportError(CE_Warning, CPLE_AppDefined,
2693 : "Stopping IFD scanning at 64th one");
2694 0 : break;
2695 : }
2696 : }
2697 : }
2698 :
2699 : static const struct
2700 : {
2701 : LIBERTIFF_NS::TagCodeType code;
2702 : const char *mditem;
2703 : } strTags[] = {
2704 : {LIBERTIFF_NS::TagCode::DocumentName, "TIFFTAG_DOCUMENTNAME"},
2705 : {LIBERTIFF_NS::TagCode::ImageDescription, "TIFFTAG_IMAGEDESCRIPTION"},
2706 : {LIBERTIFF_NS::TagCode::Software, "TIFFTAG_SOFTWARE"},
2707 : {LIBERTIFF_NS::TagCode::DateTime, "TIFFTAG_DATETIME"},
2708 : {LIBERTIFF_NS::TagCode::Copyright, "TIFFTAG_COPYRIGHT"},
2709 : };
2710 :
2711 5490 : for (const auto &strTag : strTags)
2712 : {
2713 4575 : const auto *tag = m_image->tag(strTag.code);
2714 4575 : constexpr size_t ARBITRARY_MAX_SIZE = 65536;
2715 4575 : if (tag && tag->type == LIBERTIFF_NS::TagType::ASCII &&
2716 11 : !(tag->count > 4 && tag->invalid_value_offset) &&
2717 11 : tag->count < ARBITRARY_MAX_SIZE)
2718 : {
2719 11 : bool ok = true;
2720 22 : const std::string str = m_image->readTagAsString(*tag, ok);
2721 11 : if (ok)
2722 : {
2723 11 : GDALDataset::SetMetadataItem(strTag.mditem, str.c_str());
2724 : }
2725 : }
2726 : }
2727 :
2728 915 : ReadSRS();
2729 915 : ReadGeoTransform();
2730 915 : ReadRPCTag();
2731 :
2732 : const auto *psGDALMetadataTag =
2733 915 : m_image->tag(LIBERTIFF_NS::TagCode::GDAL_METADATA);
2734 915 : constexpr size_t ARBITRARY_MAX_SIZE_GDAL_METADATA = 10 * 1024 * 1024;
2735 915 : if (psGDALMetadataTag &&
2736 80 : psGDALMetadataTag->type == LIBERTIFF_NS::TagType::ASCII &&
2737 80 : !(psGDALMetadataTag->count > 4 &&
2738 80 : psGDALMetadataTag->invalid_value_offset) &&
2739 80 : psGDALMetadataTag->count < ARBITRARY_MAX_SIZE_GDAL_METADATA)
2740 : {
2741 80 : bool ok = true;
2742 : const std::string str =
2743 160 : m_image->readTagAsString(*psGDALMetadataTag, ok);
2744 80 : if (ok)
2745 : {
2746 160 : auto oRoot = CPLXMLTreeCloser(CPLParseXMLString(str.c_str()));
2747 80 : if (oRoot.get())
2748 : {
2749 : const CPLXMLNode *psItem =
2750 80 : oRoot.get() ? CPLGetXMLNode(oRoot.get(), "=GDALMetadata")
2751 80 : : nullptr;
2752 80 : if (psItem)
2753 80 : psItem = psItem->psChild;
2754 295 : for (; psItem != nullptr; psItem = psItem->psNext)
2755 : {
2756 215 : if (psItem->eType != CXT_Element ||
2757 215 : !EQUAL(psItem->pszValue, "Item"))
2758 0 : continue;
2759 :
2760 : const char *pszKey =
2761 215 : CPLGetXMLValue(psItem, "name", nullptr);
2762 : const char *pszValue =
2763 215 : CPLGetXMLValue(psItem, nullptr, nullptr);
2764 215 : int nBand = atoi(CPLGetXMLValue(psItem, "sample", "-1"));
2765 215 : if (nBand < -1 || nBand > 65535)
2766 0 : continue;
2767 215 : nBand++;
2768 215 : const char *pszRole = CPLGetXMLValue(psItem, "role", "");
2769 : const char *pszDomain =
2770 215 : CPLGetXMLValue(psItem, "domain", "");
2771 :
2772 215 : if (pszKey == nullptr || pszValue == nullptr)
2773 6 : continue;
2774 209 : if (EQUAL(pszDomain, "IMAGE_STRUCTURE"))
2775 : {
2776 81 : if (m_image->compression() ==
2777 90 : LIBERTIFF_NS::Compression::WEBP &&
2778 9 : EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
2779 : {
2780 : // go on
2781 : }
2782 72 : else if (m_image->compression() ==
2783 72 : LIBERTIFF_NS::Compression::WEBP &&
2784 0 : EQUAL(pszKey, "WEBP_LEVEL"))
2785 : {
2786 0 : const int nLevel = atoi(pszValue);
2787 0 : if (nLevel >= 1 && nLevel <= 100)
2788 : {
2789 0 : GDALDataset::SetMetadataItem(
2790 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2791 : "IMAGE_STRUCTURE");
2792 : }
2793 : }
2794 72 : else if (m_image->compression() ==
2795 114 : LIBERTIFF_NS::Compression::LERC &&
2796 42 : EQUAL(pszKey, "MAX_Z_ERROR"))
2797 : {
2798 : // go on
2799 : }
2800 72 : else if (m_image->compression() ==
2801 114 : LIBERTIFF_NS::Compression::LERC &&
2802 42 : EQUAL(pszKey, "MAX_Z_ERROR_OVERVIEW"))
2803 : {
2804 : // go on
2805 : }
2806 72 : else if (m_image->compression() ==
2807 100 : LIBERTIFF_NS::Compression::JXL &&
2808 28 : EQUAL(pszKey, "COMPRESSION_REVERSIBILITY"))
2809 : {
2810 : // go on
2811 : }
2812 58 : else if (m_image->compression() ==
2813 72 : LIBERTIFF_NS::Compression::JXL &&
2814 14 : EQUAL(pszKey, "JXL_DISTANCE"))
2815 : {
2816 0 : const double dfVal = CPLAtof(pszValue);
2817 0 : if (dfVal > 0 && dfVal <= 15)
2818 : {
2819 0 : GDALDataset::SetMetadataItem(
2820 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2821 : "IMAGE_STRUCTURE");
2822 : }
2823 : }
2824 58 : else if (m_image->compression() ==
2825 72 : LIBERTIFF_NS::Compression::JXL &&
2826 14 : EQUAL(pszKey, "JXL_ALPHA_DISTANCE"))
2827 : {
2828 0 : const double dfVal = CPLAtof(pszValue);
2829 0 : if (dfVal > 0 && dfVal <= 15)
2830 : {
2831 0 : GDALDataset::SetMetadataItem(
2832 : "COMPRESSION_REVERSIBILITY", "LOSSY",
2833 : "IMAGE_STRUCTURE");
2834 : }
2835 : }
2836 58 : else if (m_image->compression() ==
2837 72 : LIBERTIFF_NS::Compression::JXL &&
2838 14 : EQUAL(pszKey, "JXL_EFFORT"))
2839 : {
2840 : // go on
2841 : }
2842 : else
2843 : {
2844 44 : continue;
2845 : }
2846 : }
2847 :
2848 165 : bool bIsXML = false;
2849 :
2850 165 : if (STARTS_WITH_CI(pszDomain, "xml:"))
2851 0 : bIsXML = TRUE;
2852 :
2853 : // Note: this un-escaping should not normally be done, as the
2854 : // deserialization of the tree from XML also does it, so we end up
2855 : // width double XML escaping, but keep it for backward
2856 : // compatibility.
2857 : char *pszUnescapedValue =
2858 165 : CPLUnescapeString(pszValue, nullptr, CPLES_XML);
2859 165 : if (nBand == 0)
2860 : {
2861 121 : if (bIsXML)
2862 : {
2863 0 : char *apszMD[2] = {pszUnescapedValue, nullptr};
2864 0 : GDALDataset::SetMetadata(apszMD, pszDomain);
2865 : }
2866 : else
2867 : {
2868 121 : GDALDataset::SetMetadataItem(
2869 : pszKey, pszUnescapedValue, pszDomain);
2870 : }
2871 : }
2872 : else
2873 : {
2874 44 : auto poBand = cpl::down_cast<LIBERTIFFBand *>(
2875 : GetRasterBand(nBand));
2876 44 : if (poBand != nullptr)
2877 : {
2878 44 : if (EQUAL(pszRole, "scale"))
2879 : {
2880 0 : poBand->m_bHaveOffsetScale = true;
2881 0 : poBand->m_dfScale = CPLAtofM(pszUnescapedValue);
2882 : }
2883 44 : else if (EQUAL(pszRole, "offset"))
2884 : {
2885 0 : poBand->m_bHaveOffsetScale = true;
2886 0 : poBand->m_dfOffset =
2887 0 : CPLAtofM(pszUnescapedValue);
2888 : }
2889 44 : else if (EQUAL(pszRole, "unittype"))
2890 : {
2891 0 : poBand->m_osUnitType = pszUnescapedValue;
2892 : }
2893 44 : else if (EQUAL(pszRole, "description"))
2894 : {
2895 4 : poBand->m_osDescription = pszUnescapedValue;
2896 : }
2897 40 : else if (EQUAL(pszRole, "colorinterp"))
2898 : {
2899 9 : if (EQUAL(pszUnescapedValue, "undefined"))
2900 0 : poBand->m_eColorInterp = GCI_Undefined;
2901 : else
2902 : {
2903 9 : poBand->m_eColorInterp =
2904 9 : GDALGetColorInterpretationByName(
2905 : pszUnescapedValue);
2906 9 : if (poBand->m_eColorInterp == GCI_Undefined)
2907 : {
2908 0 : poBand->GDALRasterBand::SetMetadataItem(
2909 : "COLOR_INTERPRETATION",
2910 : pszUnescapedValue);
2911 : }
2912 : }
2913 : }
2914 : else
2915 : {
2916 31 : if (bIsXML)
2917 : {
2918 0 : char *apszMD[2] = {pszUnescapedValue,
2919 0 : nullptr};
2920 0 : poBand->GDALRasterBand::SetMetadata(
2921 : apszMD, pszDomain);
2922 : }
2923 : else
2924 : {
2925 31 : poBand->GDALRasterBand::SetMetadataItem(
2926 : pszKey, pszUnescapedValue, pszDomain);
2927 : }
2928 : }
2929 : }
2930 : }
2931 165 : CPLFree(pszUnescapedValue);
2932 : }
2933 : }
2934 : }
2935 : }
2936 :
2937 1818 : if ((m_image->compression() == LIBERTIFF_NS::Compression::WEBP ||
2938 1792 : m_image->compression() == LIBERTIFF_NS::Compression::JXL ||
2939 2719 : m_image->compression() == LIBERTIFF_NS::Compression::JXL_DNG_1_7) &&
2940 26 : GetMetadataItem("COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE") ==
2941 : nullptr)
2942 : {
2943 : const char *pszDriverName =
2944 3 : m_image->compression() == LIBERTIFF_NS::Compression::WEBP
2945 3 : ? "WEBP"
2946 3 : : "JPEGXL";
2947 3 : auto poTileDriver = GDALGetDriverByName(pszDriverName);
2948 3 : if (poTileDriver)
2949 : {
2950 3 : bool ok = true;
2951 3 : const uint64_t offset = m_image->strileOffset(0, ok);
2952 3 : const uint64_t bytecount = m_image->strileByteCount(0, ok);
2953 3 : if (ok && bytecount > 0)
2954 : {
2955 : const std::string osSubfile(
2956 : CPLSPrintf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
2957 : static_cast<GUIntBig>(offset),
2958 3 : static_cast<int>(std::min(
2959 3 : static_cast<uint64_t>(1024), bytecount)),
2960 6 : GetDescription()));
2961 3 : const char *const apszDrivers[] = {pszDriverName, nullptr};
2962 : auto poTileDataset =
2963 : std::unique_ptr<GDALDataset>(GDALDataset::Open(
2964 6 : osSubfile.c_str(), GDAL_OF_RASTER, apszDrivers));
2965 3 : if (poTileDataset)
2966 : {
2967 : const char *pszReversibility =
2968 3 : poTileDataset->GetMetadataItem(
2969 3 : "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
2970 3 : if (pszReversibility)
2971 3 : GDALDataset::SetMetadataItem(
2972 : "COMPRESSION_REVERSIBILITY", pszReversibility,
2973 : "IMAGE_STRUCTURE");
2974 : }
2975 : }
2976 : }
2977 : }
2978 :
2979 : // Init mask bands
2980 67840 : for (int i = 0; i < nBands; ++i)
2981 : {
2982 66925 : cpl::down_cast<LIBERTIFFBand *>(papoBands[i])->InitMaskBand();
2983 : }
2984 927 : for (auto &poOvrDS : m_apoOvrDS)
2985 : {
2986 26 : for (int i = 0; i < nBands; ++i)
2987 : {
2988 14 : cpl::down_cast<LIBERTIFFBand *>(poOvrDS->papoBands[i])
2989 14 : ->InitMaskBand();
2990 : }
2991 : }
2992 :
2993 915 : m_fileReader->setPReadAllowed();
2994 :
2995 915 : if (poOpenInfo->fpL)
2996 : {
2997 913 : m_poFile.reset(poOpenInfo->fpL);
2998 913 : poOpenInfo->fpL = nullptr;
2999 : }
3000 :
3001 : const int nThreads =
3002 915 : GDALGetNumThreads(poOpenInfo->papszOpenOptions, "NUM_THREADS",
3003 : GDAL_DEFAULT_MAX_THREAD_COUNT,
3004 : /* bDefaultAllCPUs = */ false);
3005 915 : if (nThreads > 1)
3006 : {
3007 99 : m_poThreadPool = GDALGetGlobalThreadPool(nThreads);
3008 : }
3009 :
3010 915 : return true;
3011 : }
3012 :
3013 : /************************************************************************/
3014 : /* ReadSRS() */
3015 : /************************************************************************/
3016 :
3017 : // Simplified GeoTIFF SRS reader, assuming the SRS is encoded as a EPSG code
3018 915 : void LIBERTIFFDataset::ReadSRS()
3019 : {
3020 : const auto psGeoKeysTag =
3021 915 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoKeyDirectory);
3022 915 : constexpr int VALUES_PER_GEOKEY = 4;
3023 915 : if (psGeoKeysTag && psGeoKeysTag->type == LIBERTIFF_NS::TagType::Short &&
3024 318 : !psGeoKeysTag->invalid_value_offset &&
3025 318 : psGeoKeysTag->count >= VALUES_PER_GEOKEY &&
3026 318 : (psGeoKeysTag->count % VALUES_PER_GEOKEY) == 0 &&
3027 : // Sanity check
3028 318 : psGeoKeysTag->count < 1000)
3029 : {
3030 318 : bool ok = true;
3031 : const auto values =
3032 318 : m_image->readTagAsVector<uint16_t>(*psGeoKeysTag, ok);
3033 318 : if (values.size() >= 4)
3034 : {
3035 315 : const uint16_t geokeysCount = values[3];
3036 315 : constexpr uint16_t GEOTIFF_KEY_DIRECTORY_VERSION_V1 = 1;
3037 315 : constexpr uint16_t GEOTIFF_KEY_VERSION_MAJOR_V1 = 1;
3038 315 : if (values[0] == GEOTIFF_KEY_DIRECTORY_VERSION_V1 &&
3039 : // GeoTIFF 1.x
3040 630 : values[1] == GEOTIFF_KEY_VERSION_MAJOR_V1 &&
3041 : // No equality for autotest/gcore/data/ycbcr_with_mask.tif
3042 315 : geokeysCount <= psGeoKeysTag->count / VALUES_PER_GEOKEY - 1)
3043 : {
3044 314 : constexpr uint16_t GeoTIFFTypeShort = 0;
3045 314 : constexpr uint16_t GeoTIFFTypeDouble =
3046 : LIBERTIFF_NS::TagCode::GeoTIFFDoubleParams;
3047 :
3048 314 : constexpr uint16_t GTModelTypeGeoKey = 1024;
3049 314 : constexpr uint16_t ModelTypeProjected = 1;
3050 314 : constexpr uint16_t ModelTypeGeographic = 2;
3051 :
3052 314 : constexpr uint16_t GTRasterTypeGeoKey = 1025;
3053 314 : constexpr uint16_t RasterPixelIsArea = 1;
3054 314 : constexpr uint16_t RasterPixelIsPoint = 2;
3055 :
3056 314 : constexpr uint16_t GeodeticCRSGeoKey = 2048;
3057 314 : constexpr uint16_t ProjectedCRSGeoKey = 3072;
3058 :
3059 314 : constexpr uint16_t VerticalGeoKey = 4096;
3060 :
3061 314 : constexpr uint16_t CoordinateEpochGeoKey = 5120;
3062 :
3063 314 : uint16_t nModelType = 0;
3064 314 : uint16_t nEPSGCode = 0;
3065 314 : uint16_t nEPSGCodeVertical = 0;
3066 314 : double dfCoordEpoch = 0;
3067 314 : bool bHasCoordEpoch = false;
3068 2484 : for (uint32_t i = 1; i <= geokeysCount; ++i)
3069 : {
3070 2170 : const auto geokey = values[VALUES_PER_GEOKEY * i];
3071 2170 : const auto geokeyType = values[VALUES_PER_GEOKEY * i + 1];
3072 2170 : const auto geokeyCount = values[VALUES_PER_GEOKEY * i + 2];
3073 2170 : const auto geokeyValue = values[VALUES_PER_GEOKEY * i + 3];
3074 2170 : if (geokey == GTModelTypeGeoKey)
3075 : {
3076 310 : nModelType = geokeyValue;
3077 : }
3078 1860 : else if (geokey == GeodeticCRSGeoKey &&
3079 180 : nModelType == ModelTypeGeographic &&
3080 180 : geokeyType == GeoTIFFTypeShort &&
3081 180 : geokeyCount == 1 && geokeyValue > 0)
3082 : {
3083 180 : nEPSGCode = geokeyValue;
3084 : }
3085 1680 : else if (geokey == ProjectedCRSGeoKey &&
3086 126 : nModelType == ModelTypeProjected &&
3087 126 : geokeyType == GeoTIFFTypeShort &&
3088 126 : geokeyCount == 1 && geokeyValue > 0)
3089 : {
3090 126 : nEPSGCode = geokeyValue;
3091 : }
3092 1554 : else if (geokey == GTRasterTypeGeoKey &&
3093 312 : geokeyType == GeoTIFFTypeShort && geokeyCount == 1)
3094 : {
3095 312 : if (geokeyValue == RasterPixelIsArea)
3096 : {
3097 306 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT,
3098 : GDALMD_AOP_AREA);
3099 : }
3100 6 : else if (geokeyValue == RasterPixelIsPoint)
3101 : {
3102 6 : GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT,
3103 : GDALMD_AOP_POINT);
3104 : }
3105 : }
3106 1253 : else if (values[2] == 1 /* GeoTIFF 1.1 */ &&
3107 7 : geokey == VerticalGeoKey &&
3108 1253 : geokeyType == GeoTIFFTypeShort && geokeyCount == 1)
3109 : {
3110 7 : nEPSGCodeVertical = geokeyValue;
3111 : }
3112 1235 : else if (geokey == CoordinateEpochGeoKey &&
3113 1 : geokeyType == GeoTIFFTypeDouble &&
3114 : geokeyCount == 1)
3115 : {
3116 1 : const auto psGeoDoubleParamsTag = m_image->tag(
3117 : LIBERTIFF_NS::TagCode::GeoTIFFDoubleParams);
3118 1 : if (psGeoDoubleParamsTag &&
3119 1 : psGeoDoubleParamsTag->type ==
3120 1 : LIBERTIFF_NS::TagType::Double &&
3121 1 : psGeoDoubleParamsTag->count > geokeyValue)
3122 : {
3123 1 : ok = true;
3124 : const auto doubleValues =
3125 : m_image->readTagAsVector<double>(
3126 2 : *psGeoDoubleParamsTag, ok);
3127 1 : if (ok && doubleValues.size() > geokeyValue)
3128 : {
3129 1 : bHasCoordEpoch = true;
3130 1 : dfCoordEpoch = doubleValues[geokeyValue];
3131 : }
3132 : }
3133 : }
3134 : }
3135 :
3136 314 : if (nEPSGCode > 0 && nEPSGCode != 32767 &&
3137 : nEPSGCodeVertical != 32767)
3138 : {
3139 295 : m_oSRS.importFromEPSG(nEPSGCode);
3140 :
3141 295 : if (nEPSGCodeVertical > 0)
3142 : {
3143 14 : OGRSpatialReference oSRSVertical;
3144 7 : oSRSVertical.importFromEPSG(nEPSGCodeVertical);
3145 10 : if (oSRSVertical.IsGeographic() &&
3146 3 : oSRSVertical.GetAxesCount() == 3)
3147 : {
3148 3 : m_oSRS = std::move(oSRSVertical);
3149 : }
3150 : else
3151 : {
3152 4 : m_oSRS.SetFromUserInput(CPLSPrintf(
3153 : "EPSG:%d+%d", nEPSGCode, nEPSGCodeVertical));
3154 : }
3155 : }
3156 :
3157 295 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3158 295 : if (bHasCoordEpoch)
3159 1 : m_oSRS.SetCoordinateEpoch(dfCoordEpoch);
3160 295 : return;
3161 : }
3162 :
3163 19 : const char *const apszAllowedDrivers[] = {"GTiff", nullptr};
3164 : auto poTmpDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3165 19 : GetDescription(), GDAL_OF_RASTER | GDAL_OF_INTERNAL,
3166 38 : apszAllowedDrivers, nullptr, nullptr));
3167 19 : if (poTmpDS)
3168 : {
3169 19 : const OGRSpatialReference *poSRS = poTmpDS->GetSpatialRef();
3170 19 : if (!poSRS)
3171 2 : poSRS = poTmpDS->GetGCPSpatialRef();
3172 19 : if (poSRS)
3173 19 : m_oSRS = *poSRS;
3174 : }
3175 : }
3176 : }
3177 : }
3178 : }
3179 :
3180 : /************************************************************************/
3181 : /* ReadGeoTransform() */
3182 : /************************************************************************/
3183 :
3184 915 : void LIBERTIFFDataset::ReadGeoTransform()
3185 : {
3186 : // Number of values per GCP in the GeoTIFFTiePoints tag
3187 915 : constexpr int VALUES_PER_GCP = 6;
3188 :
3189 915 : constexpr int GCP_PIXEL = 0;
3190 915 : constexpr int GCP_LINE = 1;
3191 : // constexpr int GCP_DEPTH = 2;
3192 915 : constexpr int GCP_X = 3;
3193 915 : constexpr int GCP_Y = 4;
3194 915 : constexpr int GCP_Z = 5;
3195 :
3196 : const auto *psTagTiePoints =
3197 915 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFTiePoints);
3198 : const auto *psTagPixelScale =
3199 915 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFPixelScale);
3200 : const auto *psTagGeoTransMatrix =
3201 915 : m_image->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoTransMatrix);
3202 915 : if (psTagTiePoints &&
3203 319 : psTagTiePoints->type == LIBERTIFF_NS::TagType::Double &&
3204 319 : !psTagTiePoints->invalid_value_offset &&
3205 319 : psTagTiePoints->count == VALUES_PER_GCP && psTagPixelScale &&
3206 315 : psTagPixelScale->type == LIBERTIFF_NS::TagType::Double &&
3207 315 : !psTagPixelScale->invalid_value_offset && psTagPixelScale->count == 3)
3208 : {
3209 315 : bool ok = true;
3210 : const auto tiepoints =
3211 315 : m_image->readTagAsVector<double>(*psTagTiePoints, ok);
3212 : const auto pixelScale =
3213 315 : m_image->readTagAsVector<double>(*psTagPixelScale, ok);
3214 315 : if (!ok)
3215 3 : return;
3216 :
3217 312 : m_geotransformValid = true;
3218 312 : m_gt.xscale = pixelScale[GCP_PIXEL];
3219 312 : m_gt.yscale = -pixelScale[GCP_LINE];
3220 312 : m_gt.xorig = tiepoints[GCP_X] - tiepoints[GCP_PIXEL] * m_gt.xscale;
3221 624 : m_gt.yorig = tiepoints[GCP_Y] - tiepoints[GCP_LINE] * m_gt.yscale;
3222 : }
3223 600 : else if (psTagGeoTransMatrix &&
3224 5 : psTagGeoTransMatrix->type == LIBERTIFF_NS::TagType::Double &&
3225 5 : !psTagGeoTransMatrix->invalid_value_offset &&
3226 5 : psTagGeoTransMatrix->count == 16)
3227 : {
3228 5 : bool ok = true;
3229 : const auto matrix =
3230 10 : m_image->readTagAsVector<double>(*psTagGeoTransMatrix, ok);
3231 5 : if (ok)
3232 : {
3233 5 : m_geotransformValid = true;
3234 5 : m_gt.xorig = matrix[3];
3235 5 : m_gt.xscale = matrix[0];
3236 5 : m_gt.xrot = matrix[1];
3237 5 : m_gt.yorig = matrix[7];
3238 5 : m_gt.yrot = matrix[4];
3239 5 : m_gt.yscale = matrix[5];
3240 5 : }
3241 : }
3242 595 : else if (psTagTiePoints &&
3243 4 : psTagTiePoints->type == LIBERTIFF_NS::TagType::Double &&
3244 4 : !psTagTiePoints->invalid_value_offset &&
3245 4 : psTagTiePoints->count > VALUES_PER_GCP &&
3246 4 : (psTagTiePoints->count % VALUES_PER_GCP) == 0 &&
3247 4 : psTagTiePoints->count <= 10000 * VALUES_PER_GCP)
3248 : {
3249 4 : bool ok = true;
3250 : const auto tiepoints =
3251 8 : m_image->readTagAsVector<double>(*psTagTiePoints, ok);
3252 4 : if (ok)
3253 : {
3254 4 : bool pixelIsPoint = false;
3255 4 : if (const char *pszAreaOrPoint =
3256 4 : GetMetadataItem(GDALMD_AREA_OR_POINT))
3257 : {
3258 4 : pixelIsPoint = EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
3259 : }
3260 4 : const int gcpCount =
3261 4 : static_cast<int>(psTagTiePoints->count / VALUES_PER_GCP);
3262 18 : for (int iGCP = 0; iGCP < gcpCount; ++iGCP)
3263 : {
3264 : m_aoGCPs.emplace_back(
3265 14 : CPLSPrintf("%d", iGCP + 1), "",
3266 14 : /* pixel = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_PIXEL],
3267 14 : /* line = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_LINE],
3268 14 : /* X = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_X],
3269 14 : /* Y = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_Y],
3270 28 : /* Z = */ tiepoints[iGCP * VALUES_PER_GCP + GCP_Z]);
3271 :
3272 14 : if (pixelIsPoint)
3273 : {
3274 8 : m_aoGCPs.back().Pixel() += 0.5;
3275 8 : m_aoGCPs.back().Line() += 0.5;
3276 : }
3277 : }
3278 : }
3279 : }
3280 :
3281 912 : if (m_geotransformValid)
3282 : {
3283 317 : if (const char *pszAreaOrPoint = GetMetadataItem(GDALMD_AREA_OR_POINT))
3284 : {
3285 308 : if (EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT))
3286 : {
3287 4 : m_gt.xorig -= (m_gt.xscale * 0.5 + m_gt.xrot * 0.5);
3288 4 : m_gt.yorig -= (m_gt.yrot * 0.5 + m_gt.yscale * 0.5);
3289 : }
3290 : }
3291 : }
3292 : }
3293 :
3294 : /************************************************************************/
3295 : /* ReadRPCTag() */
3296 : /* */
3297 : /* Format a TAG according to: */
3298 : /* */
3299 : /* http://geotiff.maptools.org/rpc_prop.html */
3300 : /************************************************************************/
3301 :
3302 915 : void LIBERTIFFDataset::ReadRPCTag()
3303 :
3304 : {
3305 : const auto *psTagRPCCoefficients =
3306 915 : m_image->tag(LIBERTIFF_NS::TagCode::RPCCoefficients);
3307 915 : if (psTagRPCCoefficients &&
3308 4 : psTagRPCCoefficients->type == LIBERTIFF_NS::TagType::Double &&
3309 4 : !psTagRPCCoefficients->invalid_value_offset &&
3310 4 : psTagRPCCoefficients->count == 92)
3311 : {
3312 4 : bool ok = true;
3313 : const auto adfRPC =
3314 8 : m_image->readTagAsVector<double>(*psTagRPCCoefficients, ok);
3315 4 : if (ok && adfRPC.size() == 92)
3316 : {
3317 4 : GDALDataset::SetMetadata(
3318 8 : gdal::tiff_common::TIFFRPCTagToRPCMetadata(adfRPC.data())
3319 4 : .List(),
3320 : "RPC");
3321 : }
3322 : }
3323 915 : }
3324 :
3325 : /************************************************************************/
3326 : /* OpenStatic() */
3327 : /************************************************************************/
3328 :
3329 1230 : /* static */ GDALDataset *LIBERTIFFDataset::OpenStatic(GDALOpenInfo *poOpenInfo)
3330 : {
3331 1230 : if (!Identify(poOpenInfo))
3332 0 : return nullptr;
3333 :
3334 2460 : auto poDS = std::make_unique<LIBERTIFFDataset>();
3335 1230 : if (!poDS->Open(poOpenInfo))
3336 309 : return nullptr;
3337 921 : return poDS.release();
3338 : }
3339 :
3340 : /************************************************************************/
3341 : /* GDALRegister_LIBERTIFF() */
3342 : /************************************************************************/
3343 :
3344 2059 : void GDALRegister_LIBERTIFF()
3345 :
3346 : {
3347 2059 : if (GDALGetDriverByName("LIBERTIFF") != nullptr)
3348 283 : return;
3349 :
3350 3552 : auto poDriver = std::make_unique<GDALDriver>();
3351 1776 : poDriver->SetDescription("LIBERTIFF");
3352 1776 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3353 1776 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
3354 1776 : "GeoTIFF (using LIBERTIFF library)");
3355 1776 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
3356 1776 : "drivers/raster/libertiff.html");
3357 1776 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/tiff");
3358 1776 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "tif tiff");
3359 1776 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3360 1776 : poDriver->SetMetadataItem(GDAL_DCAP_COORDINATE_EPOCH, "YES");
3361 :
3362 1776 : poDriver->pfnIdentify = LIBERTIFFDataset::Identify;
3363 1776 : poDriver->pfnOpen = LIBERTIFFDataset::OpenStatic;
3364 :
3365 1776 : poDriver->SetMetadataItem(
3366 : GDAL_DMD_OPENOPTIONLIST,
3367 : "<OpenOptionList>"
3368 : " <Option name='NUM_THREADS' type='string' description='Number of "
3369 : "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3370 1776 : "</OpenOptionList>");
3371 :
3372 1776 : if (CPLGetDecompressor("lzma"))
3373 : {
3374 1776 : poDriver->SetMetadataItem("LZMA_SUPPORT", "YES", "LIBERTIFF");
3375 : }
3376 : #ifdef ZSTD_SUPPORT
3377 1776 : poDriver->SetMetadataItem("ZSTD_SUPPORT", "YES", "LIBERTIFF");
3378 : #endif
3379 : #if defined(LERC_SUPPORT)
3380 1776 : poDriver->SetMetadataItem("LERC_SUPPORT", "YES", "LIBERTIFF");
3381 : #if defined(LERC_VERSION_MAJOR)
3382 : poDriver->SetMetadataItem("LERC_VERSION_MAJOR",
3383 : XSTRINGIFY(LERC_VERSION_MAJOR), "LERC");
3384 : poDriver->SetMetadataItem("LERC_VERSION_MINOR",
3385 : XSTRINGIFY(LERC_VERSION_MINOR), "LERC");
3386 : poDriver->SetMetadataItem("LERC_VERSION_PATCH",
3387 : XSTRINGIFY(LERC_VERSION_PATCH), "LERC");
3388 : #endif
3389 : #endif
3390 :
3391 1776 : GetGDALDriverManager()->RegisterDriver(poDriver.release());
3392 : }
|