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