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