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

Generated by: LCOV version 1.14