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