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