LCOV - code coverage report
Current view: top level - frmts/libertiff - libertiffdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1530 1645 93.0 %
Date: 2026-03-25 02:32:38 Functions: 72 78 92.3 %

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

Generated by: LCOV version 1.14