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