LCOV - code coverage report
Current view: top level - frmts/libertiff - libertiffdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1501 1612 93.1 %
Date: 2025-10-24 23:03:13 Functions: 71 76 93.4 %

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

Generated by: LCOV version 1.14