LCOV - code coverage report
Current view: top level - frmts/libertiff - libertiffdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1484 1595 93.0 %
Date: 2025-07-05 22:54:03 Functions: 71 76 93.4 %

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

Generated by: LCOV version 1.14